VOID OnIoDeviceControl( _In_ WDFQUEUE FxQueue, _In_ WDFREQUEST FxRequest, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength, _In_ ULONG IoControlCode ) /*++ Routine Description: This event is called when the framework receives IRP_MJ_DEVICE_CONTROL requests from the system. Arguments: FxQueue - Handle to the framework queue object that is associated with the I/O request. FxRequest - Handle to a framework request object. OutputBufferLength - length of the request's output buffer, if an output buffer is available. InputBufferLength - length of the request's input buffer, if an input buffer is available. IoControlCode - the driver-defined or system-defined I/O control code (IOCTL) that is associated with the request. Return Value: VOID --*/ { FuncEntry(TRACE_FLAG_SPBAPI); WDFDEVICE device; PDEVICE_CONTEXT pDevice; BOOLEAN fSync = FALSE; NTSTATUS status = STATUS_SUCCESS; UNREFERENCED_PARAMETER(OutputBufferLength); UNREFERENCED_PARAMETER(InputBufferLength); device = WdfIoQueueGetDevice(FxQueue); pDevice = GetDeviceContext(device); CyapaPrint( DEBUG_LEVEL_INFO, DBG_IOCTL, "DeviceIoControl request %p received with IOCTL=%lu", FxRequest, IoControlCode); CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, "%s, Queue:0x%p, Request:0x%p\n", DbgHidInternalIoctlString(IoControlCode), FxQueue, FxRequest ); // // Translate the test IOCTL into the appropriate // SPB API method. Open and close are completed // synchronously. // switch (IoControlCode) { case IOCTL_HID_GET_DEVICE_DESCRIPTOR: // // Retrieves the device's HID descriptor. // status = CyapaGetHidDescriptor(device, FxRequest); fSync = TRUE; break; case IOCTL_HID_GET_DEVICE_ATTRIBUTES: // //Retrieves a device's attributes in a HID_DEVICE_ATTRIBUTES structure. // status = CyapaGetDeviceAttributes(FxRequest); fSync = TRUE; break; case IOCTL_HID_GET_REPORT_DESCRIPTOR: // //Obtains the report descriptor for the HID device. // status = CyapaGetReportDescriptor(device, FxRequest); fSync = TRUE; break; case IOCTL_HID_GET_STRING: // // Requests that the HID minidriver retrieve a human-readable string // for either the manufacturer ID, the product ID, or the serial number // from the string descriptor of the device. The minidriver must send // a Get String Descriptor request to the device, in order to retrieve // the string descriptor, then it must extract the string at the // appropriate index from the string descriptor and return it in the // output buffer indicated by the IRP. Before sending the Get String // Descriptor request, the minidriver must retrieve the appropriate // index for the manufacturer ID, the product ID or the serial number // from the device extension of a top level collection associated with // the device. // status = CyapaGetString(FxRequest); fSync = TRUE; break; case IOCTL_HID_WRITE_REPORT: case IOCTL_HID_SET_OUTPUT_REPORT: // //Transmits a class driver-supplied report to the device. // status = CyapaWriteReport(pDevice, FxRequest); fSync = TRUE; break; case IOCTL_HID_READ_REPORT: case IOCTL_HID_GET_INPUT_REPORT: // // Returns a report from the device into a class driver-supplied buffer. // status = CyapaReadReport(pDevice, FxRequest, &fSync); break; case IOCTL_HID_GET_FEATURE: // // returns a feature report associated with a top-level collection // status = CyapaGetFeature(pDevice, FxRequest, &fSync); break; case IOCTL_HID_ACTIVATE_DEVICE: // // Makes the device ready for I/O operations. // case IOCTL_HID_DEACTIVATE_DEVICE: // // Causes the device to cease operations and terminate all outstanding // I/O requests. // default: fSync = TRUE; status = STATUS_NOT_SUPPORTED; CyapaPrint( DEBUG_LEVEL_INFO, DBG_IOCTL, "Request %p received with unexpected IOCTL=%lu", FxRequest, IoControlCode); } // // Complete the request if necessary. // if (fSync) { CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, "%s completed, Queue:0x%p, Request:0x%p\n", DbgHidInternalIoctlString(IoControlCode), FxQueue, FxRequest ); WdfRequestComplete(FxRequest, status); } else { CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, "%s deferred, Queue:0x%p, Request:0x%p\n", DbgHidInternalIoctlString(IoControlCode), FxQueue, FxRequest ); } FuncExit(TRACE_FLAG_SPBAPI); }
NTSTATUS HidUmdfInternalIoctlWorker( _In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp ) /*++ Routine Description: IOCTL handler. Arguments: DeviceObject - pointer to a device object. Irp - pointer to an I/O Request Packet. Return Value: NT status code --*/ { PIO_STACK_LOCATION currStack, nextStack; ULONG ioctlCode, newIoctlCode; BOOLEAN setCompletionRoutine = FALSE; NTSTATUS status = STATUS_SUCCESS; BOOLEAN modeChanged = FALSE; PULONG temp = NULL; currStack = IoGetCurrentIrpStackLocation(Irp); // // Copy current stack to next instead of skipping. We do this to preserve // current stack information provided by hidclass driver to the minidriver // IoCopyCurrentIrpStackLocationToNext(Irp); // // HIDclass uses INTERNAL_IOCTL but since UMDF doesn't yet have support for // internal IOCTLS we change the IOCTL type to DEVICE_CONTROL for next stack // so that UMDF stack can handle it as normal IOCTL. // Note that user mode apps cannot open a handle to minidriver since // HIDClass doesn't allow that (it own's minidriver's dispatch table), // and therefore they can't send these IOCTLs to UMDF minidriver by calling // win32 API. // nextStack = IoGetNextIrpStackLocation(Irp); nextStack->MajorFunction = IRP_MJ_DEVICE_CONTROL; // // Some IOCTLs are not of type METHOD_NEITHER and are forwarded by // HIDClass to minidriver. We modify those IOCTLs to use METHOD_NEITHER // and send it down to UMDF driver. // ioctlCode = nextStack->Parameters.DeviceIoControl.IoControlCode; newIoctlCode = ioctlCode; switch(ioctlCode) { case IOCTL_HID_GET_DEVICE_DESCRIPTOR: // METHOD_NEITHER, KM case IOCTL_HID_GET_REPORT_DESCRIPTOR: // METHOD_NEITHER, KM case IOCTL_HID_READ_REPORT: // METHOD_NEITHER, KM case IOCTL_HID_ACTIVATE_DEVICE: // METHOD_NEITHER, KM case IOCTL_HID_DEACTIVATE_DEVICE: // METHOD_NEITHER, KM case IOCTL_HID_GET_DEVICE_ATTRIBUTES: // METHOD_NEITHER, KM case IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST: // METHOD_NEITHER, KM // // Nothing to do. These IOCTLs have been listed for completeness. // break; case IOCTL_HID_WRITE_REPORT: // METHOD_NEITHER, KM case IOCTL_HID_SET_FEATURE: // METHOD_IN_DIRECT, KM/UM case IOCTL_HID_GET_FEATURE: // METHOD_OUT_DIRECT, KM/UM case IOCTL_HID_GET_INPUT_REPORT: // METHOD_OUT_DIRECT, KM/UM case IOCTL_HID_SET_OUTPUT_REPORT: // METHOD_IN_DIRECT, KM/UM // // These IOCTLs use HID_XFER_PACKET. They need their buffer location // updated. See comments in function for IOCTL specific updates. // status = UpdateBufferLocationAndIoctl(Irp, &newIoctlCode); if (!NT_SUCCESS(status)) { KdPrint(("HidUmdf: Ioctl %s failed status 0x%x\n", DbgHidInternalIoctlString(ioctlCode), status)); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } // // set completion routine // setCompletionRoutine = TRUE; break; case IOCTL_GET_PHYSICAL_DESCRIPTOR: // METHOD_OUT_DIRECT, KM/UM // // These IOCTLs are not METHOD_NEITHER but hidclass places buffers at // locations that are standard locations for METHOD_NEITHER // (Type3InputBuffer and Irp->UserBuffer), so we modify the IOCTL type // to use METHOD_NEITHER so that UMDF can provide the buffers from // standard METHOD_NEITHER buffer locations. // newIoctlCode = IOCTL_UMDF_GET_PHYSICAL_DESCRIPTOR; break; case IOCTL_HID_GET_STRING: // METHOD_NEITHER, KM // // This is a METHOD_NEITHER IOCTL. Hidclass places an input // ULONG value, and not a buffer, at Type3inputBuffer location. // We store the input value in Irp->AssocatedIrp.SystemBuffer location // and store pointer to Irp->AssocatedIrp.SystemBuffer at // Type3InputBuffer so that lower driver can access it as input buffer. // // // swap current SystemBuffer content with Type3inputBuffer // temp = Irp->AssociatedIrp.SystemBuffer; Irp->AssociatedIrp.SystemBuffer = currStack->Parameters.DeviceIoControl.Type3InputBuffer; currStack->Parameters.DeviceIoControl.Type3InputBuffer = temp; // // store the address of SystemBuffer in next stack's Type3InputBuffer // and set buffer size // nextStack->Parameters.DeviceIoControl.Type3InputBuffer = &Irp->AssociatedIrp.SystemBuffer; nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof(ULONG); setCompletionRoutine = TRUE; break; case IOCTL_HID_GET_INDEXED_STRING: // METHOD_OUT_DIRECT, KM/UM // // This is a METHOD_OUT_DIRECT IOCTL however hidclass places buffer/value // in a mix of locations (Type3InputBuffer for input instead of // Irp->AssociatedIrp.SystemBuffer and Irp->MdlAddress for output). // Also, the input is not a buffer but a ULONG value. // // We store the address of next stack's Type3InputBuffer that contains // the input value at Irp->AssiciatedIrp.SystemBuffer // (standard location for METHOD_OUT_DIRECT), and keep the output buffer // location (Irp->UserBuffer) unchanged since it's already at standard // location. The input buffer location is reverted back to original in // completion routine. // // // store SystemBuffer in curr stack's Type3inputBuffer so we // can get it back in completion routine. // currStack->Parameters.DeviceIoControl.Type3InputBuffer = Irp->AssociatedIrp.SystemBuffer; // // store the address of next stack's Type3InputBuffer in SystemBuffer // and set buffer size. // Irp->AssociatedIrp.SystemBuffer = &nextStack->Parameters.DeviceIoControl.Type3InputBuffer; nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof(ULONG); setCompletionRoutine = TRUE; break; default: NT_ASSERTMSG("Unexpected IOCTL", FALSE); break; } // // update ioctl code for next stack location // nextStack->Parameters.DeviceIoControl.IoControlCode = newIoctlCode; if (Irp->RequestorMode == UserMode) { Irp->RequestorMode = KernelMode; setCompletionRoutine = TRUE; modeChanged = TRUE; } if (setCompletionRoutine) { IoSetCompletionRoutine(Irp, UserIoctlCompletion, (modeChanged ? (PVOID)Irp : NULL), // context TRUE, TRUE, TRUE ); } return IoCallDriver(GET_NEXT_DEVICE_OBJECT(DeviceObject), Irp); }