/*************************************************************************** Copyright (c) 2001 Microsoft Corporation Module Name: USBUTIL.C Abstract: Generic USB routines - must be called at PASSIVE_LEVEL Environment: Kernel Mode Only Notes: THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. Copyright (c) 2001 Microsoft Corporation. All Rights Reserved. Revision History: 01/08/2001 : created Authors: Tom Green ****************************************************************************/ #include "pch.h" #include #include #include #include "intread.h" #include "usbutil.h" #include "usbpriv.h" #include "usbdbg.h" #ifdef ALLOC_PRAGMA #endif // ALLOC_PRAGMA #if DBG ULONG USBUtil_DebugTraceLevel = 0; PUSB_WRAP_PRINT USBUtil_DbgPrint = DbgPrint; #endif /************************************************************************/ /* USBCallSync */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Send URB down USB stack. Synchronous call. */ /* Caller is responsible for URB (allocating and freeing) */ /* */ /* Arguments: */ /* */ /* LowerDevObj - pointer to a device object */ /* Urb - pointer to URB */ /* MillisecondsTimeout - milliseconds to wait for completion */ /* RemoveLock - pointer to remove lock */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS USBCallSyncEx(IN PDEVICE_OBJECT LowerDevObj, IN PURB Urb, IN LONG MillisecondsTimeout, IN PIO_REMOVE_LOCK RemoveLock, IN ULONG RemLockSize) { NTSTATUS ntStatus = STATUS_SUCCESS; PIRP irp = NULL; KEVENT event; PIO_STACK_LOCATION nextStack; BOOLEAN gotRemoveLock = FALSE; __try { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: USBCallSync\n")); ntStatus = IoAcquireRemoveLockEx(RemoveLock, LowerDevObj, __FILE__, __LINE__, RemLockSize); if(NT_SUCCESS(ntStatus)) { gotRemoveLock = TRUE; } else { DBGPRINT(DBG_USBUTIL_USB_ERROR, ("USBCallSync: Pending remove on device\n")); __leave; } // do some parameter checking before going too far if(LowerDevObj == NULL || Urb == NULL || MillisecondsTimeout < 0) { DBGPRINT(DBG_USBUTIL_USB_ERROR, ("USBCallSync: Invalid paramemter passed in\n")); ntStatus = STATUS_INVALID_PARAMETER; __leave; } // issue a synchronous request KeInitializeEvent(&event, SynchronizationEvent, FALSE); irp = IoAllocateIrp(LowerDevObj->StackSize, FALSE); // check to see if we allocated an Irp if(!irp) { DBGPRINT(DBG_USBUTIL_USB_ERROR, ("USBCallSync: Couldn't allocate Irp\n")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; __leave; } // Set the Irp parameters nextStack = IoGetNextIrpStackLocation(irp); nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB; nextStack->Parameters.Others.Argument1 = Urb; // Set the completion routine, which will signal the event IoSetCompletionRoutine(irp, USBCallSyncCompletionRoutine, &event, TRUE, // InvokeOnSuccess TRUE, // InvokeOnError TRUE); // InvokeOnCancel ntStatus = IoCallDriver(LowerDevObj, irp); // block on pending request if(ntStatus == STATUS_PENDING) { LARGE_INTEGER timeout; PLARGE_INTEGER pTimeout = NULL; // check and see if they have passed in a number of milliseconds to time out if(MillisecondsTimeout) { // setup timeout timeout = RtlEnlargedIntegerMultiply(ONE_MILLISECOND_TIMEOUT, MillisecondsTimeout); pTimeout = &timeout; } ntStatus = KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, pTimeout); // if it timed out, cancel the irp and return appropriate status if(ntStatus == STATUS_TIMEOUT) { ntStatus = STATUS_IO_TIMEOUT; // Cancel the Irp we just sent. IoCancelIrp(irp); // Wait until the cancel completes KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); } else { // didn't timeout, so return current status ntStatus = irp->IoStatus.Status; } } } __finally { if(gotRemoveLock) { IoReleaseRemoveLockEx(RemoveLock, LowerDevObj, RemLockSize); } if(irp) { IoFreeIrp(irp); } DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: USBCallSync\n")); } return ntStatus; } // USBCallSync /************************************************************************/ /* USBCallSyncCompletionRoutine */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Completion routine for USB sync request. */ /* */ /* Arguments: */ /* */ /* DeviceObject - pointer to device object */ /* Irp - pointer to an I/O Request Packet */ /* Context - pointer to context of call */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS USBCallSyncCompletionRoutine(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { PKEVENT kevent; DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: USBCallSyncCompletionRoutine\n")); kevent = (PKEVENT) Context; KeSetEvent(kevent, IO_NO_INCREMENT, FALSE); DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: USBCallSyncCompletionRoutine\n")); return STATUS_MORE_PROCESSING_REQUIRED; } // USBCallSyncCompletionRoutine /************************************************************************/ /* USBVendorRequest */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Issue USB vendor specific request */ /* */ /* Arguments: */ /* */ /* LowerDevObj - pointer to a device object */ /* Request - request field of vendor specific command */ /* Value - value field of vendor specific command */ /* Index - index field of vendor specific command */ /* Buffer - pointer to data buffer */ /* BufferSize - data buffer length */ /* Read - data direction flag */ /* Timeout - number of milliseconds to wait for completion */ /* RemoveLock - pointer to remove lock */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS USBVendorRequestEx(IN PDEVICE_OBJECT LowerDevObj, IN REQUEST_RECIPIENT Recipient, IN UCHAR Request, IN USHORT Value, IN USHORT Index, IN OUT PVOID Buffer, IN OUT PULONG BufferSize, IN BOOLEAN Read, IN LONG MillisecondsTimeout, IN PIO_REMOVE_LOCK RemoveLock, IN ULONG RemLockSize) { NTSTATUS ntStatus = STATUS_SUCCESS; PURB urb = NULL; ULONG size; ULONG length; USHORT function; PAGED_CODE(); __try { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: USBVendorRequest\n")); // do some parameter checking before going too far if(LowerDevObj == NULL || MillisecondsTimeout < 0) { DBGPRINT(DBG_USBUTIL_USB_ERROR, ("USBClassRequest: Invalid paramemter passed in\n")); ntStatus = STATUS_INVALID_PARAMETER; __leave; } // length of buffer passed in length = BufferSize ? *BufferSize : 0; // set the buffer length to 0 in case of error if(BufferSize) { *BufferSize = 0; } size = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST); // allocate memory for the Urb urb = ALLOC_MEM(NonPagedPool, size, USBLIB_TAG); // check to see if we allocated a urb if(!urb) { DBGPRINT(DBG_USBUTIL_USB_ERROR, ("USBVendorRequest: Couldn't allocate URB\n")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; __leave; } switch (Recipient) { case Device: function = URB_FUNCTION_VENDOR_DEVICE; break; case Interface: function = URB_FUNCTION_VENDOR_INTERFACE; break; case Endpoint: function = URB_FUNCTION_VENDOR_ENDPOINT; break; case Other: function = URB_FUNCTION_VENDOR_OTHER; break; } UsbBuildVendorRequest(urb, function, (USHORT) size, Read ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT, 0, Request, Value, Index, Buffer, NULL, length, NULL); ntStatus = USBCallSyncEx(LowerDevObj, urb, MillisecondsTimeout, RemoveLock, RemLockSize); // get length of buffer if(BufferSize) { *BufferSize = urb->UrbControlVendorClassRequest.TransferBufferLength; } } __finally { if(urb) { FREE_MEM(urb); } DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: USBVendorRequest\n")); } return ntStatus; } // USBVendorRequest /************************************************************************/ /* USBClassRequest */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Issue USB Class specific request */ /* */ /* Arguments: */ /* */ /* LowerDevObj - pointer to a device object */ /* Recipient - request recipient /* Request - request field of class specific command */ /* Value - value field of class specific command */ /* Index - index field of class specific command */ /* Buffer - pointer to data buffer */ /* BufferSize - data buffer length */ /* Read - data direction flag */ /* RemoveLock - pointer to remove lock */ /* Timeout - number of milliseconds to wait for completion */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS USBClassRequestEx(IN PDEVICE_OBJECT LowerDevObj, IN REQUEST_RECIPIENT Recipient, IN UCHAR Request, IN USHORT Value, IN USHORT Index, IN OUT PVOID Buffer, IN OUT PULONG BufferSize, IN BOOLEAN Read, IN LONG MillisecondsTimeout, IN PIO_REMOVE_LOCK RemoveLock, IN ULONG RemLockSize) { NTSTATUS ntStatus = STATUS_SUCCESS; PURB urb = NULL; ULONG size; ULONG length; USHORT function; __try { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: USBClassRequest\n")); // do some parameter checking before going too far if(LowerDevObj == NULL || MillisecondsTimeout < 0) { DBGPRINT(DBG_USBUTIL_USB_ERROR, ("USBClassRequest: Invalid paramemter passed in\n")); ntStatus = STATUS_INVALID_PARAMETER; __leave; } // length of buffer passed in length = BufferSize ? *BufferSize : 0; // set the buffer length to 0 in case of error if(BufferSize) { *BufferSize = 0; } size = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST); // allocate memory for the Urb urb = ALLOC_MEM(NonPagedPool, size, USBLIB_TAG); // check to see if we allocated a urb if(!urb) { DBGPRINT(DBG_USBUTIL_USB_ERROR, ("USBClassRequest: Couldn't allocate URB\n")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; __leave; } switch (Recipient) { case Device: function = URB_FUNCTION_CLASS_DEVICE; break; case Interface: function = URB_FUNCTION_CLASS_INTERFACE; break; case Endpoint: function = URB_FUNCTION_CLASS_ENDPOINT; break; case Other: function = URB_FUNCTION_CLASS_OTHER; break; } UsbBuildVendorRequest(urb, function, (USHORT) size, Read ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT, 0, Request, Value, Index, Buffer, NULL, length, NULL); ntStatus = USBCallSyncEx(LowerDevObj, urb, MillisecondsTimeout, RemoveLock, RemLockSize); // get length of buffer if(BufferSize) { *BufferSize = urb->UrbControlVendorClassRequest.TransferBufferLength; } } __finally { if(urb) { FREE_MEM(urb); } DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: USBClassRequest\n")); } return ntStatus; } // USBClassRequest /************************************************************************/ /* USBInitializeInterruptTransfers */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Initialize interrupt read pipe */ /* */ /* Arguments: */ /* */ /* DeviceObject - pointer to the device object */ /* LowerDevObj - pointer to the lower device object */ /* Buffer - pointer to buffer for data from interrupt pipe */ /* BuffSize - size of buffer passed in */ /* InterruptPipe - pipe descriptor */ /* DriverContext - context passed to driver callback routine */ /* DriverCallback - driver routines called on completion */ /* RemoveLock - pointer to remove lock for device */ /* */ /* Return Value: */ /* */ /* USB_WRAPPER_HANDLE */ /* */ /************************************************************************/ USB_WRAPPER_HANDLE USBInitializeInterruptTransfersEx(IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_OBJECT LowerDevObj, IN ULONG MaxTransferSize, IN PUSBD_PIPE_INFORMATION InterruptPipe, IN PVOID DriverContext, IN INTERRUPT_CALLBACK DriverCallback, IN ULONG NotificationTypes, IN ULONG PingPongCount, IN PIO_REMOVE_LOCK RemoveLock, IN ULONG RemLockSize) { PUSB_WRAPPER_EXTENSION pUsbWrapperExtension = NULL; ULONG size; NTSTATUS status; BOOLEAN error = FALSE; PAGED_CODE(); __try { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: USBInitializeInterruptTransfers\n")); // // Parameter Checking // if ((LowerDevObj == NULL) || (InterruptPipe == NULL) || (RemoveLock == NULL)) { DBGPRINT(DBG_USBUTIL_USB_ERROR, ("USBInitializeInterruptTransfers: Invalid paramemter passed in\n")); error = TRUE; __leave; } // // Allocate UsbWrapperExtension // pUsbWrapperExtension = ALLOC_MEM(NonPagedPool, sizeof(USB_WRAPPER_EXTENSION), USBLIB_TAG); if(!pUsbWrapperExtension) { DBGPRINT(DBG_USBUTIL_USB_ERROR, ("USBInitializeInterruptTransfers: Couldn't allocate Wrapper Extension\n")); error = TRUE; __leave; } // // Initialize UsbWrapperExtension // pUsbWrapperExtension->DeviceObject = DeviceObject; pUsbWrapperExtension->LowerDeviceObject = LowerDevObj; pUsbWrapperExtension->RemoveLock = RemoveLock; pUsbWrapperExtension->RemLockSize = RemLockSize; // //Initialize Interrupt Read Wrap // UsbWrapInitializeInterruptReadData( pUsbWrapperExtension, InterruptPipe, DriverCallback, DriverContext, MaxTransferSize, NotificationTypes, PingPongCount); InterlockedExchange(&pUsbWrapperExtension->IntReadWrap.HandlingError, 0); // // Init ping-pong stuff // status = UsbWrapInitializePingPongIrps(pUsbWrapperExtension); if(!NT_SUCCESS(status)) { DBGPRINT(DBG_USBUTIL_USB_ERROR, ("USBInitializeInterruptTransfers: Couldn't initialize ping pong irps\n")); error = TRUE; __leave; } __leave; } __finally { if (error && pUsbWrapperExtension) { if (pUsbWrapperExtension->IntReadWrap.PingPongs) { UsbWrapDestroyPingPongs(pUsbWrapperExtension); } FREE_MEM(pUsbWrapperExtension); } DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: USBInitializeInterruptTransfers\n")); } return (USB_WRAPPER_HANDLE) pUsbWrapperExtension; } // USBInitializeInterruptTransfers /************************************************************************/ /* USBStartInterruptTransfers */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Start transfers on interrupt pipe */ /* */ /* Arguments: */ /* */ /* WrapperHandle - pointer to wrapper handle from Init call */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS USBStartInterruptTransfers(IN USB_WRAPPER_HANDLE WrapperHandle) { PUSB_WRAPPER_EXTENSION wrapExt = (PUSB_WRAPPER_EXTENSION) WrapperHandle; NTSTATUS status; __try { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: USBStartInterruptTransfers\n")); if (!wrapExt) { status = STATUS_INVALID_PARAMETER; __leave; } ASSERT(IsListEmpty(&wrapExt->IntReadWrap.IncomingQueue)); status = UsbWrapStartAllPingPongs(wrapExt); } __finally { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: USBStartInterruptTransfers\n")); } return status; } /************************************************************************/ /* USBStopInterruptTransfers */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Stop transfers on interrupt pipe and free resources */ /* */ /* Arguments: */ /* */ /* WrapperHandle - pointer to wrapper handle from Init call */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS USBStopInterruptTransfers(IN USB_WRAPPER_HANDLE WrapperHandle) { PUSB_WRAPPER_EXTENSION wrapExt = WrapperHandle; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); __try { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: USBStopInterruptTransfers\n")); if (!WrapperHandle) { status = STATUS_INVALID_PARAMETER; __leave; } InterlockedExchange(&wrapExt->IntReadWrap.PumpState, PUMP_STOPPED); UsbWrapCancelAllPingPongIrps(wrapExt); UsbWrapEmptyQueue(wrapExt, &wrapExt->IntReadWrap.IncomingQueue); } __finally { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: USBStopInterruptTransfers\n")); } return STATUS_SUCCESS; } // USBStopInterruptTransfers /************************************************************************/ /* USBReleaseInterruptTransfers */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Frees all resources allocated in */ /* USBInitializeInterruptTransfers */ /* */ /* Arguments: */ /* */ /* WrapperHandle - pointer to wrapper handle from Init call */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS USBReleaseInterruptTransfers(IN USB_WRAPPER_HANDLE WrapperHandle) { PUSB_WRAPPER_EXTENSION wrapExt = (PUSB_WRAPPER_EXTENSION) WrapperHandle; NTSTATUS status = STATUS_SUCCESS; __try { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: USBReleaseInterruptTransfers\n")); if (!wrapExt) { status = STATUS_INVALID_PARAMETER; __leave; } UsbWrapDestroyPingPongs(wrapExt); UsbWrapEmptyQueue(wrapExt, &wrapExt->IntReadWrap.IncomingQueue); UsbWrapEmptyQueue(wrapExt, &wrapExt->IntReadWrap.SavedQueue); FREE_MEM(wrapExt); wrapExt = NULL; } __finally { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: USBReleaseInterruptTransfers\n")); } return status; } /************************************************************************/ /* USBStartSelectiveSuspend */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Start selective suspend support for device */ /* */ /* Arguments: */ /* */ /* LowerDevObj - pointer to device object */ /* */ /* Return Value: */ /* */ /* USB_WRAPPER_HANDLE */ /* */ /************************************************************************/ USB_WRAPPER_HANDLE USBStartSelectiveSuspend(IN PDEVICE_OBJECT LowerDevObj) { PAGED_CODE(); __try { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: USBStartSelectiveSuspend\n")); } __finally { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: USBStartSelectiveSuspend\n")); } return NULL; } // USBStartSelectiveSuspend /************************************************************************/ /* USBStopSelectiveSuspend */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Stop selective suspend support for device */ /* */ /* Arguments: */ /* */ /* WrapperHandle - wrapper handle returned by start routine */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS USBStopSelectiveSuspend(IN USB_WRAPPER_HANDLE WrapperHandle) { PAGED_CODE(); __try { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: USBStopSelectiveSuspend\n")); } __finally { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: USBStopSelectiveSuspend\n")); } return STATUS_SUCCESS; } // USBStopSelectiveSuspend /************************************************************************/ /* USBRequestIdle */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Idle request for device */ /* */ /* Arguments: */ /* */ /* WrapperHandle - wrapper handle returned by start routine */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS USBRequestIdle(IN USB_WRAPPER_HANDLE WrapperHandle) { PAGED_CODE(); __try { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: USBRequestIdle\n")); } __finally { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: USBRequestIdle\n")); } return STATUS_SUCCESS; } // USBRequestIdle /************************************************************************/ /* USBRequestWake */ /************************************************************************/ /* */ /* Routine Description: */ /* */ /* Wake request for device */ /* */ /* Arguments: */ /* */ /* WrapperHandle - wrapper handle returned by start routine */ /* */ /* Return Value: */ /* */ /* NTSTATUS */ /* */ /************************************************************************/ NTSTATUS USBRequestWake(IN USB_WRAPPER_HANDLE WrapperHandle) { PAGED_CODE(); __try { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Enter: USBRequestWake\n")); } __finally { DBGPRINT(DBG_USBUTIL_ENTRY_EXIT, ("Exit: USBRequestWake\n")); } return STATUS_SUCCESS; } // USBRequestWake