Monday, August 14, 2006

Windows USB Driver IRP 射後不理的方式

目的:想要非同步丟 IRP,但是不想管理及善後 IRP 。

在 Programming The Microsoft Windows Driver Model 一書,第十二章,有提到如何用同步的方式發出 USB 的 IRP。但是它沒提到如何發出非同步的 USB IRP。非同步發出 USB IRP 的方式是必要的,因為你總不可能每次都等待這個 IRP 完成後才進行下個步驟吧。 其實 irp 善後很簡單,只要呼叫 iofreeirp 就可以。不過因為 usb 還要多 free urb,所以事情會稍微複雜一點。我原本想要在 complete routine 內利用傳入的 irp 把它對應的 urb 給找出來,不過失敗了(其實這是有可能的,而且這才是比較正確的作法)。所以只好利用傳入的 context 來搞花樣。

IRP 射後不理的方式很簡單,只要能夠在 complete routine 內把對應的 irp 及 urb free 掉就可以。作法是弄一個 data struct ,把 irp 及 urb 的 pointer 寫進去,最後把這個 data struct 傳給 complete routine 。然後你就可以在 complete routine 內 free irp 及 urb。問題來了,這個 data struct 是動態 allocate 的,所以也要 free 它,可以在 complete routine 內 free 它嗎?答案是可以的,所以一切事情就解決了。

要注意幾點: 1. iofreeirp 必須在 complete routine 內最後呼叫,不然整個 OS 會 hang 住。我不知道這是為甚麼。

2. 這樣作會大量消耗 CPU resource。

3. 如果 irp 無法 complete,你也無法 cancel 它。這是個大問題,不過這樣只會導致你的 driver halt 後,在 non-paged pool 內發生 memory leak。解決方法是有,不過考量現況,不作也罷。最快的方式是重開機。不過我很少遇到 irp 不會 complete 的狀況。

下面是範例: ---------------------------------------------------------------------------------------

struct IRP_INFO{ DeviceInfo *deviceInfo; USBD_PIPE_HANDLE pipeHandle; PURB pUrb; UCHAR data[2048]; };
/*! \brief The Completion routine for Asynchronous Bulk Out request. */ NTSTATUS USBAsynchronousBulkOutComplete(PDEVICE_OBJECT pUSBDeviceObject, PIRP pIrp, PVOID context) {
NTSTATUS status = STATUS_SUCCESS; struct IRP_INFO *irpInfo = (struct IRP_INFO *)context; DeviceInfo deviceInfo = irpInfo->deviceInfo; PADAPTER pAdapter = (PADAPTER)deviceInfo->pAdapter; USBD_PIPE_HANDLE pipeHandle; ULONG fifoIndex = 0;
// find this IRP belongs which Endpoint pipeHandle = irpInfo->pipeHandle;
if( pIrp->Cancel == TRUE ) { DbgPrint("AsynchronousBulkOutComplete(() : IRP has been cancelled.\n"); }
NdisFreeMemory(irpInfo->pUrb, sizeof(URB), 0); NdisFreeMemory(irpInfo, sizeof(struct IRP_INFO), 0); IoFreeIrp(pIrp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
// \brief USB Asynchronous Bulk Out // This function can be called at IRQL <= Dispatch Level. NDIS_STATUS USBASynchronousBulkOut(ADAPTER *pAdapter, ULONG fifoIndex, UCHAR *data, ULONG dataLength){ NDIS_STATUS status = NDIS_STATUS_SUCCESS; PURB pUrb = NULL; HANDLE usbPipeHandle = NULL; IO_STATUS_BLOCK ioStatus; PIRP pIrp; NTSTATUS ntStatus; PIO_STACK_LOCATION stack; USBD_STATUS usbStatus; struct IRP_INFO *irpInfo = NULL;
if (dataLength > 2048) { return NDIS_STATUS_NOT_ACCEPTED; }
usbPipeHandle = pAdapter->BulkOutPipeHandle;
// Allocate IRP_INFO NdisAllocateMemoryWithTag((PVOID *)&irpInfo, sizeof(struct IRP_INFO), 'AABB'); if (irpInfo == NULL){ KdPrint(("USBASynchronousBulkOut() : can not allocate IRP_INFO.\n")); return NDIS_STATUS_NOT_ACCEPTED; }
// Allocate URB NdisAllocateMemoryWithTag((PVOID *)&pUrb, sizeof(URB), 'AABB'); if (pUrb == NULL){ KdPrint(("USBASynchronousBulkOut() : can not allocate URB.")); NdisFreeMemory(irpInfo, sizeof(struct IRP_INFO), 0); return NDIS_STATUS_NOT_ACCEPTED; }
// Allocate IRP pIrp = IoAllocateIrp(pAdapter->NextDeviceStackSize, FALSE); if (pIrp == NULL){ KdPrint(("USBASynchronousBulkOut() : can not allocate IRP.")); NdisFreeMemory(pUrb, sizeof(URB), 0); NdisFreeMemory(irpInfo, sizeof(struct IRP_INFO), 0); return NDIS_STATUS_NOT_ACCEPTED; }
irpInfo->deviceInfo = &pAdapter->DeviceInfo; irpInfo->pipeHandle = usbPipeHandle; irpInfo->pUrb = pUrb; NdisMoveMemory(irpInfo->data, data, dataLength);
// Build URB for USBD UsbBuildInterruptOrBulkTransferRequest( pUrb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), usbPipeHandle, (PVOID)irpInfo->data, NULL, dataLength, 0, NULL);
// call the calss driver to perform the operation // pass the URB to the USB driver stack stack = IoGetNextIrpStackLocation(pIrp); stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; stack->Parameters.Others.Argument1 = pUrb; stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
IoSetCompletionRoutine( pIrp, // irp to use USBAsynchronousBulkOutComplete, // routine to call when irp is done (PVOID)irpInfo, // context to pass routine TRUE, // call on success TRUE, // call on error TRUE); // call on cancel
// Call IoCallDriver to send the irp to the usb port ntStatus = IoCallDriver(pAdapter->DeviceIfno.pUSBDeviceObject, pIrp); usbStatus = URB_STATUS(pUrb);
// The USB driver should always return STATUS_PENDING when it receives a write irp if ( ntStatus != STATUS_PENDING || USBD_HALTED(usbStatus) ) {
DbgPrint("[Error]USBASynchronousBulkOut() : IoCallDriver failed!!! IRP STATUS = 0x%X, USB STATUS = %X.\n", ntStatus, usbStatus); status = NDIS_STATUS_NOT_ACCEPTED;
} else {
status = NDIS_STATUS_SUCCESS; }
return status; }

No comments:

codeblock