Hi All,
I’m writing a function driver for one of our own HID device. The driver acts as a dynamic bus to enumerate Xbox 360 controller. I can install the driver and the controller can be simulated successfully. However, when I tried to read from the HID device, I ran into two issues:
-
The user mode application can’t read anything from the HID device. Without installing this driver, the application can read the data from the device. The driver doesn’t set the callback for the IoRead so I assume the WDF would handle that automatically. Do you know why this happens?
-
When I issued a Read request to the local IO target from the HID notification routine to the HID device, the request completion routine calls the WdfRequestGetStatus returns STATUS_PRIVILEGE_NOT_HELD. I searched one article about this issue http://www.osronline.com/showThread.CFM?link=193441, but I need to issue driver owned request instead of filter Read request.
The following is my code. I setup the dynamic enumerated bus in the HID device add routine and registers a HID PnP notification routine in the event EvtDeviceSelfManagedIoInit. The notification routine actually determines the HID device and plugin a Xbox 360 controller and issues the Read request.
NTSTATUS
Bthxbus_EvtDeviceAdd(
IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit
)
{
NTSTATUS status = STATUS_SUCCESS;
PBTHXBUS_DEVICE_CONTEXT pDeviceContext = NULL;
PBTHXBUS_DRIVER_CONTEXT pDriverContext = NULL;
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks = {0};
WDF_OBJECT_ATTRIBUTES attributes = {0};
WDF_CHILD_LIST_CONFIG config = {0};
WDFDEVICE hDevice = NULL;
PNP_BUS_INFORMATION pnpBusInfo = {0};
WDF_IO_QUEUE_CONFIG queueConfig = {0};
WDFQUEUE hQueue = NULL;
UNREFERENCED_PARAMETER(Driver);
PAGED_CODE();
BthxTraceFuncEntry();
WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER);
WdfDeviceInitSetExclusive(DeviceInit, FALSE);
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
pnpPowerCallbacks.EvtDeviceSelfManagedIoInit = Bthxbus_EvtDeviceSelfManagedIoInit;
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
WDF_CHILD_LIST_CONFIG_INIT(
&config,
sizeof(BTHXPDO_ID_DESC),
Bthxpdo_EvtDeviceListCreate
);
config.EvtChildListIdentificationDescriptionDuplicate = Bthxpdo_EvtChildListIdentificationDescriptionDuplicate;
config.EvtChildListIdentificationDescriptionCompare = Bthxpdo_EvtChildListIdentificationDescriptionCompare;
config.EvtChildListIdentificationDescriptionCleanup = Bthxpdo_EvtChildListIdentificationDescriptionCleanup;
WdfFdoInitSetDefaultChildListConfig(DeviceInit, &config, WDF_NO_OBJECT_ATTRIBUTES);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BTHXBUS_DEVICE_CONTEXT);
attributes.EvtCleanupCallback = Bthxbus_EvtDeviceContextCleanup;
status = WdfDeviceCreate(&DeviceInit, &attributes, &hDevice);
IfFailGoToExit(status, BTHX_BUS, “WdfDeviceCreate failed”);
pDriverContext = Bthxbus_GetDriverContext(Driver);
pDeviceContext = Bthxbus_GetDeviceContext(hDevice);
pDeviceContext->ulBusInstanceId = pDriverContext->ulBusInstanceNumber;
//
// Create two collections to store IO targets and XUSB PDOs.
//
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = hDevice;
status = WdfCollectionCreate(&attributes, &pDeviceContext->hColPdos);
IfFailGoToExit(status, BTHX_BUS, “WdfCollectionCreate for the Pdos of XUSB devices failed”);
//
// Configure a default queue so that requests that are not
// configure-fowarded using WdfDeviceConfigureRequestDispatching to goto
// other queues get dispatched here.
//
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
&queueConfig,
WdfIoQueueDispatchParallel
);
queueConfig.EvtIoDeviceControl = Bthxbus_EvtIoDeviceControl;
status = WdfIoQueueCreate(
hDevice,
&queueConfig,
&attributes,
&hQueue
);
IfFailGoToExit(status, BTHX_BUS, “WdfIoQueueCreate failed”);
//
// Create device interface for this device. The interface will be
// enabled by the framework when we return from StartDevice successfully.
// Clients of this driver will open this interface and send ioctls.
//
status = WdfDeviceCreateDeviceInterface(
hDevice,
&GUID_DEVINTERFACE_BTHXBUS,
NULL
);
IfFailGoToExit(status, BTHX_BUS, “WdfDeviceCreateDeviceInterface failed”);
//
// This value is used in responding to the IRP_MN_QUERY_BUS_INFORMATION
// for the child devices. This is an optional information provided to
// uniquely idenitfy the bus the device is connected.
//
pnpBusInfo.BusTypeGuid = GUID_BUS_TYPE_USB;
pnpBusInfo.LegacyBusType = PNPBus;
pnpBusInfo.BusNumber = 0;
WdfDeviceSetBusInformationForChildren(hDevice, &pnpBusInfo);
Exit:
BthxTraceFuncExit();
return status;
}
NTSTATUS
Bthxbus_PnpNotifyHidInterfaceChange(
IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationStruct,
IN PVOID Context
)
{
NTSTATUS status = STATUS_SUCCESS;
WDFDEVICE hDevice = (WDFDEVICE)Context;
PBTHXBUS_DEVICE_CONTEXT pDeviceContext = NULL;
PBTHXBUS_DRIVER_CONTEXT pDriverContext = NULL;
WDFIOTARGET hIoTarget = NULL;
BthxTraceFuncEntry();
pDriverContext = Bthxbus_GetDriverContext(WdfDeviceGetDriver(hDevice));
pDeviceContext = Bthxbus_GetDeviceContext(hDevice);
if (IsEqualGUID(&NotificationStruct->Event, &GUID_DEVICE_INTERFACE_ARRIVAL))
{
BthxTrace(
TRACE_LEVEL_VERBOSE,
BTHX_BUS,
“[0x%p]: %!USTR! arrives”,
hDevice,
NotificationStruct->SymbolicLinkName
);
if (Bthxbus_IsDeviceMatched(hDevice, NotificationStruct->SymbolicLinkName) == TRUE)
{
hIoTarget = WdfDeviceGetIoTarget(hDevice);
status = Bthxbus_PlugInDevice(hDevice, BTHXUSB_HARDWARE_IDS, BTHXUSB_HARDWARE_IDS_LENGTH, pDeviceContext->ulBusInstanceId);
IfFailGoToExit(status, BTHX_BUS, L"Bthxbus_PlugInDevice failed");
++pDriverContext->ulBusInstanceNumber;
status = Bthxctrl_Initialize(hDevice);
IfFailGoToExit(status, BTHX_BUS, L"Bthxctrl_Initialize failed");
status = Bthxctrl_ReadReportAsync(hDevice);
IfFailGoToExit(status, BTHX_BUS, L"Bthxctrl_ReadReportAsync failed");
}
}
Exit:
BthxTraceFuncExit();
return status;
}
NTSTATUS
Bthxctrl_ReadReportAsync(
WDFDEVICE hDevice
)
{
NTSTATUS status = STATUS_SUCCESS;
BOOLEAN bRetVal = FALSE;
WDF_REQUEST_REUSE_PARAMS reuseParams = {0};
WDFIOTARGET hIoTarget = WdfDeviceGetIoTarget(hDevice);
PBTHXCTRL_CONTEXT pContext = NULL;
USHORT usReadReportSize = 0;
BthxTraceFuncEntry();
pContext = &(Bthxbus_GetDeviceContext(hDevice)->ctrlCxt);
usReadReportSize = pContext->hidpCaps.InputReportByteLength;
WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS);
status = WdfRequestReuse(pContext->hReadRequest, &reuseParams);
IfFailGoToExit(status, BTHX_CTRL, “WdfRequestReuse failed”);
status = WdfIoTargetFormatRequestForRead(
hIoTarget,
pContext->hReadRequest,
pContext->hMemReadRequest,
NULL,
NULL
);
IfFailGoToExit(status, BTHX_CTRL, “WdfIoTargetFormatRequestForRead failed”);
WdfRequestSetCompletionRoutine(
pContext->hReadRequest,
pContext->pfnReadCompletionRoutine,
pContext
);
bRetVal = WdfRequestSend(
pContext->hReadRequest,
hIoTarget,
WDF_NO_SEND_OPTIONS
);
IfFalseGoToExit(
bRetVal,
WdfRequestGetStatus(pContext->hReadRequest),
BTHX_CTRL,
L"WdfRequestSend failed"
);
Exit:
BthxTraceFuncExit();
return status;
}
Thanks,
Marshall