NTSTATUS Pipe_Reset(__in PDEVICE_CONTEXT deviceContext, __in UCHAR pipeID) { NTSTATUS status = STATUS_INVALID_PARAMETER; PPIPE_CONTEXT pipeContext; PAGED_CODE(); pipeContext = GetPipeContextByID(deviceContext, pipeID); if (pipeContext->Pipe) { USBMSG("pipeID=%02Xh\n", pipeID); status = WdfUsbTargetPipeResetSynchronously(pipeContext->Pipe, WDF_NO_HANDLE, NULL); if (!NT_SUCCESS(status)) { USBERR("WdfUsbTargetPipeResetSynchronously failed pipeID=%02Xh status=%Xh\n", pipeID, status); } else { if (pipeContext->Queue) { PQUEUE_CONTEXT queueContext = GetQueueContext(pipeContext->Queue); queueContext->IsFreshPipeReset = TRUE; } } } else { USBERR("pipeID=%02Xh not found\n", pipeID); } return status; }
/* EvtIoStop Invoked for every inflight pipe write request. The callback executes when: 1) libusbK will stop the queue because of queue policy changes. 2) The system requests stand-by 3) The device is removed */ VOID Queue_OnStop( __in WDFQUEUE Queue, __in WDFREQUEST Request, __in ULONG ActionFlags) { PQUEUE_CONTEXT queueContext; PREQUEST_CONTEXT requestContext; if (Request == NULL || Queue == NULL) { USBERRN("Invalid wdf object."); return; } if ((queueContext = GetQueueContext(Queue)) == NULL) { USBERRN("Invalid queue context."); return; } if ((requestContext = GetRequestContext(Request)) == NULL) { USBERRN("Invalid request context."); return; } // None of the libusbK transfer functions set EvtRequestCancel, hence this should never happen. if (ActionFlags & WdfRequestStopRequestCancelable) { USBERRN("WdfRequestStopRequestCancelable! pipeID=%02Xh", queueContext->Info.EndpointAddress); WdfVerifierDbgBreakPoint(); return; } if (ActionFlags & WdfRequestStopActionSuspend) { USBDBGN("StopAcknowledge for ActionSuspend. pipeID=%02Xh request=%p timeout=%d", queueContext->Info.EndpointAddress, Request, requestContext->Timeout); WdfRequestStopAcknowledge(Request, FALSE); } else if(ActionFlags & WdfRequestStopActionPurge) { USBDBGN("CancelSentRequest for ActionPurge. pipeID=%02Xh request=%p timeout=%d", queueContext->Info.EndpointAddress, Request, requestContext->Timeout); WdfRequestCancelSentRequest(Request); } }
/* EvtIoResume Invoked for pipe write requests which were "StopAcknowledge" in EvtIoStop. This callback exucutes after the device has re-entered D0(working). */ VOID Queue_OnResume(WDFQUEUE Queue, WDFREQUEST Request) { PQUEUE_CONTEXT queueContext; PREQUEST_CONTEXT requestContext; NTSTATUS status = STATUS_SUCCESS; if (Request == NULL || Queue == NULL) { USBERRN("Invalid wdf object."); return; } if ((queueContext = GetQueueContext(Queue)) == NULL) { USBERRN("Invalid queue context."); return; } if ((requestContext = GetRequestContext(Request)) == NULL) { USBERRN("Invalid request context."); return; } mXfer_HandlePipeResetScenarios(status, queueContext, requestContext); }
NTSTATUS Pipe_InitQueue( __in PDEVICE_CONTEXT deviceContext, __in PPIPE_CONTEXT pipeContext, __out WDFQUEUE* queueRef) { WDF_IO_QUEUE_CONFIG queueConfig; WDF_OBJECT_ATTRIBUTES objectAttributes; NTSTATUS status = STATUS_INVALID_HANDLE; WDFQUEUE queue = NULL; PQUEUE_CONTEXT queueContext; WDF_OBJECT_ATTRIBUTES memAttributes; WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); objectAttributes.SynchronizationScope = WdfSynchronizationScopeQueue; *queueRef = NULL; // All queues get a context. At the very least, they will use a local copy of policy and pipe information WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&objectAttributes, QUEUE_CONTEXT); queueConfig.DispatchType = WdfIoQueueDispatchInvalid; Pipe_InitQueueConfig(pipeContext, &queueConfig); if (queueConfig.DispatchType != WdfIoQueueDispatchInvalid) { status = WdfIoQueueCreate(deviceContext->WdfDevice, &queueConfig, &objectAttributes, &queue); if (!NT_SUCCESS(status)) { USBERR("WdfIoQueueCreate failed. pipeID=%02Xh status=%Xh\n", pipeContext->PipeInformation.EndpointAddress, status); goto Exit; } // Create the memory for partial read storage queueContext = GetQueueContext(queue); queueContext->IsFreshPipeReset = TRUE; // SET queueContext->OverOfs RtlZeroMemory(&queueContext->OverOfs, sizeof(queueContext->OverOfs)); // SET queueContext->PipeHandle queueContext->PipeHandle = pipeContext->Pipe; // SET queueContext->Info RtlCopyMemory(&queueContext->Info, &pipeContext->PipeInformation, sizeof(queueContext->Info)); WDF_OBJECT_ATTRIBUTES_INIT(&memAttributes); memAttributes.ParentObject = queue; // Only bulk and interrupt pipes have an OverMem buffer and it is only used // when the queue type is sequential. (RAW_IO=FALSE) if ((queueContext->Info.MaximumPacketSize) && (queueContext->Info.PipeType == WdfUsbPipeTypeBulk || queueContext->Info.PipeType == WdfUsbPipeTypeInterrupt)) { // SET queueContext->OverMem // SET queueContext->OverBuf status = WdfMemoryCreate(&memAttributes, NonPagedPool, POOL_TAG, queueContext->Info.MaximumPacketSize, &queueContext->OverMem, &queueContext->OverBuf); if (!NT_SUCCESS(status)) { USBERRN("WdfMemoryCreate failed. status=%08Xh", status); WdfObjectDelete(queue); goto Exit; } } } *queueRef = queue; Exit: return status; }
VOID EvtIoDeviceControl( _In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength, _In_ ULONG IoControlCode ) /*++ Routine Description: This event callback function is called when the driver receives an (KMDF) IOCTL_HID_Xxx code when handlng IRP_MJ_INTERNAL_DEVICE_CONTROL (UMDF) IOCTL_HID_Xxx, IOCTL_UMDF_HID_Xxx when handling IRP_MJ_DEVICE_CONTROL Arguments: Queue - A handle to the queue object that is associated with the I/O request Request - A handle to a framework request object. OutputBufferLength - The length, in bytes, of the request's output buffer, if an output buffer is available. InputBufferLength - The length, in bytes, of the request's input buffer, if an input buffer is available. IoControlCode - The driver or system defined IOCTL associated with the request Return Value: NTSTATUS --*/ { NTSTATUS status; BOOLEAN completeRequest = TRUE; WDFDEVICE device = WdfIoQueueGetDevice(Queue); PDEVICE_CONTEXT deviceContext = NULL; PQUEUE_CONTEXT queueContext = GetQueueContext(Queue); UNREFERENCED_PARAMETER (OutputBufferLength); UNREFERENCED_PARAMETER (InputBufferLength); deviceContext = GetDeviceContext(device); switch (IoControlCode) { case IOCTL_HID_GET_DEVICE_DESCRIPTOR: // METHOD_NEITHER // // Retrieves the device's HID descriptor. // _Analysis_assume_(deviceContext->HidDescriptor.bLength != 0); status = RequestCopyFromBuffer(Request, &deviceContext->HidDescriptor, deviceContext->HidDescriptor.bLength); break; case IOCTL_HID_GET_DEVICE_ATTRIBUTES: // METHOD_NEITHER // //Retrieves a device's attributes in a HID_DEVICE_ATTRIBUTES structure. // status = RequestCopyFromBuffer(Request, &queueContext->DeviceContext->HidDeviceAttributes, sizeof(HID_DEVICE_ATTRIBUTES)); break; case IOCTL_HID_GET_REPORT_DESCRIPTOR: // METHOD_NEITHER // //Obtains the report descriptor for the HID device. // status = RequestCopyFromBuffer(Request, deviceContext->ReportDescriptor, deviceContext->HidDescriptor.DescriptorList[0].wReportLength); break; case IOCTL_HID_READ_REPORT: // METHOD_NEITHER // // Returns a report from the device into a class driver-supplied // buffer. // status = ReadReport(queueContext, Request, &completeRequest); break; case IOCTL_HID_WRITE_REPORT: // METHOD_NEITHER // // Transmits a class driver-supplied report to the device. // status = WriteReport(queueContext, Request); break; #ifdef _KERNEL_MODE case IOCTL_HID_GET_FEATURE: // METHOD_OUT_DIRECT status = GetFeature(queueContext, Request); break; case IOCTL_HID_SET_FEATURE: // METHOD_IN_DIRECT status = SetFeature(queueContext, Request); break; case IOCTL_HID_GET_INPUT_REPORT: // METHOD_OUT_DIRECT status = GetInputReport(queueContext, Request); break; case IOCTL_HID_SET_OUTPUT_REPORT: // METHOD_IN_DIRECT status = SetOutputReport(queueContext, Request); break; #else // UMDF specific // // HID minidriver IOCTL uses HID_XFER_PACKET which contains an embedded pointer. // // typedef struct _HID_XFER_PACKET { // PUCHAR reportBuffer; // ULONG reportBufferLen; // UCHAR reportId; // } HID_XFER_PACKET, *PHID_XFER_PACKET; // // UMDF cannot handle embedded pointers when marshalling buffers between processes. // Therefore a special driver mshidumdf.sys is introduced to convert such IRPs to // new IRPs (with new IOCTL name like IOCTL_UMDF_HID_Xxxx) where: // // reportBuffer - passed as one buffer inside the IRP // reportId - passed as a second buffer inside the IRP // // The new IRP is then passed to UMDF host and driver for further processing. // case IOCTL_UMDF_HID_GET_FEATURE: // METHOD_NEITHER status = GetFeature(queueContext, Request); break; case IOCTL_UMDF_HID_SET_FEATURE: // METHOD_NEITHER status = SetFeature(queueContext, Request); break; case IOCTL_UMDF_HID_GET_INPUT_REPORT: // METHOD_NEITHER status = GetInputReport(queueContext, Request); break; case IOCTL_UMDF_HID_SET_OUTPUT_REPORT: // METHOD_NEITHER status = SetOutputReport(queueContext, Request); break; #endif // _KERNEL_MODE case IOCTL_HID_GET_STRING: // METHOD_NEITHER status = GetString(Request); break; case IOCTL_HID_GET_INDEXED_STRING: // METHOD_OUT_DIRECT status = GetIndexedString(Request); break; case IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST: // METHOD_NEITHER // // This has the USBSS Idle notification callback. If the lower driver // can handle it (e.g. USB stack can handle it) then pass it down // otherwise complete it here as not inplemented. For a virtual // device, idling is not needed. // // Not implemented. fall through... // case IOCTL_HID_ACTIVATE_DEVICE: // METHOD_NEITHER case IOCTL_HID_DEACTIVATE_DEVICE: // METHOD_NEITHER case IOCTL_GET_PHYSICAL_DESCRIPTOR: // METHOD_OUT_DIRECT // // We don't do anything for these IOCTLs but some minidrivers might. // // Not implemented. fall through... // default: status = STATUS_NOT_IMPLEMENTED; break; } // // Complete the request. Information value has already been set by request // handlers. // if (completeRequest) { WdfRequestComplete(Request, status); } }
NTSTATUS QueueCreate( _In_ WDFDEVICE Device, _Out_ WDFQUEUE *Queue ) /*++ Routine Description: This function creates a default, parallel I/O queue to proces IOCTLs from hidclass.sys. Arguments: Device - Handle to a framework device object. Queue - Output pointer to a framework I/O queue handle, on success. Return Value: NTSTATUS --*/ { NTSTATUS status; WDF_IO_QUEUE_CONFIG queueConfig; WDF_OBJECT_ATTRIBUTES queueAttributes; WDFQUEUE queue; PQUEUE_CONTEXT queueContext; WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( &queueConfig, WdfIoQueueDispatchParallel); #ifdef _KERNEL_MODE queueConfig.EvtIoInternalDeviceControl = EvtIoDeviceControl; #else // // HIDclass uses INTERNAL_IOCTL which is not supported by UMDF. Therefore // the hidumdf.sys changes the IOCTL type to DEVICE_CONTROL for next stack // and sends it down // queueConfig.EvtIoDeviceControl = EvtIoDeviceControl; #endif WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE( &queueAttributes, QUEUE_CONTEXT); status = WdfIoQueueCreate( Device, &queueConfig, &queueAttributes, &queue); if( !NT_SUCCESS(status) ) { KdPrint(("WdfIoQueueCreate failed 0x%x\n",status)); return status; } queueContext = GetQueueContext(queue); queueContext->Queue = queue; queueContext->DeviceContext = GetDeviceContext(Device); queueContext->OutputReport = 0; *Queue = queue; return status; }
VOID Xfer_ReadBulk ( __in WDFQUEUE Queue, __in WDFREQUEST Request) { NTSTATUS status; PREQUEST_CONTEXT requestContext = NULL; PQUEUE_CONTEXT queueContext = NULL; PDEVICE_CONTEXT deviceContext; ULONG remainingLength; ULONG stageLength = 0; ULONG remainderLength = 0; WDF_REQUEST_SEND_OPTIONS sendOptions; PUCHAR transferBuffer; deviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue)); requestContext = GetRequestContext(Request); VALIDATE_REQUEST_CONTEXT(requestContext, status); if (!NT_SUCCESS(status)) goto Exit; if ((queueContext = GetQueueContext(Queue)) == NULL) { status = STATUS_INVALID_DEVICE_REQUEST; USBERRN("Invalid queue context"); goto Exit; } queueContext->Xfer.Transferred = 0; queueContext->Xfer.Length = requestContext->Length; if (!queueContext->OverMem) { status = STATUS_INVALID_DEVICE_REQUEST; USBERRN("Invalid queue context:OverMem"); goto Exit; } if (!queueContext->Info.MaximumPacketSize) { status = STATUS_INVALID_BUFFER_SIZE; USBERRN("PipeID=%02Xh MaximumPacketSize=0", queueContext->Info.EndpointAddress); goto Exit; } // handle pipe reset scenarios: ResetPipeOnResume, AutoClearStall mXfer_HandlePipeResetScenarios(status, queueContext, requestContext); // Handle special case scenario where read length = 0 if (!queueContext->Xfer.Length) { // if AllowPartialReads complete successfully for 0 bytes status = requestContext->Policies.AllowPartialReads ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE; goto Exit; } // Init queue user memory status = GetTransferMemory(Request, requestContext->ActualRequestType, &queueContext->Xfer.UserMem); if (!NT_SUCCESS(status)) { USBERR("GetTransferMemory failed. Status=%08Xh\n", status); goto Exit; } queueContext->Xfer.UserOfs.BufferOffset = 0; queueContext->Xfer.UserOfs.BufferLength = queueContext->Xfer.Length; // Check if there are bytes left over from a previous transfer. if (queueContext->OverOfs.BufferLength > 0) { // Copy partial read bytes bytes into UserMem remainingLength = queueContext->Xfer.Length - queueContext->Xfer.Transferred; stageLength = (ULONG)queueContext->OverOfs.BufferLength > remainingLength ? remainingLength : (ULONG)queueContext->OverOfs.BufferLength; transferBuffer = &queueContext->OverBuf[queueContext->OverOfs.BufferOffset]; mXfer_CopyPartialReadToUserMemory(status, queueContext, transferBuffer, stageLength, goto Exit); USBDBGN("PipeID=%02Xh Transferred %u bytes from a previous partial read.", queueContext->Info.EndpointAddress, queueContext->Xfer.Transferred); if (queueContext->Xfer.Transferred >= queueContext->Xfer.Length) { if (requestContext->Policies.AutoFlush) { // discard any extra partial read bytes queueContext->OverOfs.BufferLength = 0; queueContext->OverOfs.BufferOffset = 0; } USBMSGN("PipeID=%02Xh DoneReason: Transferred==Requested. Transferred=%u", queueContext->Info.EndpointAddress, queueContext->Xfer.Transferred); status = STATUS_SUCCESS; goto Exit; } /* It would seem if IgnoreShortPackets=FALSE we would complete the request here. However, WinUSB does not handle it this way and will still submit the request. */ #if 0 else if (!requestContext->Policies.IgnoreShortPackets) { USBMSGN("PipeID=%02Xh DoneReason: IgnoreShortPackets=FALSE. Transferred=%u", queueContext->Info.EndpointAddress, queueContext->Xfer.Transferred); status = STATUS_SUCCESS; goto Exit; } #endif }
VOID PipeQueue_OnIoControl(__in WDFQUEUE Queue, __in WDFREQUEST Request, __in size_t OutputBufferLength, __in size_t InputBufferLength, __in ULONG IoControlCode) { NTSTATUS status; ULONG length = 0; PREQUEST_CONTEXT requestContext = GetRequestContext(Request); PQUEUE_CONTEXT queueContext = NULL; VALIDATE_REQUEST_CONTEXT(requestContext, status); if (!NT_SUCCESS(status)) goto Done; if ((queueContext = GetQueueContext(Queue)) == NULL) { status = STATUS_INVALID_DEVICE_REQUEST; USBERRN("Invalid queue context"); goto Done; } switch(IoControlCode) { case LIBUSB_IOCTL_ISOCHRONOUS_READ: case LIBUSB_IOCTL_ISOCHRONOUS_WRITE: case LIBUSB_IOCTL_INTERRUPT_OR_BULK_WRITE: case LIBUSB_IOCTL_INTERRUPT_OR_BULK_READ: switch (queueContext->Info.PipeType) { case WdfUsbPipeTypeIsochronous: if (USB_ENDPOINT_DIRECTION_IN(queueContext->Info.EndpointAddress)) { XferIsoRead(Queue, Request); return; } XferIsoWrite(Queue, Request); return; case WdfUsbPipeTypeBulk: case WdfUsbPipeTypeInterrupt: if(requestContext->Policies.RawIO) { if (USB_ENDPOINT_DIRECTION_IN(queueContext->Info.EndpointAddress)) Xfer_ReadBulkRaw(Queue, Request); else Xfer_WriteBulkRaw(Queue, Request); } else { if (USB_ENDPOINT_DIRECTION_IN(queueContext->Info.EndpointAddress)) Xfer_ReadBulk(Queue, Request); else Xfer_WriteBulk(Queue, Request); } return; default: status = STATUS_INVALID_PARAMETER; USBERRN("Invalid PipeType=%s\n", GetPipeTypeString(queueContext->Info.PipeType)); break; } case LIBUSBK_IOCTL_ISOEX_READ: case LIBUSBK_IOCTL_ISOEX_WRITE: if (queueContext->Info.PipeType == WdfUsbPipeTypeIsochronous) { XferIsoEx(Queue, Request); return; } status = STATUS_INVALID_PARAMETER; USBERRN("Invalid PipeType=%s\n", GetPipeTypeString(queueContext->Info.PipeType)); break; /* case LIBUSBK_IOCTL_AUTOISOEX_READ: case LIBUSBK_IOCTL_AUTOISOEX_WRITE: if (queueContext->Info.PipeType == WdfUsbPipeTypeIsochronous) { XferAutoIsoEx(Queue, Request); return; } status = STATUS_INVALID_PARAMETER; USBERRN("Invalid PipeType=%s\n", GetPipeTypeString(queueContext->Info.PipeType)); break; */ case LIBUSB_IOCTL_SET_FEATURE: case LIBUSB_IOCTL_CLEAR_FEATURE: case LIBUSB_IOCTL_GET_DESCRIPTOR: case LIBUSB_IOCTL_SET_DESCRIPTOR: case LIBUSB_IOCTL_VENDOR_WRITE: case LIBUSB_IOCTL_VENDOR_READ: case LIBUSB_IOCTL_CONTROL_READ: case LIBUSB_IOCTL_CONTROL_WRITE: XferCtrl(Queue, Request, InputBufferLength, OutputBufferLength); return; default: USBERR("unknown IoControlCode %Xh (function=%04Xh)\n", IoControlCode, FUNCTION_FROM_CTL_CODE(IoControlCode)); status = STATUS_INVALID_DEVICE_REQUEST; WdfRequestCompleteWithInformation(Request, status, 0); return; } Done: WdfRequestCompleteWithInformation(Request, status, length); return; }
VOID PipeQueue_OnWrite(__in WDFQUEUE Queue, __in WDFREQUEST Request, __in size_t InputBufferLength) { NTSTATUS status = STATUS_SUCCESS; PDEVICE_CONTEXT deviceContext; PREQUEST_CONTEXT requestContext; PQUEUE_CONTEXT queueContext = NULL; UNREFERENCED_PARAMETER(InputBufferLength); deviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue)); requestContext = GetRequestContext(Request); VALIDATE_REQUEST_CONTEXT(requestContext, status); if (!NT_SUCCESS(status)) goto Done; if ((queueContext = GetQueueContext(Queue)) == NULL) { status = STATUS_INVALID_DEVICE_REQUEST; USBERRN("Invalid queue context"); goto Done; } if (!queueContext->PipeHandle) { USBERR("null pipe handle\n"); status = STATUS_INVALID_HANDLE; goto Done; } switch(queueContext->Info.PipeType) { case WdfUsbPipeTypeIsochronous: if (USB_ENDPOINT_DIRECTION_OUT(queueContext->Info.EndpointAddress)) { XferIsoWrite(Queue, Request); return; } break; case WdfUsbPipeTypeBulk: case WdfUsbPipeTypeInterrupt: if (USB_ENDPOINT_DIRECTION_IN(queueContext->Info.EndpointAddress)) { status = STATUS_INVALID_DEVICE_REQUEST; USBERRN("Cannot write to an IN pipe."); goto Done; } if(requestContext->Policies.RawIO) Xfer_WriteBulkRaw(Queue, Request); else Xfer_WriteBulk(Queue, Request); } status = STATUS_INVALID_DEVICE_REQUEST; USBERRN("PipeID=%02Xh Invalid request", queueContext->Info.EndpointAddress); Done: WdfRequestCompleteWithInformation(Request, status, 0); }