Aros/開發人員/USBDriversDev
要編寫驅動程式,您首先需要了解 Exec 訊號、訊息、IO 請求和列表。
對於驅動程式(通常是裝置),只有幾個“函式”DoIO、SendIO 等。細節在 IO 請求欄位中。檢視 VUSBHCI 驅動程式。沒有硬體內容,只有針對 Poseidon 驅動程式和 libusb 的高階程式碼。
VUSBHCI 也更易讀,因為所有內容都以結構形式清晰可見。您將識別出其中的 usb 規範內容以及內容是如何傳遞的。
Poseidon 是主要的 USB 程式碼,它本身不知道硬體的任何資訊。驅動程式程式碼附加到它。Poseidon 負責所有首選項、類和驅動程式。驅動程式只嘗試滿足來自 Poseidon 或更具體地說是其類的更高階請求。
每個驅動程式都必須有一個 roothub,根據 usb 規範。Roothub 代表您的硬體。Roothub 有埠,這些是主機控制器擁有的物理埠(不必如此……您也可以實現虛擬埠並讓虛擬裝置連線)
hub.class 呼叫您的 roothub,您的驅動程式必須響應 roothub 請求。所有這些都在 usb 規範中。Poseidon 尚未了解 usb 3.x 請求的任何資訊。驅動程式還響應對物理裝置的請求並在主機和裝置之間傳遞訊息。
EHCI 主機控制器必須在其相應的 PCI 插槽上具有伴侶控制器,否則它不知道哪個埠是其伴侶埠並且無法釋放其埠(它需要事先知道該埠是否有伴侶埠……)。在 poseidon 上,只有一個 PCIUSB 驅動程式,所有這些主機都在單個驅動程式上,並且使用了一些技巧來進行埠轉發,但它們不必在同一個驅動程式上。晶片組能夠檢測到何時釋放埠,伴侶主機控制器將自動接管該埠。
當前的 AROS XHCI 驅動程式程式碼可以從主機控制器中找到 USB3 和 USB2 埠,併為它們以及相應的 roothub 建立埠列表。在某些主機板上,XHCI 可能在其控制器上只有 USB3 埠,還有一個真正的 EHCI 晶片,並且 usb 資料線透過數字 MUX 指向。
來自 pcixhci_controller.c 的函式 BOOL PCIXHCI_FindPorts(struct PCIXHCIUnit *unit) 應該能夠從 XHCI 控制器中檢測到 usb3.0 和 usb2.0 埠。
- 建議使用他的 sata 驅動程式的 slab 記憶體分配器。XHCI 中的記憶體分配很糟糕。這是一個問題。
- 對 init 例程進行了編碼,使其使用 PCI INT(僅事件處理程式 0)。對於 PCI-X,也可以使用其他事件環。
- 刪除巨大的 switch 語句並新增所有 usb3 命令(並非所有命令在一開始都需要),並對 hubss.class 進行編碼以使用它們。
- 硬體環形緩衝區需要正確實現。
如果 XHCI 同時具有 usb3 和 usb2 埠,則驅動程式需要為每個控制器提供兩個單元。這是因為我們需要為 Poseidon 提供正確的 roothub(hub 或 hubss),並且一個驅動程式單元每個單元只能提供一個 roothub。VUSBHCI 中已經存在一些程式碼來實現這一點,但沒有針對 usb3 部分的程式碼。即使在擁有原生 XHCI 驅動程式之前,也可以使用上述託管驅動程式透過 VUSBHCI 驅動程式對更高級別的 usb3 程式碼進行編碼。
一個好處是,我們應該獲得 ISOC 傳輸,因為 XHCI 負責將它們排列起來,無需為不同的資料包建立排程器。
CPU ---------------- Device Side Controller ---------------- Peripherals
Controller Bus Bridge Peripheral Bus
PCI, etc USB, etc
以下是在託管 usb “工作”時所需的步驟
- 構建 AROS 的託管版本(現在需要使用 --enable_usb30_code 構建)
- cd 到構建目錄
- 發出 make kernel-usb(如果它沒有構建所有內容,那麼只構建其餘部分)
- 發出 make kernel-usb-vusbhci
- 發出 make kernel-fs-fat(如果打算使用 fat usb 驅動器)
- 發出 make kernel-partition(否則 AROS 會崩潰,因為它會嘗試使用 partition.library,錯誤?它應該知道它沒有開啟它)
- 發出 make kernel-usb-classes-hubss
某些 Linux 發行版不允許使用者訪問 usb 裝置,因此可能需要在 /etc/udev/rules.d 中新增規則
# Allow user to access all usb devices SUBSYSTEMS=="usb", \ MODE:="0666"
並將其儲存為“49-all-usb-devices.rules”
- 啟動託管 AROS 並轉到首選項,開啟 Trident。
- 轉到“控制器”並按下“新建”,選擇“vusbhci.device”作為單元號 0。按下“聯機”並按下“儲存”。
- 再次轉到“控制器”並按下“新建”,選擇“vusbhci.device”作為單元號 1。按下“聯機”並按下“儲存”。
單元 0 現在是 USB2 roothub,單元 1 是 USB3 roothub。(硬編碼,順序不重要)
每次啟動託管版本的 AROS 時都需要開啟 Trident。託管版本沒有 usbrombootstrap,因此 Trident 用於設定 USB 堆疊。
VUSBHCI.device 依賴於 libusb,因此它需要 Linux 端的包含檔案(libusb-dev 或類似的東西……)
VUSBHCI 目前可能會輸出大量除錯訊息。如果從控制檯啟動,這些訊息當然會輸出到控制檯。
VUSBHCI 只是指 (V)irtual (USB) (H)ost (C)ontroller (I)nterface(虛擬 USB 主機控制器介面)
每個虛擬主機控制器(usb2/usb3(usb3 尚未實現))只有一個虛擬埠。AROS 將在 Trident 啟動後以及裝置插入後看到 usb 裝置。AROS 不能接管已插入的裝置,因為驅動程式使用 libusb 的插入事件。Linux 已經列舉並重置了裝置,因此在 AROS 端需要一些 hackery。VUSHCI 驅動程式不會重置裝置,也不會為其發出新的地址。否則它會使 Linux 混亂。AROS 然後將擁有該裝置並按其意願使用它。
要開始為 USB3 編碼(現在,如果沒有它,vusbhci.device 就無法構建),那麼 AROS 構建需要使用“--enable-usb30-code”開關進行配置。這將改變 Poseidon USB 堆疊,使其對 USB3 有所瞭解。要確保其穩定性,還有很多工作要做。
類原始碼儲存在 rom/usb/classes 中的各自抽屜(資料夾)中
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
/*
* $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 */
#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))
/*
* $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;
}
/* \\\ */
/**************************************************************************/
/* * $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 */
唯一剩下的事情是在 Aros 上定義 uvc api 並編寫驅動程式(不像在 Aros 上看到 v4l(2))。
uvc.class 應該公開裝置還是庫?我希望它公開一個庫,可以查詢連線的裝置並控制攝像頭。如果裝置上存在一些按鈕,它還需要某種事件處理程式。當然,一個像 Webcam:cam1 這樣的帶有 fifo 的裝置會很好。將所有解碼留給 ffmpeg 或 mplayer 或其他任何東西……庫更適合 Amiga,而我們可以使用庫 + lib264 始終使用 mui 編寫新的網路攝像頭工具應用程式。
從網路攝像頭捕獲影片的建議
- 僅支援網路攝像頭和測試圖案。僅輸入 -> webcam.library,可以在其中註冊不同的驅動程式
- webcam.library 自行提供測試圖案
另一方面,如果網路攝像頭的輸出不能隨意丟擲,那麼它就沒有用處。它將需要各種介面,幀格式還需要轉換。MJPEG 到 RGB 等等。需要某種通用的影片堆疊。
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 攝像頭,它使用 BULK 傳輸。使用該攝像頭將是 Aros 上捕獲影片的最快途徑。PS3 攝像頭還具有極高的幀速率,可以用於各種事情,如 3D 掃描等。成功開啟 BULK 端點。攝像頭輸出 YUV,我只是將其寫入視窗上的 ARGB。兩者之間需要一些轉換。一些偽像來自幀頭(每隔 12 位元組出現一次)。攝像頭需要一些時間才能顯示任何內容,這僅僅是因為我在捕獲之前等待最後一幀到達,有時它無法捕獲到。刪除了 uvc 頭位元組,它開始看起來像可以識別的東西。有效負載大小有一個最佳值,一旦你達到這個值,就不需要同步幀以將原始畫素寫入 rastport。
一些或所有公共 Poseidon 包含檔案都使用這些指令打包
#if defined(__GNUC__) # pragma pack(1) #endif
對於 ABIv1,是否反對刪除它?在 Aros 中,我們儘可能使用原生打包。
這意味著程式碼將不再可以在 AROS/OS3/MorphOS/OS4 之間互換,並且還會影響與 PPC 上的 m68k 的二進位制相容性。在我看來,這不是一個好主意。也許新增某種 #define 或宏來僅在啟用 M68K_COMPATIBILITY 時新增打包將允許在不可能使用 m68k 透明模擬器的平臺上實現更好的效能。對 USB 來說,沒有必要進行二進位制相容性。其他系統結構都沒有打包。
這些是原始 USB 資料包。不要這樣做,否則 Poseidon 將停止工作。MorphOS 使用 pack(2)。抱歉,關於 pack(1) 例項你說得對。但是,還有其他使用 pack(2) 打包的系統結構,應該解包。