An issue about the bus drivr on a HID device

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:

  1. 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?

  2. 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

xxxxx@hotmail.com wrote:

  1. 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?

What did you expect WDF to do with it? It doesn’t know anything about
your device. If you don’t set a callback, the request is rejected with
STATUS_NOT_IMPLEMENTED. If you want the request to be passed to the
next driver stack, then you have to DO that.

  1. 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.

That makes no difference. The issue is that the hidclass driver
requires a FILE_OBJECT in the IRP, and you aren’t supplying one.

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.

I’m fairly confused by your architecture. Your driver is an FDO, but an
FDO for what device? The driver assumes that there is genuine USB HID
hardware below it. Is that true? What hardware ID have you claimed in
your INF file?

You are creating your PDO (at least, I assume that’s what PlugInDevice
does) in response to a device interface arrival. What device interface
are you waiting for? If this driver sits on top of a real HID device,
then you don’t have to wait for the device interface arrival. Your
driver won’t be loaded until the device is ready. If you are watching
for some other HID device, then what’s the point? You don’t have a
connection to that other device. You can’t send requests to it.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.