Aros/Developer/USBDriversDev
Introduction
editFor coding a driver you first need to understand Exec signals, messages, iorequests and lists.
For a driver (device normally) there are only a few "functions" DoIO, SendIO etc. The details are in the IO request fields. Take a look at the VUSBHCI driver. There's no hw stuff, only high level code for Poseidon driver and libusb.
VUSBHCI is also much more readable as everything is in plain sight in structures. You will recognize usb spec things in it and how things are passed around.
Poseidon is the main USB code, it doesn't know by itself anything about the hardware. Driver code is attached to it. Poseidon takes care of all the preferences, classes and drivers. Drivers only try to satisfy higher level requests from Poseidon or it's classes more specifically.
Every driver has to have a roothub as per usb spec. Roothub represents your hw. Roothub has ports, these are the physical ports that the host controller has (doesn't have to be... You could also implement a virtual port and have virtual devices attach)
hub.class calls your roothub and your driver has to respond to roothub requests. It's all in the usb specs. Poseidon doesn't yet know anything about usb 3.x requests. Driver also responds to requests to the physical device and conveys messages between the host and device.
EHCI host controller has to have it's companion controller on corresponding PCI slot otherwise it doesn't know which port is it's companion port and can't release it's port (it needs to know beforehand if that port has a companion port...). On poseidon there's just one PCIUSB driver and all those hosts are on a single driver and some quirks are used for the port forwarding but they doesn't need to be on the same driver. The chipset is able to detect when a port is released and the companion host controller will automaticly take over the port.
The current AROS XHCI driver code can find the USB3 and USB2 ports from the host controller and create a port list for them as well corresponding roothubs. On some motherboards the XHCI might only have USB3 ports on it's controller and a real EHCI chip and the usb data lines are directed via a digital MUX.
The function BOOL PCIXHCI_FindPorts(struct PCIXHCIUnit *unit) from pcixhci_controller.c should be able to detect the usb3.0 and usb2.0 ports from the XHCI controller.
- suggested to use his sata driver's slab memory allocator. Memory allocations are bad in XHCI. That is one issue.
- coded the init routine such that it uses PCI INT (event handler 0 only) With PCI-X other event rings can also be used.
- removal of huge switch statement and adding all the usb3 commands (not all of them are needed at start) and coding the hubss.class to use them.
- the hardware ring buffers need to be implemented correctly.
The driver needs to present two units per controller if the XHCI has both usb3 and usb2 ports. That's because we need to give Poseidon the correct roothub (hub or hubss) and one driver unit can only present one roothub per unit. There's already code present in the VUSBHCI to sort of implement this but there's no code for the usb3 part. Higher level usb3 code can be coded with the VUSBHCI driver even before we have a native XHCI driver via the said hosted driver.
One benefit would be that we should get ISOC transfers as the XHCI takes care of lining them up and there's no need to make an scheduler for the different packets.
CPU ---------------- Device Side Controller ---------------- Peripherals Controller Bus Bridge Peripheral Bus PCI, etc USB, etc
Hosted
editThese are the steps needed to get hosted usb "working"
- build the hosted version of AROS (It now needs to be build with --enable_usb30_code)
- cd into the build directory
- issue make kernel-usb (if it doesn't build everything then just make the rest)
- issue make kernel-usb-vusbhci
- issue make kernel-fs-fat (if intending to use fat usb sticks)
- issue make kernel-partition (otherwise AROS will crash as it tries to use the partition.library, bug? it should know it hasn't opened it)
- issue make kernel-usb-classes-hubss
Some Linux distribution do not allow the user to access usb devices, so a rule may need to be added in /etc/udev/rules.d
# Allow user to access all usb devices SUBSYSTEMS=="usb", \ MODE:="0666"
And saved it as "49-all-usb-devices.rules"
- Start hosted AROS and go to prefs and open Trident.
- Go to "Controllers" and press "New" and select "vusbhci.device" as unit number 0. press "Online" and press "Save"
- Go again to "Controllers" and press "New" and select "vusbhci.device", as unit number 1. press "Online" and press "Save"
Unit 0 is now USB2 roothub and unit 1 is USB3 roothub. (hardcoded, the order isn't significant)
Trident needs to be opened everytime you boot into the hosted version of AROS. There's no usbrombootstrap for the hosted version so Trident is used to setup the USB stack.
VUSBHCI.device depends on libusb so it will need the includes for it on Linux side (libusb-dev or something...)
VUSBHCI may output a lot of debug messages at the moment. Those go of course to the console if started from it.
VUSBHCI just means (V)irtual (USB) (H)ost (C)ontroller (I)nterface
There's only one virtual port per virtual host controller (usb2/usb3 (usb3 not implemented yet)). AROS will see the usb device when Trident is started and the device plugged in after. AROS can't take a device already plugged in as the driver uses libusb's plug in event. Linux has already enumerated and reset the device so a bit of hackery is needed on AROS side. VUSHCI driver will not reset the device nor issue it a new address. Otherwise it would confuse Linux. AROS will then own the device and use it as it will.
To start coding for the USB3 (vusbhci.device now doesn't build without it) then AROS build needs to be configured with the "--enable-usb30-code" switch. This will alter the Poseidon USB stack quite a bit to be USB3 aware. There's a lot of work to do to make sure it's solid.
Classes
editHow to create new POSEIDON classes for new hardware
editclass sources are kept in rom/usb/classes in their own drawers (folders)
rom/usb/classes/rndis/rndis.h rom/usb/classes/mmakefile.src rom/usb/classes/rndis/LEGAL rom/usb/classes/rndis/common.h rom/usb/classes/rndis/debug.c rom/usb/classes/rndis/debug.h rom/usb/classes/rndis/dev.c rom/usb/classes/rndis/dev.h rom/usb/classes/rndis/if_urndis.c rom/usb/classes/rndis/if_urndisreg.h rom/usb/classes/rndis/mmakefile.src rom/usb/classes/rndis/rndis.class.c rom/usb/classes/rndis/rndis.class.h rom/usb/classes/rndis/rndis.conf
rndis.h
edit/* * $Id$ */ #ifndef RNDIS_H #define RNDIS_H #include <intuition/intuition.h> #include <intuition/intuitionbase.h> #include <libraries/mui.h> #include <libraries/gadtools.h> #include <devices/sana2.h> #include <devices/sana2specialstats.h> #include <exec/devices.h> #include <stdint.h> #if defined(__GNUC__) # pragma pack(2) #endif #define DDF_CONFIGURED (1<<2) /* station address is configured */ #define DDF_ONLINE (1<<3) /* device is online */ #define DDF_OFFLINE (1<<4) /* device was put offline */ #define DROPPED (1<<0) /* Did the packet get dropped? */ #define PACKETFILTER (1<<1) /* Use the packet filter? */ /* Ethernet address bytesize */ #define ETHER_ADDR_SIZE 6 #define ETHER_MIN_LEN 60 /* smallest amount that nic will accept */ #define ETHER_MAX_LEN 1536 /* largest legal amount for Ethernet */ /* Ethernet packet data sizes (maximum) */ #define ETHERPKT_SIZE 1500 #define RAWPKT_SIZE 1514 #define ID_ABOUT 0x55555555 #define ID_STORE_CONFIG 0xaaaaaaaa #define ID_DEF_CONFIG 0xaaaaaaab struct ClsDevCfg { ULONG cdc_ChunkID; ULONG cdc_Length; ULONG cdc_DefaultUnit; UBYTE cdc_MACAddress[ETHER_ADDR_SIZE]; }; #if defined(__GNUC__) # pragma pack() #endif /* Structure of an ethernet packet - internal */ struct EtherPacketHeader { UBYTE eph_Dest[ETHER_ADDR_SIZE]; /* 0 destination address */ UBYTE eph_Src[ETHER_ADDR_SIZE]; /* 6 originator address */ UWORD eph_Type; /* 12 packet type */ }; /* Buffer management node - private */ struct BufMan { struct Node bm_Node; APTR bm_DMACopyFromBuf32; APTR bm_CopyFromBuf; APTR bm_DMACopyToBuf32; APTR bm_CopyToBuf; APTR bm_PacketFilter; struct List bm_RXQueue; /* read requests */ }; /* Multicast address range record - private */ struct MulticastAddressRange { struct Node mar_Node; /* 0 list node */ ULONG mar_UseCount; /* 8 number of times used */ UBYTE mar_LowerAddr[ETHER_ADDR_SIZE]; /* 12 multicast address lower bound */ UBYTE mar_UpperAddr[ETHER_ADDR_SIZE]; /* 18 multicast address upper bound */ }; struct PacketTypeStats { struct Node pts_Node; ULONG pts_PacketType; struct Sana2PacketTypeStats pts_Stats; }; struct NepEthDevBase { struct Library np_Library; /* standard */ UWORD np_Flags; /* various flags */ BPTR np_SegList; /* device seglist */ struct NepEthBase *np_ClsBase; /* pointer to class base */ struct Library *np_UtilityBase; /* cached utilitybase */ }; struct NepClassEth { struct Unit ncp_Unit; /* Unit structure */ ULONG ncp_UnitNo; /* Unit number */ ULONG ncp_OpenFlags; /* Flags used to open the device */ struct NepEthBase *ncp_ClsBase; /* Up linkage */ struct NepEthDevBase *ncp_DevBase; /* Device base */ struct Library *ncp_Base; /* Poseidon base */ struct PsdDevice *ncp_Device; /* Up linkage */ struct PsdConfig *ncp_Config; /* Up linkage */ struct PsdInterface *ncp_Interface; /* Up linkage */ struct Task *ncp_ReadySigTask; /* Task to send ready signal to */ LONG ncp_ReadySignal; /* Signal to send when ready */ struct Task *ncp_Task; /* Subtask */ struct MsgPort *ncp_TaskMsgPort; /* Message Port of Subtask */ struct PsdPipe *ncp_EP0Pipe; /* Endpoint 0 pipe */ struct PsdEndpoint *ncp_EPOut; /* Endpoint 1 */ struct PsdPipe *ncp_EPOutPipe[2]; /* Endpoint 1 pipes */ struct PsdEndpoint *ncp_EPIn; /* Endpoint 2 */ struct PsdPipe *ncp_EPInPipe; /* Endpoint 2 pipe */ struct MsgPort *ncp_DevMsgPort; /* Message Port for IOParReq */ UWORD ncp_UnitProdID; /* ProductID of unit */ UWORD ncp_UnitVendorID; /* VendorID of unit */ //BOOL ncp_DenyRequests; /* Do not accept further IO requests */ struct List ncp_BufManList; /* Buffer Managers */ struct List ncp_EventList; /* List for DoEvent */ struct List ncp_TrackList; /* List of trackables */ struct List ncp_Multicasts; /* List of multicast addresses */ UBYTE ncp_MacAddress[ETHER_ADDR_SIZE]; /* Current Mac Address */ UBYTE ncp_ROMAddress[ETHER_ADDR_SIZE]; /* ROM Mac Address */ UBYTE ncp_MulticastArray[8]; /* array for the multicast hashes */ ULONG ncp_StateFlags; /* State of the unit */ ULONG ncp_Retries; /* tx collision count */ ULONG ncp_BadMulticasts; /* bad multicast count */ UBYTE *ncp_ReadBuffer[2]; /* Packet Double Buffered Read Buffer */ UBYTE *ncp_WriteBuffer[2]; /* Packet Write Buffer */ UWORD ncp_ReadBufNum; /* Next Read Buffer to use */ UWORD ncp_WriteBufNum; /* Next Write Buffer to use */ struct Sana2DeviceStats ncp_DeviceStats; /* SANA Stats */ struct Sana2PacketTypeStats *ncp_TypeStats2048; /* IP protocol stats ptr, or NULL */ struct Sana2PacketTypeStats *ncp_TypeStats2054; /* ARP protocol stats ptr, or NULL */ UBYTE *ncp_ReadPending; /* read IORequest pending */ struct IOSana2Req *ncp_WritePending[2]; /* write IORequest pending */ struct List ncp_OrphanQueue; /* List of orphan read requests */ struct List ncp_WriteQueue; /* List of write requests */ UBYTE ncp_DevIDString[128]; /* Device ID String */ BOOL ncp_UsingDefaultCfg; struct ClsDevCfg *ncp_CDC; uint32_t sc_filter; /* rndis stuff */ uint32_t sc_lim_pktsz; struct Library *ncp_MUIBase; /* MUI master base */ struct Library *ncp_PsdBase; /* Poseidon base */ struct Library *ncp_IntBase; /* Intuition base */ }; struct NepEthBase { struct Library nh_Library; /* standard */ UWORD nh_Flags; /* various flags */ struct Library *nh_UtilityBase; /* utility base */ struct NepEthDevBase *nh_DevBase; /* base of device created */ struct List nh_Units; /* List of units available */ struct NepClassEth nh_DummyNCP; /* Dummy ncp for default config */ }; #endif /* RNDIS_H */
common.h
edit#include LC_LIBDEFS_FILE #include <aros/libcall.h> #include <aros/asmcall.h> #include <aros/symbolsets.h> #include <exec/types.h> #include <exec/lists.h> #include <exec/alerts.h> #include <exec/memory.h> #include <exec/libraries.h> #include <exec/interrupts.h> #include <exec/semaphores.h> #include <exec/execbase.h> #include <exec/devices.h> #include <exec/io.h> #include <exec/ports.h> #include <exec/errors.h> #include <exec/resident.h> #include <exec/initializers.h> #include <devices/timer.h> #include <devices/input.h> #include <utility/utility.h> #include <dos/dos.h> #include <intuition/intuition.h> #include <devices/usb.h> #include <devices/usbhardware.h> #include <libraries/usbclass.h> #include <string.h> #include <stddef.h> #include <stdio.h> #include <proto/dos.h> #include <proto/commodities.h> #include <proto/intuition.h> #include <proto/poseidon.h> #include <proto/utility.h> #include <proto/keymap.h> #include <proto/layers.h> #include <proto/input.h> #include <proto/expansion.h> #include <proto/exec.h> #include <proto/muimaster.h> #define NewList NEWLIST #include <stdarg.h> #define min(x,y) (((x) < (y)) ? (x) : (y)) #define max(x,y) (((x) > (y)) ? (x) : (y))
rndis.class.c
edit/* * $Id$ */ #include "debug.h" #include "rndis.class.h" /* /// "Lib Stuff" */ static const STRPTR libname = MOD_NAME_STRING; static const APTR DevFuncTable[] = { &AROS_SLIB_ENTRY(devOpen, dev), &AROS_SLIB_ENTRY(devClose, dev), &AROS_SLIB_ENTRY(devExpunge, dev), &AROS_SLIB_ENTRY(devReserved, dev), &AROS_SLIB_ENTRY(devBeginIO, dev), &AROS_SLIB_ENTRY(devAbortIO, dev), (APTR) -1, }; static int libInit(LIBBASETYPEPTR nh) { struct NepClassEth *ncp; struct NepEthBase *ret = NULL; KPRINTF(10, ("libInit nh: 0x%08lx SysBase: 0x%08lx\n", nh, SysBase)); nh->nh_UtilityBase = OpenLibrary("utility.library", 39); #define UtilityBase nh->nh_UtilityBase if(UtilityBase) { NewList(&nh->nh_Units); if((nh->nh_DevBase = (struct NepEthDevBase *) MakeLibrary((APTR) DevFuncTable, NULL, (APTR) devInit, sizeof(struct NepEthDevBase), NULL))) { ncp = &nh->nh_DummyNCP; ncp->ncp_ClsBase = nh; ncp->ncp_Interface = NULL; ncp->ncp_CDC = AllocVec(sizeof(struct ClsDevCfg), MEMF_PUBLIC|MEMF_CLEAR); if(ncp->ncp_CDC) { nh->nh_DevBase->np_ClsBase = nh; Forbid(); AddDevice((struct Device *) nh->nh_DevBase); nh->nh_DevBase->np_Library.lib_OpenCnt++; Permit(); ret = nh; } } else { KPRINTF(20, ("failed to create usbrndis.device\n")); } if(!ret) { CloseLibrary(UtilityBase); } } else { KPRINTF(20, ("libInit: OpenLibrary(\"utility.library\", 39) failed!\n")); } KPRINTF(10, ("libInit: Ok\n")); return(ret ? TRUE : FALSE); } static int libExpunge(LIBBASETYPEPTR nh) { struct NepClassEth *ncp; KPRINTF(10, ("libExpunge nh: 0x%08lx\n", nh)); if(nh->nh_DevBase->np_Library.lib_OpenCnt == 1) { KPRINTF(1, ("libExpunge: closelibrary utilitybase 0x%08lx\n", UtilityBase)); CloseLibrary((struct Library *) UtilityBase); ncp = (struct NepClassEth *) nh->nh_Units.lh_Head; while(ncp->ncp_Unit.unit_MsgPort.mp_Node.ln_Succ) { Remove((struct Node *) ncp); FreeVec(ncp->ncp_CDC); FreeVec(ncp); ncp = (struct NepClassEth *) nh->nh_Units.lh_Head; } nh->nh_DevBase->np_Library.lib_OpenCnt--; RemDevice((struct Device *) nh->nh_DevBase); KPRINTF(5, ("libExpunge: Unloading done! rndis.class expunged!\n\n")); } else { KPRINTF(5, ("libExpunge: Could not expunge, LIBF_DELEXP set!\n")); return(FALSE); } return(TRUE); } ADD2INITLIB(libInit, 0) ADD2EXPUNGELIB(libExpunge, 0) /* \\\ */ /* * *********************************************************************** * * Library functions * * *********************************************************************** */ struct AutoBindData { UWORD abd_VendID; UWORD abd_ProdID; }; struct AutoBindData ClassBinds[] = { //{ 0x12d1, 0x1039 }, // Huawei u8800 { 0, 0 } }; /* /// "usbAttemptDeviceBinding()" */ struct NepClassEth * usbAttemptDeviceBinding(struct NepEthBase *nh, struct PsdDevice *pd) { struct Library *ps; struct AutoBindData *abd = ClassBinds; struct PsdInterface *pif; IPTR prodid; IPTR vendid; IPTR ifclass; IPTR subclass; IPTR proto; KPRINTF(1, ("nepEthAttemptDeviceBinding(%08lx)\n", pd)); if((ps = OpenLibrary("poseidon.library", 4))) { psdGetAttrs(PGA_DEVICE, pd, DA_VendorID, &vendid, DA_ProductID, &prodid, TAG_END); if( (pif = psdFindInterface(pd, NULL,TAG_END)) ){ psdGetAttrs(PGA_INTERFACE, pif, IFA_Class, &ifclass, IFA_SubClass, &subclass, IFA_Protocol, &proto, TAG_DONE); if (ifclass == 224 && // WIRELESS subclass == 1 && // RF proto == 3) // RNDIS { CloseLibrary(ps); return(usbForceDeviceBinding(nh, pd)); } } while(abd->abd_VendID) { if((vendid == abd->abd_VendID) && (prodid == abd->abd_ProdID)) { CloseLibrary(ps); return(usbForceDeviceBinding(nh, pd)); } abd++; } } return(NULL); } /* \\\ */ /* /// "usbForceDeviceBinding()" */ struct NepClassEth * usbForceDeviceBinding(struct NepEthBase *nh, struct PsdDevice *pd) { struct Library *ps; struct NepClassEth *ncp; struct NepClassEth *tmpncp; struct ClsDevCfg *cdc; STRPTR devname; STRPTR devidstr; IPTR prodid; IPTR vendid; ULONG unitno; BOOL unitfound; UBYTE buf[64]; KPRINTF(1, ("nepEthForceDeviceBinding(%08lx)\n", pd)); if((ps = OpenLibrary("poseidon.library", 4))) { psdGetAttrs(PGA_DEVICE, pd, DA_ProductID, &prodid, DA_VendorID, &vendid, DA_ProductName, &devname, DA_IDString, &devidstr, TAG_END); Forbid(); unitfound = FALSE; unitno = (ULONG) -1; ncp = (struct NepClassEth *) nh->nh_Units.lh_Head; while(ncp->ncp_Unit.unit_MsgPort.mp_Node.ln_Succ) { if(!strcmp(ncp->ncp_DevIDString, devidstr)) { unitno = ncp->ncp_UnitNo; unitfound = TRUE; break; } ncp = (struct NepClassEth *) ncp->ncp_Unit.unit_MsgPort.mp_Node.ln_Succ; } if(!unitfound) { /* as units are freed in the expunge-vector, the memory is outside the scope of the poseidon library */ if(!(ncp = AllocVec(sizeof(struct NepClassEth), MEMF_PUBLIC|MEMF_CLEAR))) { Permit(); CloseLibrary(ps); return(NULL); } ncp->ncp_CDC = cdc = AllocVec(sizeof(struct ClsDevCfg), MEMF_PUBLIC|MEMF_CLEAR); if(!cdc) { Permit(); FreeVec(ncp); CloseLibrary(ps); return(NULL); } /* IORequests may be queued even if the task is gone. */ ncp->ncp_UnitNo = (ULONG) -1; NewList(&ncp->ncp_Unit.unit_MsgPort.mp_MsgList); NewList(&ncp->ncp_OrphanQueue); NewList(&ncp->ncp_WriteQueue); NewList(&ncp->ncp_BufManList); NewList(&ncp->ncp_EventList); NewList(&ncp->ncp_TrackList); NewList(&ncp->ncp_Multicasts); strncpy(ncp->ncp_DevIDString, devidstr, 127); AddTail(&nh->nh_Units, &ncp->ncp_Unit.unit_MsgPort.mp_Node); } ncp->ncp_ClsBase = nh; ncp->ncp_Device = pd; ncp->ncp_UnitProdID = prodid; ncp->ncp_UnitVendorID = vendid; //nLoadBindingConfig(ncp); /* Find next free unit number */ if(unitno == (ULONG) -1) { unitno = ncp->ncp_CDC->cdc_DefaultUnit; tmpncp = (struct NepClassEth *) nh->nh_Units.lh_Head; while(tmpncp->ncp_Unit.unit_MsgPort.mp_Node.ln_Succ) { if(tmpncp->ncp_UnitNo == unitno) { unitno++; tmpncp = (struct NepClassEth *) nh->nh_Units.lh_Head; } else { tmpncp = (struct NepClassEth *) tmpncp->ncp_Unit.unit_MsgPort.mp_Node.ln_Succ; } } } ncp->ncp_UnitNo = unitno; Permit(); psdSafeRawDoFmt(buf, 64, "rndis.class<%08lx>", ncp); ncp->ncp_ReadySignal = SIGB_SINGLE; ncp->ncp_ReadySigTask = FindTask(NULL); SetSignal(0, SIGF_SINGLE); if(psdSpawnSubTask(buf, nEthTask, ncp)) { Wait(1L<<ncp->ncp_ReadySignal); if(ncp->ncp_Task) { ncp->ncp_ReadySigTask = NULL; //FreeSignal(ncp->ncp_ReadySignal); psdAddErrorMsg(RETURN_OK, (STRPTR) libname, "Mr. Data linked '%s' to %s unit %ld!", devname, nh->nh_DevBase->np_Library.lib_Node.ln_Name, ncp->ncp_UnitNo); CloseLibrary(ps); return(ncp); } } ncp->ncp_ReadySigTask = NULL; //FreeSignal(ncp->ncp_ReadySignal); /* Get rid of unit structure */ /*Forbid(); Remove((struct Node *) ncp); FreeVec(ncp->ncp_CDC); FreeVec(ncp); Permit();*/ CloseLibrary(ps); } return(NULL); } /* \\\ */ /* /// "usbReleaseDeviceBinding()" */ void usbReleaseDeviceBinding(struct NepEthBase *nh, struct NepClassEth *ncp) { struct Library *ps; STRPTR devname; KPRINTF(1, ("nepEthReleaseDeviceBinding(%08lx)\n", ncp)); if((ps = OpenLibrary("poseidon.library", 4))) { Forbid(); ncp->ncp_ReadySignal = SIGB_SINGLE; ncp->ncp_ReadySigTask = FindTask(NULL); if(ncp->ncp_Task) { Signal(ncp->ncp_Task, SIGBREAKF_CTRL_C); } Permit(); while(ncp->ncp_Task) { Wait(1L<<ncp->ncp_ReadySignal); } //FreeSignal(ncp->ncp_ReadySignal); psdGetAttrs(PGA_DEVICE, ncp->ncp_Device, DA_ProductName, &devname, TAG_END); psdAddErrorMsg(RETURN_OK, (STRPTR) libname, "Shrinkwrapped and wasted '%s'.", devname); /*psdFreeVec(ncp);*/ CloseLibrary(ps); } } /* \\\ */ /* /// "usbGetAttrsA()" */ AROS_LH3(LONG, usbGetAttrsA, AROS_LHA(ULONG, type, D0), AROS_LHA(APTR, usbstruct, A0), AROS_LHA(struct TagItem *, tags, A1), LIBBASETYPEPTR, nh, 5, nep) { AROS_LIBFUNC_INIT struct TagItem *ti; LONG count = 0; KPRINTF(1, ("nepEthGetAttrsA(%ld, %08lx, %08lx)\n", type, usbstruct, tags)); switch(type) { case UGA_CLASS: if((ti = FindTagItem(UCCA_Priority, tags))) { *((SIPTR *) ti->ti_Data) = -100; count++; } if((ti = FindTagItem(UCCA_Description, tags))) { *((STRPTR *) ti->ti_Data) = "Ethernet SANA wrapper for RNDIS devices via usbrndis.device"; count++; } if((ti = FindTagItem(UCCA_HasClassCfgGUI, tags))) { *((IPTR *) ti->ti_Data) = TRUE; count++; } if((ti = FindTagItem(UCCA_HasBindingCfgGUI, tags))) { *((IPTR *) ti->ti_Data) = TRUE; count++; } if((ti = FindTagItem(UCCA_AfterDOSRestart, tags))) { *((IPTR *) ti->ti_Data) = FALSE; count++; } if((ti = FindTagItem(UCCA_UsingDefaultCfg, tags))) { *((IPTR *) ti->ti_Data) = nh->nh_DummyNCP.ncp_UsingDefaultCfg; count++; } break; case UGA_BINDING: if((ti = FindTagItem(UCBA_UsingDefaultCfg, tags))) { *((IPTR *) ti->ti_Data) = ((struct NepClassEth *) usbstruct)->ncp_UsingDefaultCfg; count++; } break; } return(count); AROS_LIBFUNC_EXIT } /* \\\ */ /* /// "usbSetAttrsA()" */ AROS_LH3(LONG, usbSetAttrsA, AROS_LHA(ULONG, type, D0), AROS_LHA(APTR, usbstruct, A0), AROS_LHA(struct TagItem *, tags, A1), LIBBASETYPEPTR, nh, 6, nep) { AROS_LIBFUNC_INIT return(0); AROS_LIBFUNC_EXIT } /* \\\ */ /* /// "usbDoMethodA()" */ AROS_LH2(IPTR, usbDoMethodA, AROS_LHA(ULONG, methodid, D0), AROS_LHA(IPTR *, methoddata, A1), LIBBASETYPEPTR, nh, 7, nep) { AROS_LIBFUNC_INIT KPRINTF(10, ("Do Method %ld\n", methodid)); switch(methodid) { case UCM_AttemptDeviceBinding: return((IPTR) usbAttemptDeviceBinding(nh, (struct PsdDevice *) methoddata[0])); case UCM_ForceDeviceBinding: return((IPTR) usbForceDeviceBinding(nh, (struct PsdDevice *) methoddata[0])); case UCM_ReleaseDeviceBinding: usbReleaseDeviceBinding(nh, (struct NepClassEth *) methoddata[0]); return(TRUE); default: break; } return(0); AROS_LIBFUNC_EXIT } /* \\\ */ /**************************************************************************/ #undef ps #define ps ncp->ncp_Base /* /// "nEthTask()" */ AROS_UFH0(void, nEthTask) { AROS_USERFUNC_INIT struct NepClassEth *ncp; struct PsdPipe *pp; ULONG sigmask; ULONG sigs; LONG ioerr; UBYTE *pktptr; ULONG pktlen; UWORD cnt; LONG lastioerr = 0; ULONG errcount = 0; struct IOSana2Req *ioreq; if((ncp = nAllocEth())) { urndis_attach(ncp); Forbid(); if(ncp->ncp_ReadySigTask) { Signal(ncp->ncp_ReadySigTask, 1L<<ncp->ncp_ReadySignal); } Permit(); { /* Record start time_of_day */ //GetSysTime(&ncp->ncp_DeviceStats.LastStart); /* Now online */ ncp->ncp_StateFlags |= DDF_ONLINE; ncp->ncp_StateFlags &= ~DDF_OFFLINE; /* Trigger any ONLINE events */ nDoEvent(ncp, S2EVENT_ONLINE); } /* Main task */ sigmask = (1L<<ncp->ncp_Unit.unit_MsgPort.mp_SigBit)|(1L<<ncp->ncp_TaskMsgPort->mp_SigBit)|SIGBREAKF_CTRL_C; do { // start transmitting read request if online... if((ncp->ncp_StateFlags & DDF_ONLINE) && (ncp->ncp_ReadPending == NULL)) { ncp->ncp_ReadPending = ncp->ncp_ReadBuffer[ncp->ncp_ReadBufNum]; psdSendPipe(ncp->ncp_EPInPipe, ncp->ncp_ReadPending, RNDIS_BUFSZ ); ncp->ncp_ReadBufNum ^= 1; } while((pp = (struct PsdPipe *) GetMsg(ncp->ncp_TaskMsgPort))) { KPRINTF(1, ("Pipe back %08lx\n", pp)); for(cnt = 0; cnt < 2; cnt++) { if(pp == ncp->ncp_EPOutPipe[cnt]) { if((ioreq = ncp->ncp_WritePending[cnt])) { ioerr = psdGetPipeError(pp); if(ioerr) { psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "Eth transmit failed: %s (%ld)", psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr); /* Trigger any tx or generic error events */ nDoEvent(ncp, S2EVENT_ERROR|S2EVENT_TX); /* Set error code and terminate the iorequest. NOTE: Can't use RC_* or deverror() this is not called from devBeginIO()! */ ioreq->ios2_DataLength = 0; ioreq->ios2_Req.io_Error = S2ERR_TX_FAILURE; ioreq->ios2_WireError = S2WERR_GENERIC_ERROR; psdDelayMS(50); } ReplyMsg((struct Message *) ioreq); ncp->ncp_WritePending[cnt] = NULL; } break; } } if(pp == ncp->ncp_EPInPipe) { if((pktptr = ncp->ncp_ReadPending)) { ioerr = psdGetPipeError(pp); pktlen = psdGetPipeActual(pp); KPRINTF(1, ("ReadBack with %ld bytes.\n", pktlen)); // interleave next packet reading ASAP. if(ncp->ncp_StateFlags & DDF_ONLINE) { ncp->ncp_ReadPending = ncp->ncp_ReadBuffer[ncp->ncp_ReadBufNum]; psdSendPipe(ncp->ncp_EPInPipe, ncp->ncp_ReadPending, RNDIS_BUFSZ ); ncp->ncp_ReadBufNum ^= 1; } else { ncp->ncp_ReadPending = NULL; } if(ioerr) { if(lastioerr != ioerr) { psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "Eth receive failed: %s (%ld)", psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr); errcount = 0; } else { errcount++; if(errcount > 20) { psdAddErrorMsg(RETURN_FAIL, (STRPTR) libname, "That's it, that device pissed me off long enough!"); Signal(ncp->ncp_Task, SIGBREAKF_CTRL_C); } } lastioerr = ioerr; psdDelayMS(50); break; } else { KPRINTF(1, ("Pkt %ld received\n", pktlen)); nReadPacket(ncp, pktptr, pktlen); } } } } Forbid(); while((!ncp->ncp_WritePending[ncp->ncp_WriteBufNum]) && ncp->ncp_WriteQueue.lh_Head->ln_Succ) { ioreq = (struct IOSana2Req *) RemHead(&ncp->ncp_WriteQueue); Permit(); nWritePacket(ncp, ioreq); Forbid(); } Permit(); sigs = Wait(sigmask); } while(!(sigs & SIGBREAKF_CTRL_C)); Forbid(); /* Now remove all requests still pending *anywhere* */ //ncp->ncp_DenyRequests = TRUE; /* Current transfers */ for(cnt = 0; cnt < 2; cnt++) { if((ioreq = ncp->ncp_WritePending[cnt])) { KPRINTF(1, ("Aborting pending write...\n")); psdAbortPipe(ncp->ncp_EPOutPipe[cnt]); psdWaitPipe(ncp->ncp_EPOutPipe[cnt]); ioreq->ios2_Req.io_Error = IOERR_ABORTED; ReplyMsg((struct Message *) ioreq); ncp->ncp_WritePending[cnt] = NULL; } } if(ncp->ncp_ReadPending) { KPRINTF(1, ("Aborting pending read...\n")); psdAbortPipe(ncp->ncp_EPInPipe); psdWaitPipe(ncp->ncp_EPInPipe); ncp->ncp_ReadPending = NULL; } Permit(); nDoEvent(ncp, S2EVENT_OFFLINE); KPRINTF(20, ("Going down the river!\n")); nFreeEth(ncp); } AROS_USERFUNC_EXIT } /* \\\ */ /* /// "nAllocEth()" */ struct NepClassEth * nAllocEth(void) { struct Task *thistask; struct NepClassEth *ncp; thistask = FindTask(NULL); do { ncp = thistask->tc_UserData; if(!(ncp->ncp_Base = OpenLibrary("poseidon.library", 4))) { Alert(AG_OpenLib); break; } ncp->ncp_Interface = NULL; do { ncp->ncp_Interface = psdFindInterface(ncp->ncp_Device, ncp->ncp_Interface, TAG_END); if(!ncp->ncp_Interface) { break; } ncp->ncp_EPIn = psdFindEndpoint(ncp->ncp_Interface, NULL, EA_IsIn, TRUE, EA_TransferType, USEAF_BULK, TAG_END); ncp->ncp_EPOut = psdFindEndpoint(ncp->ncp_Interface, NULL, EA_IsIn, FALSE, EA_TransferType, USEAF_BULK, TAG_END); } while(!(ncp->ncp_EPOut && ncp->ncp_EPIn)); if(!ncp->ncp_Interface) { psdAddErrorMsg(RETURN_FAIL, (STRPTR) libname, "No interface?"); break; } if(!(ncp->ncp_EPIn && ncp->ncp_EPOut)) { psdAddErrorMsg(RETURN_FAIL, (STRPTR) libname, "IN or OUT endpoint missing!"); break; } ncp->ncp_ReadPending = NULL; ncp->ncp_WritePending[0] = NULL; ncp->ncp_WritePending[1] = NULL; if(!(ncp->ncp_ReadBuffer[0] = AllocVec(ETHER_MAX_LEN * 4, MEMF_PUBLIC|MEMF_CLEAR))) { KPRINTF(1, ("Out of memory for read buffer\n")); break; } ncp->ncp_ReadBuffer[1] = ncp->ncp_ReadBuffer[0] + ETHER_MAX_LEN; ncp->ncp_WriteBuffer[0] = ncp->ncp_ReadBuffer[1] + ETHER_MAX_LEN; ncp->ncp_WriteBuffer[1] = ncp->ncp_WriteBuffer[0] + ETHER_MAX_LEN; ncp->ncp_Unit.unit_MsgPort.mp_SigBit = AllocSignal(-1); ncp->ncp_Unit.unit_MsgPort.mp_SigTask = thistask; ncp->ncp_Unit.unit_MsgPort.mp_Node.ln_Type = NT_MSGPORT; ncp->ncp_Unit.unit_MsgPort.mp_Flags = PA_SIGNAL; if((ncp->ncp_TaskMsgPort = CreateMsgPort())) { if((ncp->ncp_EP0Pipe = psdAllocPipe(ncp->ncp_Device, ncp->ncp_TaskMsgPort, NULL))) { if((ncp->ncp_EPOutPipe[0] = psdAllocPipe(ncp->ncp_Device, ncp->ncp_TaskMsgPort, ncp->ncp_EPOut))) { /* Turn off short packets */ psdSetAttrs(PGA_PIPE, ncp->ncp_EPOutPipe[0], PPA_NoShortPackets, FALSE, PPA_NakTimeout, TRUE, PPA_NakTimeoutTime, 5000, TAG_END); if((ncp->ncp_EPOutPipe[1] = psdAllocPipe(ncp->ncp_Device, ncp->ncp_TaskMsgPort, ncp->ncp_EPOut))) { /* Turn off short packets */ psdSetAttrs(PGA_PIPE, ncp->ncp_EPOutPipe[1], PPA_NoShortPackets, FALSE, PPA_NakTimeout, TRUE, PPA_NakTimeoutTime, 5000, TAG_END); if((ncp->ncp_EPInPipe = psdAllocPipe(ncp->ncp_Device, ncp->ncp_TaskMsgPort, ncp->ncp_EPIn))) { /* Turn off short packets */ psdSetAttrs(PGA_PIPE, ncp->ncp_EPInPipe, PPA_NakTimeout, FALSE, PPA_NakTimeoutTime, 5000, PPA_AllowRuntPackets, TRUE, TAG_END); ncp->ncp_Task = thistask; return(ncp); } psdFreePipe(ncp->ncp_EPOutPipe[1]); } psdFreePipe(ncp->ncp_EPOutPipe[0]); } psdFreePipe(ncp->ncp_EP0Pipe); } DeleteMsgPort(ncp->ncp_TaskMsgPort); } FreeSignal((LONG) ncp->ncp_Unit.unit_MsgPort.mp_SigBit); } while(FALSE); if(ncp->ncp_ReadBuffer[0]) { FreeVec(ncp->ncp_ReadBuffer[0]); ncp->ncp_ReadBuffer[0] = NULL; } CloseLibrary(ncp->ncp_Base); Forbid(); ncp->ncp_Task = NULL; if(ncp->ncp_ReadySigTask) { Signal(ncp->ncp_ReadySigTask, 1L<<ncp->ncp_ReadySignal); } return(NULL); } /* \\\ */ /* /// "nFreeEth()" */ void nFreeEth(struct NepClassEth *ncp) { struct IOSana2Req *ioreq; Forbid(); /* Disable the message port, messages may still be queued */ ncp->ncp_Unit.unit_MsgPort.mp_SigTask = NULL; ncp->ncp_Unit.unit_MsgPort.mp_Flags = PA_IGNORE; FreeSignal((LONG) ncp->ncp_Unit.unit_MsgPort.mp_SigBit); // get rid of all messages that still have appeared here while((ioreq = (struct IOSana2Req *) GetMsg(&ncp->ncp_Unit.unit_MsgPort))) { ioreq->ios2_Req.io_Error = IOERR_ABORTED; ReplyMsg((struct Message *) ioreq); } Permit(); psdFreePipe(ncp->ncp_EPInPipe); psdFreePipe(ncp->ncp_EPOutPipe[0]); psdFreePipe(ncp->ncp_EPOutPipe[1]); psdFreePipe(ncp->ncp_EP0Pipe); if(ncp->ncp_ReadBuffer[0]) { FreeVec(ncp->ncp_ReadBuffer[0]); ncp->ncp_ReadBuffer[0] = NULL; } DeleteMsgPort(ncp->ncp_TaskMsgPort); CloseLibrary(ncp->ncp_Base); Forbid(); ncp->ncp_Task = NULL; if(ncp->ncp_ReadySigTask) { Signal(ncp->ncp_ReadySigTask, 1L<<ncp->ncp_ReadySignal); } } /* \\\ */ /* /// "nDoEvent()" */ void nDoEvent(struct NepClassEth *ncp, ULONG events) { struct IOSana2Req *worknode, *nextnode; KPRINTF(1, ("DoEvent events: 0x%08lx\n", events)); Forbid(); /* Process pending S2_ONEVENT requests */ worknode = (struct IOSana2Req *) ncp->ncp_EventList.lh_Head; while((nextnode = (struct IOSana2Req *) (((struct Node *) worknode)->ln_Succ))) { if(worknode->ios2_WireError & events) { Remove(&worknode->ios2_Req.io_Message.mn_Node); worknode->ios2_Req.io_Message.mn_Node.ln_Type = NT_REPLYMSG; KPRINTF(1, ("DoEvent: returned eventreq 0x%08lx\n", worknode)); ReplyMsg(&worknode->ios2_Req.io_Message); } worknode = nextnode; } Permit(); } /* \\\ */ /* /// "support routines" */ static inline void *callcopy(void *routine, void *from, void *to, ULONG len) { void * (*call) (APTR, APTR, ULONG) = routine; return (*call) (from, to, len); } #define callfilter CallHookPkt /* \\\ */ /* /// "nWritePacket()" */ BOOL nWritePacket(struct NepClassEth *ncp, struct IOSana2Req *ioreq) { ULONG packettype; struct EtherPacketHeader *eph; // UBYTE *packetdata; UBYTE *copydest; UWORD writelen; struct BufMan *bufman; struct Sana2PacketTypeStats *stats; UBYTE *buf = ncp->ncp_WriteBuffer[ncp->ncp_WriteBufNum]; LONG encaplen; packettype = ioreq->ios2_PacketType; copydest = buf; writelen = ioreq->ios2_DataLength; bufman = ioreq->ios2_BufferManagement; // remove RNDIS header encaplen = urndis_encap(ncp, buf ,writelen + (!(ioreq->ios2_Req.io_Flags & SANA2IOF_RAW) ? sizeof(struct EtherPacketHeader) : 0) ); copydest += encaplen; writelen += encaplen; eph = (struct EtherPacketHeader *)copydest; /* Not a raw packet? */ if(!(ioreq->ios2_Req.io_Flags & SANA2IOF_RAW)) { UWORD cnt; KPRINTF(10, ("RAW WRITE!\n")); /* The ethernet header isn't included in the data */ /* Build ethernet packet header */ for(cnt = 0; cnt < ETHER_ADDR_SIZE; cnt++) { eph->eph_Dest[cnt] = ioreq->ios2_DstAddr[cnt]; eph->eph_Src[cnt] = ncp->ncp_MacAddress[cnt]; } eph->eph_Type = AROS_BE2WORD(packettype); /* Packet data is at txbuffer */ copydest += sizeof(struct EtherPacketHeader); writelen += sizeof(struct EtherPacketHeader); } /* Dma not available, fallback to regular copy */ if(callcopy(bufman->bm_CopyFromBuf, copydest, ioreq->ios2_Data, ioreq->ios2_DataLength) == NULL) { KPRINTF(10, ("writepacket: copyfrom returned failure!\n")); /* Trigger any tx, buff or generic error events */ nDoEvent(ncp, S2EVENT_ERROR|S2EVENT_TX|S2EVENT_BUFF); /* Set error code and terminate the iorequest. NOTE: Can't use RC_* or deverror() this is not called from devBeginIO()! */ ioreq->ios2_DataLength = 0; ioreq->ios2_Req.io_Error = S2ERR_NO_RESOURCES; ioreq->ios2_WireError = S2WERR_BUFF_ERROR; return FALSE; } //bug("out %d\n",writelen); KPRINTF(20, ("PktOut[%ld] %ld\n", ncp->ncp_WriteBufNum, writelen)); //dumpmem(buf, writelen); ncp->ncp_WritePending[ncp->ncp_WriteBufNum] = ioreq; psdSendPipe(ncp->ncp_EPOutPipe[ncp->ncp_WriteBufNum], buf, (ULONG) writelen); ncp->ncp_WriteBufNum ^= 1; DB( if(AROS_BE2WORD(eph->eph_Type) < 1500) { KPRINTF(5, ("writepacket: %04lx%08lx > %04lx%08lx (IEEE802.3) len %lu, %lu bytes\n", *((UWORD *) eph->eph_Src), *((ULONG *) (eph->eph_Src + 2)), *((UWORD *) eph->eph_Dest), *((ULONG *) (eph->eph_Dest + 2)), AROS_BE2WORD(eph->eph_Type), writelen)); } else { KPRINTF(5, ("writepacket: %04lx%08lx > %04lx%08lx type %lu, %lu bytes\n", *((UWORD *) eph->eph_Src), *((ULONG *) (eph->eph_Src + 2)), *((UWORD *) eph->eph_Dest), *((ULONG *) (eph->eph_Dest + 2)), AROS_BE2WORD(eph->eph_Type), writelen)); } //dumpmem(buf, (ULONG) writelen); ) /* Update statistics */ stats = FindPacketTypeStats(ncp, packettype); if(stats) { stats->PacketsSent++; stats->BytesSent += writelen; } ncp->ncp_DeviceStats.PacketsSent++; return TRUE; } /* \\\ */ /* /// "nReadIOReq()" */ UWORD nReadIOReq(struct NepClassEth *ncp, struct EtherPacketHeader *eph, UWORD datasize, struct IOSana2Req *ioreq, UWORD flags) { LIBBASETYPEPTR nh = ncp->ncp_ClsBase; UBYTE *copyfrom; UWORD cnt; /* Handle RAW read */ if(ioreq->ios2_Req.io_Flags & SANA2IOF_RAW) { /* ShapeShifter won't work with `sizeof(struct etherpacket_hdr)' here. This is most likely because it want the RAW ethernet packet checksum size (4) added to the packet size. */ copyfrom = (UBYTE *) eph; datasize += sizeof(struct EtherPacketHeader) + 4; } else { copyfrom = (UBYTE *) (eph + 1); } /* Build up the ios2 structure enough so we can call the packet filter. */ ioreq->ios2_PacketType = AROS_BE2WORD(eph->eph_Type); for(cnt = 0; cnt < ETHER_ADDR_SIZE; cnt++) { ioreq->ios2_SrcAddr[cnt] = eph->eph_Src[cnt]; ioreq->ios2_DstAddr[cnt] = eph->eph_Dest[cnt]; } ioreq->ios2_DataLength = datasize; /* Call the packet filter, if available. */ if((flags & PACKETFILTER) && (((struct BufMan *) ioreq->ios2_BufferManagement)->bm_PacketFilter) && (!callfilter(((struct BufMan *) ioreq->ios2_BufferManagement)->bm_PacketFilter, ioreq, copyfrom))) { /* This packet got dropped! */ KPRINTF(7, ("readioreq: packet type %lu for ioreq 0x%08lx dropped\n", AROS_BE2WORD(eph->eph_Type), ioreq)); return flags; } /* Ok, the packet didn't get dropped, set the BCAST and MCAST flags according to dstaddr. */ /* Address == Multicast? */ if(ioreq->ios2_DstAddr[0] & 1) { /* Address == Broadcast? */ if((*((ULONG *) ioreq->ios2_DstAddr) == 0xffffffff) && (*((UWORD *) (ioreq->ios2_DstAddr + 4)) == 0xffff)) { ioreq->ios2_Req.io_Flags |= SANA2IOF_BCAST; } else { ioreq->ios2_Req.io_Flags |= SANA2IOF_MCAST; } } /* Finally copy the packet data! */ if(callcopy(((struct BufMan *) ioreq->ios2_BufferManagement)->bm_CopyToBuf, ioreq->ios2_Data, copyfrom, ioreq->ios2_DataLength)) { DB( KPRINTF(5, ("readioreq: copytobuffed packet ior 0x%08lx, %04lx%08lx < %04lx%08lx, type %lu, %lu bytes, %s%s%s\n", ioreq, *((UWORD *) ioreq->ios2_DstAddr), *((ULONG *) (ioreq->ios2_DstAddr + 2)), *((UWORD *) ioreq->ios2_SrcAddr), *((ULONG *) (ioreq->ios2_SrcAddr + 2)), ioreq->ios2_PacketType, ioreq->ios2_DataLength, (ioreq->ios2_Req.io_Flags & SANA2IOF_RAW) ? "RAW " : "", (ioreq->ios2_Req.io_Flags & SANA2IOF_BCAST) ? "BCAST " : "", (ioreq->ios2_Req.io_Flags & SANA2IOF_MCAST) ? "MCAST " : "")); //dumpmem(copyfrom, ioreq->ios2_DataLength); ) /* Clear the dropped flag */ flags &= ~DROPPED; } else { KPRINTF(10, ("readioreq: copyto returned failure!\n")); /* Trigger any rx, buff or generic error events */ nDoEvent(ncp, S2EVENT_ERROR|S2EVENT_RX|S2EVENT_BUFF); /* Set error code. NOTE: Can't use RC_* or deverror() this is not called from devBeginIO()! */ ioreq->ios2_DataLength = 0; ioreq->ios2_Req.io_Error = S2ERR_NO_RESOURCES; ioreq->ios2_WireError = S2WERR_BUFF_ERROR; } /* Pull the ioreq off the list & terminate it */ Forbid(); Remove((struct Node *) ioreq); Permit(); ReplyMsg((struct Message *) ioreq); return flags; } /* \\\ */ /* /// "nReadPacket()" */ BOOL nReadPacket(struct NepClassEth *ncp, UBYTE *pktptr, ULONG pktlen) { struct EtherPacketHeader *eph; UBYTE *packetdata; struct BufMan *bufman; struct IOSana2Req *worknode, *nextnode; struct Sana2PacketTypeStats *stats; UWORD flags; UWORD datasize; KPRINTF(20, ("PktIn [%ld] %ld\n", ncp->ncp_ReadBufNum, pktlen)); //bug("in %d\n",pktlen); // add RNDIS header urndis_decap(ncp, (BYTE **)&pktptr, (LONG *)&pktlen); //dumpmem(pktptr, pktlen); if(pktlen < 14) { ncp->ncp_DeviceStats.BadData++; return FALSE; } ncp->ncp_DeviceStats.PacketsReceived++; eph = (struct EtherPacketHeader *) pktptr; packetdata = (UBYTE *) (eph + 1); stats = FindPacketTypeStats(ncp, (ULONG) AROS_BE2WORD(eph->eph_Type)); flags = DROPPED|PACKETFILTER; /* Calculate size of the actual data */ datasize = pktlen - sizeof(struct EtherPacketHeader); /* Is the packet datasize valid? */ if(pktlen <= ETHER_MAX_LEN) { /* Update the packet statistics */ if(stats) { stats->PacketsReceived++; stats->BytesReceived += datasize; /* NOTE: don't include headers */ } /* For each device user (bufman) NOTE: We absolutely *MUST* try to offer the packet to *all* different device users (SANA-II V2 spec requirement). */ Forbid(); bufman = (struct BufMan *) ncp->ncp_BufManList.lh_Head; while(((struct Node *) bufman)->ln_Succ) { /* For each queued read request (ioreq) */ worknode = (struct IOSana2Req *) bufman->bm_RXQueue.lh_Head; while((nextnode = (struct IOSana2Req *) (((struct Node *) worknode)->ln_Succ))) { /* Check the packet type. Also handles 802.3 packets. */ if((worknode->ios2_PacketType == AROS_BE2WORD(eph->eph_Type)) || ((AROS_BE2WORD(eph->eph_Type) < 1500) && (worknode->ios2_PacketType < 1500))) { flags = nReadIOReq(ncp, eph, datasize, worknode, flags); /* Break out - let other callers get the packet too */ break; } worknode = nextnode; } bufman = (struct BufMan *) (((struct Node *) bufman)->ln_Succ); } Permit(); /* Now we've tried to give the packet to every CMD_READ caller. If DROPPED is set at this point no-one wanted this packet. */ if(flags & DROPPED) { /* So there were no outstanding CMD_READs or the packet wasn't accepted by any of them. Okay, check if we have any pending S2_READORPHAN ioreq in list and if we have return this packet with it. Note that packet filter must not be used for this time! NOTE: orphanlist is global, ie. only one caller will get the packet if multiple users have pending S2_READORPHANs. */ /* Process pending orphanread iorequs */ Forbid(); worknode = (struct IOSana2Req *) ncp->ncp_OrphanQueue.lh_Head; while((nextnode = (struct IOSana2Req *) (((struct Node *) worknode)->ln_Succ))) { nReadIOReq(ncp, eph, datasize, worknode, 0); worknode = nextnode; } Permit(); } else { /* Packet not dropped - return ok */ return TRUE; } } else { KPRINTF(20, ("Pktlen %ld invalid!\n", pktlen)); ncp->ncp_DeviceStats.BadData++; } /* Update global dropped packet counter. */ ncp->ncp_DeviceStats.UnknownTypesReceived++; /* Update dropped packet statistics. */ if(stats) { stats->PacketsDropped++; } KPRINTF(9, ("readpacket: packet type %lu dropped\n", AROS_BE2WORD(eph->eph_Type))); /* Trigger any rx or generic error events */ nDoEvent(ncp, S2EVENT_ERROR|S2EVENT_RX); return FALSE; } /* \\\ */ /**************************************************************************/
rndis.class.h
edit/* * $Id$ */ #ifndef RNDIS_CLASS_H #define RNDIS_CLASS_H /* *---------------------------------------------------------------------------- * Includes for rndis class *---------------------------------------------------------------------------- */ #include "common.h" #include <devices/sana2.h> #include <devices/sana2specialstats.h> #include <libraries/gadtools.h> #include <devices/newstyle.h> #include <string.h> #include <stddef.h> #include <stdio.h> #include "if_urndisreg.h" #include "rndis.h" #include "dev.h" /* Protos */ struct NepClassEth * usbAttemptDeviceBinding(struct NepEthBase *nh, struct PsdDevice *pd); struct NepClassEth * usbForceDeviceBinding(struct NepEthBase *nh, struct PsdDevice *pd); void usbReleaseDeviceBinding(struct NepEthBase *nh, struct NepClassEth *ncp); struct NepClassEth * nAllocEth(void); void nFreeEth(struct NepClassEth *ncp); void nSetOnline(struct NepClassEth *ncp); void nDoEvent(struct NepClassEth *ncp, ULONG events); BOOL nWritePacket(struct NepClassEth *ncp, struct IOSana2Req *ioreq); BOOL nReadPacket(struct NepClassEth *ncp, UBYTE *pktptr, ULONG len); BOOL nLoadClassConfig(struct NepEthBase *nh); BOOL nLoadBindingConfig(struct NepClassEth *ncp); LONG nOpenBindingCfgWindow(struct NepEthBase *nh, struct NepClassEth *ncp); void nGUITaskCleanup(struct NepClassEth *nh); uint32_t urndis_ctrl_init(struct NepClassEth *ncp); uint32_t urndis_ctrl_handle(struct NepClassEth *ncp, struct urndis_comp_hdr *hdr,void **buf, size_t *bufsz); void urndis_attach(struct NepClassEth *ncp); long urndis_encap(struct NepClassEth *ncp, BYTE *m,LONG len ); void urndis_decap(struct NepClassEth *ncp, BYTE **buf, LONG *len); AROS_UFP0(void, nEthTask); AROS_UFP0(void, nGUITask); #endif /* RNDIS_CLASS_H */
UVC webcam
editOnly thing then left to do is to define the uvc api on Aros and code the driver (not like to see v4l(2) on Aros).
Should the uvc.class expose a device or a library? I would like it to expose a library that one can query for attached devices and control the camera(s). It would also need some kind of event handler if some buttons are present on the thing. A device with a fifo like Webcam:cam1 would be nice of course. Leaving all the decoding to ffmpeg or mplayer or whatever... Library is more amiga'ish whilst we can alway write a new webcam tool app with mui using library + lib264.
Proposal for video capturing from web cams:
- Only support web cams and a test pattern. Input only -> webcam.library which one registers different drivers
- webcam.library provides the test pattern itself
On the other hand there is no use for a web cam if its output cannot be tossed around at will. It would need all sort of interfaces and the frame format would also need conversions. MJPEG to RGB etc. Some sort of common video stack is needed.
void bayer2rgb24(unsigned char *dst, unsigned char *src, long int WIDTH, long int HEIGHT) { long int i; unsigned char *rawpt, *scanpt; long int size; rawpt = src; scanpt = dst; size = WIDTH*HEIGHT; for ( i = 0; i < size; i++ ) { if ( (i/WIDTH) % 2 == 0 ) { if ( (i % 2) == 0 ) { /* B */ if ( (i > WIDTH) && ((i % WIDTH) > 0) ) { *scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+ *(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* R */ *scanpt++ = (*(rawpt-1)+*(rawpt+1)+ *(rawpt+WIDTH)+*(rawpt-WIDTH))/4; /* G */ *scanpt++ = *rawpt; /* B */ } else { /* first line or left column */ *scanpt++ = *(rawpt+WIDTH+1); /* R */ *scanpt++ = (*(rawpt+1)+*(rawpt+WIDTH))/2; /* G */ *scanpt++ = *rawpt; /* B */ } } else { /* (B)G */ if ( (i > WIDTH) && ((i % WIDTH) < (WIDTH-1)) ) { *scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; /* R */ *scanpt++ = *rawpt; /* G */ *scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* B */ } else { /* first line or right column */ *scanpt++ = *(rawpt+WIDTH); /* R */ *scanpt++ = *rawpt; /* G */ *scanpt++ = *(rawpt-1); /* B */ } } } else { if ( (i % 2) == 0 ) { /* G(R) */ if ( (i < (WIDTH*(HEIGHT-1))) && ((i % WIDTH) > 0) ) { *scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* R */ *scanpt++ = *rawpt; /* G */ *scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; /* B */ } else { /* bottom line or left column */ *scanpt++ = *(rawpt+1); /* R */ *scanpt++ = *rawpt; /* G */ *scanpt++ = *(rawpt-WIDTH); /* B */ } } else { /* R */ if ( i < (WIDTH*(HEIGHT-1)) && ((i % WIDTH) < (WIDTH-1)) ) { *scanpt++ = *rawpt; /* R */ *scanpt++ = (*(rawpt-1)+*(rawpt+1)+ *(rawpt-WIDTH)+*(rawpt+WIDTH))/4; /* G */ *scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+ *(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* B */ } else { /* bottom line or right column */ *scanpt++ = *rawpt; /* R */ *scanpt++ = (*(rawpt-1)+*(rawpt-WIDTH))/2; /* G */ *scanpt++ = *(rawpt-WIDTH-1); /* B */ } } } rawpt++; } }
PS3-eye camera and it uses BULK transfers. Using that camera would be the quickest path capturing video on Aros. PS3 camera has also a extremely high framerate, it could be used for all sort of things like 3D scanning etc. Managed to open the BULK endpoint. Camera outputs YUV and I just write it ARGB on the window. Needs some conversion between. Some artifacts are from the frame header (12 bytes every now and then). It takes some time until camera shows anything and that's just because I wait for the last frame to arrive before capturing, sometimes it doesn't catch it. Removed uvc header bytes and it all starts to look something recognizable. There's a sweet spot on the payload size, once you hit it there is no need to sync the frames for raw pixelwrites to rastport.
Misc
editsome or all of the public Poseidon includes are packed with these directives:
#if defined(__GNUC__) # pragma pack(1) #endif
Any objections to removing this for ABIv1? in AROS we use native packing where possible.
That would mean code would stop being interchangeable between AROS/OS3/MorphOS/OS4 and would also affect binary compatibility with m68k on PPC. Not a good move IMHO. Perhaps adding some kind of #define or macro to only add the packing if M68K_COMPATIBILITY is enabled would allow better performance in platforms where no m68k transparent emulator would be possible. There's no point in having binary compatibility for USB only. None of the other system structures are packed.
These are raw USB packets. Don't do this, or Poseidon will stop working. MorphOS uses pack(2). Sorry, you're right about the pack(1) instances. However, there are other system structures packed with pack(2), which should be unpacked.