Custom HID Device Driver - Reporting Mouse Data to System But No Pointer Movement (KM

Hey all,

Newbie here, I am writing a driver for a device that I want to use as a HID mouse/pointer and I am having some weird issues with reporting the mouse data to the system. I started from the HidUsbFx2 sample driver and altered the code as necessary (ie. changed report descriptor and implemented other customized code). My driver receives data from the device successfully but does not send the mouse input report to the system correctly. From my debug print statements I can see that the driver is receiving accurate relative x,y data from the device. I also see that the driver is sending data because the system continues to send read requests (after each time the driver sends new mouse data) but the pointer never moves. My report descriptor looks alright to me and my mouse input report structure matches the descriptor. I can’t figure out what the problem is. Does anyone have any thoughts on what could be causing this behavior?

I also want the device to function as a fake HID keyboard so that I can send keyboard shortcuts to the system or active application (eg. rotate, zoom, etc.). Am I correct in thinking that I can accomplish this by including a keyboard TLC in my report descriptor, creating a keyboard input structure and reporting the key scan codes to the system under the report ID that I specified in my keyboard TLC?

Here’s my mouse report descriptor, input structure and report function:

REPORT DESCRIPTOR:

// Mouse Collection
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x85, REPORTID_MOUSE, // REPORT_ID (Mouse) (REPORTID_MOUSE = 0x01)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x02, // USAGE_MAXIMUM (Button 2)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x75, 0x10, // REPORT_SIZE (16)
0x95, 0x02, // REPORT_COUNT (2)
0x16, 0x01, 0x80, // LOGICAL_MINIMUM (-32767)
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767)
0x81, 0x02, // INPUT (Data,Var,Abs)

0xc0, // END_COLLECTION
0xc0, // END_COLLECTION

MOUSE DATA INPUT STRUCTURE:

typedef struct _HID_MOUSE_REPORT
{
UCHAR reportID;
UCHAR buttons;
SHORT xData;
SHORT yData;
}HID_MOUSE_REPORT, *PHID_MOUSE_REPORT;

FUNCTION THAT REPORTS MOUSE DATA:

VOID MouseReport(PDEVICE_EXTENSION devContext,GESTURE_DATA gestureData)
{
PHID_MOUSE_REPORT report;
NTSTATUS status;
WDFREQUEST request;

DbgPrint(“MouseReport”);

status = WdfIoQueueRetrieveNextRequest(devContext->InterruptMsgQueue, &request);

if(!NT_SUCCESS(status)){
DbgPrint(“WdfIoQueueRetrieveNextRequest failed with status: 0x%x\n”,status);
}else if (NT_SUCCESS(status)){
//
// IOCTL_HID_READ_REPORT is METHOD_NEITHER so WdfRequestRetrieveOutputBuffer
// will correctly retrieve buffer from Irp->UserBuffer. Remember that
// HIDCLASS provides the buffer in the Irp->UserBuffer field
// irrespective of the ioctl buffer type. However, framework is very
// strict about type checking. You cannot get Irp->UserBuffer by using
// WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER
// internal ioctl.
//

status = WdfRequestRetrieveOutputBuffer(request,
sizeof(HID_MOUSE_REPORT),
&report,
NULL); //bufferLength

if (!NT_SUCCESS(status)) { // should never happen
TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL,

“WdfRequestRetrieveOutputBuffer failed with status: 0x%x\n”, status);
DbgPrint((“Retrieve error\n”));
WdfRequestComplete(request, status);
}else{
// KdPrint(("mem size %d ", sizeof(HID_MOUSE_REPORT)));
// memset(report, 0, sizeof(HID_MOUSE_REPORT));
report->reportID = REPORTID_MOUSE;
report->buttons = 0x00;
report->xData = gestureData.tMotionData2D.lX;
report->yData = (-1)*gestureData.tMotionData2D.lY; // Device reports negated Y data for some reason

DbgPrint(" rpID: %d, x: %d, y: %d, buttons: %d\n",report->reportID,gestureData.tMotionData2D.lX,
(-1)*gestureData.tMotionData2D.lY,report->buttons);//report->ReportID,report->wXData,report->wYData);

WdfRequestComplete(request, status);
}
}
}

When I receive an “IOCTL_HID_READ_REPORT” request in my InternalDeviceControl function I forward the request to the appropriate queue with the following line of code (followed by the appropriate code to check for a failed forwarding attempt):

status = WdfRequestForwardToIoQueue(Request, devContext->InterruptMsgQueue);

Then, I call the MouseReport function from my EvtUsbInPipeReadComplete function (this is a function that is part of HidUsbFx2 sample code that I’ve made additions to in order to isolate the event in which I need to report the mouse data but the main functionality of this function is the same as the sample code).

Looking at your HID descriptor, you have Report Count(16) and report Size(2). If you want 16-bit just make it Report Size (16) and report Count(1). Right now you’re telling it to look for two 2-byte fields instead of 1 16-bit field. The Report count is per usage, not total. Hope that helps!

–Will Jones


From: xxxxx@lists.osr.com [xxxxx@lists.osr.com] on behalf of xxxxx@gmail.com [xxxxx@gmail.com]
Sent: Monday, July 15, 2013 11:26 AM
To: Windows System Software Devs Interest List
Subject: [ntdev] Custom HID Device Driver - Reporting Mouse Data to System But No Pointer Movement (KM

Hey all,

Newbie here, I am writing a driver for a device that I want to use as a HID mouse/pointer and I am having some weird issues with reporting the mouse data to the system. I started from the HidUsbFx2 sample driver and altered the code as necessary (ie. changed report descriptor and implemented other customized code). My driver receives data from the device successfully but does not send the mouse input report to the system correctly. From my debug print statements I can see that the driver is receiving accurate relative x,y data from the device. I also see that the driver is sending data because the system continues to send read requests (after each time the driver sends new mouse data) but the pointer never moves. My report descriptor looks alright to me and my mouse input report structure matches the descriptor. I can’t figure out what the problem is. Does anyone have any thoughts on what could be causing this behavior?

I also want the device to function as a fake HID keyboard so that I can send keyboard shortcuts to the system or active application (eg. rotate, zoom, etc.). Am I correct in thinking that I can accomplish this by including a keyboard TLC in my report descriptor, creating a keyboard input structure and reporting the key scan codes to the system under the report ID that I specified in my keyboard TLC?

Here’s my mouse report descriptor, input structure and report function:

REPORT DESCRIPTOR:

// Mouse Collection
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x85, REPORTID_MOUSE, // REPORT_ID (Mouse) (REPORTID_MOUSE = 0x01)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x02, // USAGE_MAXIMUM (Button 2)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x75, 0x10, // REPORT_SIZE (16)
0x95, 0x02, // REPORT_COUNT (2)
0x16, 0x01, 0x80, // LOGICAL_MINIMUM (-32767)
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767)
0x81, 0x02, // INPUT (Data,Var,Abs)

0xc0, // END_COLLECTION
0xc0, // END_COLLECTION

MOUSE DATA INPUT STRUCTURE:

typedef struct _HID_MOUSE_REPORT
{
UCHAR reportID;
UCHAR buttons;
SHORT xData;
SHORT yData;
}HID_MOUSE_REPORT, *PHID_MOUSE_REPORT;

FUNCTION THAT REPORTS MOUSE DATA:

VOID MouseReport(PDEVICE_EXTENSION devContext,GESTURE_DATA gestureData)
{
PHID_MOUSE_REPORT report;
NTSTATUS status;
WDFREQUEST request;

DbgPrint(“MouseReport”);

status = WdfIoQueueRetrieveNextRequest(devContext->InterruptMsgQueue, &request);

if(!NT_SUCCESS(status)){
DbgPrint(“WdfIoQueueRetrieveNextRequest failed with status: 0x%x\n”,status);
}else if (NT_SUCCESS(status)){
//
// IOCTL_HID_READ_REPORT is METHOD_NEITHER so WdfRequestRetrieveOutputBuffer
// will correctly retrieve buffer from Irp->UserBuffer. Remember that
// HIDCLASS provides the buffer in the Irp->UserBuffer field
// irrespective of the ioctl buffer type. However, framework is very
// strict about type checking. You cannot get Irp->UserBuffer by using
// WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER
// internal ioctl.
//

status = WdfRequestRetrieveOutputBuffer(request,
sizeof(HID_MOUSE_REPORT),
&report,
NULL); //bufferLength

if (!NT_SUCCESS(status)) { // should never happen
TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL,

“WdfRequestRetrieveOutputBuffer failed with status: 0x%x\n”, status);
DbgPrint((“Retrieve error\n”));
WdfRequestComplete(request, status);
}else{
// KdPrint(("mem size %d ", sizeof(HID_MOUSE_REPORT)));
// memset(report, 0, sizeof(HID_MOUSE_REPORT));
report->reportID = REPORTID_MOUSE;
report->buttons = 0x00;
report->xData = gestureData.tMotionData2D.lX;
report->yData = (-1)*gestureData.tMotionData2D.lY; // Device reports negated Y data for some reason

DbgPrint(" rpID: %d, x: %d, y: %d, buttons: %d\n",report->reportID,gestureData.tMotionData2D.lX,
(-1)*gestureData.tMotionData2D.lY,report->buttons);//report->ReportID,report->wXData,report->wYData);

WdfRequestComplete(request, status);
}
}
}

When I receive an “IOCTL_HID_READ_REPORT” request in my InternalDeviceControl function I forward the request to the appropriate queue with the following line of code (followed by the appropriate code to check for a failed forwarding attempt):

status = WdfRequestForwardToIoQueue(Request, devContext->InterruptMsgQueue);

Then, I call the MouseReport function from my EvtUsbInPipeReadComplete function (this is a function that is part of HidUsbFx2 sample code that I’ve made additions to in order to isolate the event in which I need to report the mouse data but the main functionality of this function is the same as the sample code).


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

William T. Jones wrote:

Looking at your HID descriptor, you have Report Count(16) and report Size(2). If you want 16-bit just make it Report Size (16) and report Count(1). Right now you’re telling it to look for two 2-byte fields instead of 1 16-bit field. The Report count is per usage, not total.

Did you look his structure? He has two SHORTs in there, so two 16-bit
fields is what he wants.


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

xxxxx@gmail.com wrote:

I can’t figure out what the problem is. Does anyone have any thoughts on what could be causing this behavior?

Now that you have included your driver code, yes, I see it…

report->yData = (-1)*gestureData.tMotionData2D.lY; // Device reports negated Y data for some reason

Not related to your problem, but that’s traditionally written just
-gestureData.tMotionData2D.lY.

DbgPrint(" rpID: %d, x: %d, y: %d, buttons: %d\n",report->reportID,gestureData.tMotionData2D.lX,
(-1)*gestureData.tMotionData2D.lY,report->buttons);//report->ReportID,report->wXData,report->wYData);

WdfRequestComplete(request, status);

THAT’S the problem. You are completing the request, but you are not
telling it to return any data. So, the caller is going to see that you
returned 0 bytes. You want this:
WdfRequestCompleteWithInformation( request, status,
sizeof(HID_MOUSE_REPORT) );


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

That did it! Wow, I can’t believe I didn’t catch that/try that before now. Thank you so much, Tim, you just made my day.

Follow up question.
Would I be able to use a function similar to this to report key strokes if I do the following:

  1. Include a keyboard TLC in my report descriptor
  2. Use a different report ID
  3. Change the input report structure from a mouse input report to a keyboard input report
    Or is there more that goes into reporting keyboard data?