/* * usbshare - intercepting the class driver while the device is claimed * * This is what makes the filter approach worth the trouble. While no client * holds the device, every request is forwarded untouched and the device * behaves exactly as if this driver were not installed. Only once a client * claims it do the class driver's requests get swallowed, so the two do not * fight over the same endpoints. */ #include "usbshare.h" /* * Forwards a request to the driver below unchanged. * * Send-and-forget is right here: we have no interest in the answer, and not * setting a completion routine avoids holding a reference on a request that * may outlive our interest in it. */ static VOID UsbShareForward( _In_ WDFDEVICE Device, _In_ WDFREQUEST Request ) { WDF_REQUEST_SEND_OPTIONS options; WDF_REQUEST_SEND_OPTIONS_INIT(&options, WDF_REQUEST_SEND_OPTION_SEND_AND_FORGET); WdfRequestFormatRequestUsingCurrentType(Request); if (!WdfRequestSend(Request, WdfDeviceGetIoTarget(Device), &options)) { WdfRequestComplete(Request, WdfRequestGetStatus(Request)); } } VOID UsbShareEvtIoDefault( _In_ WDFQUEUE Queue, _In_ WDFREQUEST Request ) { WDFDEVICE device = WdfIoQueueGetDevice(Queue); /* * Reads and writes are not intercepted even while claimed. They come from * user mode against the class driver's own interface, and failing them * would surface as application errors rather than a device that is simply * busy elsewhere. */ UsbShareForward(device, Request); } VOID UsbShareEvtIoInternalDeviceControl( _In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength, _In_ ULONG IoControlCode ) { WDFDEVICE device = WdfIoQueueGetDevice(Queue); PDEVICE_CONTEXT context = GetDeviceContext(device); UNREFERENCED_PARAMETER(OutputBufferLength); UNREFERENCED_PARAMETER(InputBufferLength); /* * IOCTL_INTERNAL_USB_SUBMIT_URB is how the class driver above us talks to * the USB stack. Letting those through while a client holds the device * would mean two parties submitting to the same endpoints: transfers * would be answered to whoever asked last, and a keyboard would appear to * type on both machines at once. */ if (IoControlCode == IOCTL_INTERNAL_USB_SUBMIT_URB && UsbShareIsClaimed(context)) { /* * STATUS_DEVICE_NOT_CONNECTED rather than STATUS_DEVICE_BUSY: class * drivers treat "busy" as a reason to retry in a tight loop, whereas * "not connected" makes them stand down until PnP says otherwise — * which is exactly the state the device is in from their point of view. */ WdfRequestComplete(Request, STATUS_DEVICE_NOT_CONNECTED); return; } /* * Everything else — PnP queries, port status, idle notifications — is * forwarded even while claimed. Blocking those would confuse the stack * about the device's existence, and it does still exist. */ UsbShareForward(device, Request); }