I am an intern and new to driver development. My company would like me to write a KMDF driver for a couple multi-touch HID devices that they make. They hired someone before me to work on it who was trying to use the Windows touch usages and ran into some issues with that and was therefore unable to complete the driver. So, we decided that I should write the driver so that the device acts like a mouse & keyboard composite device. That way we can send the system mouse data when a single touch is used and send keyboard data (keystrokes that activate keyboard shortcuts) when certain multi-touch gestures are recognized (rotate, zoom etc). I have the gesture recognition down and the mouse part is working great. I started from the HidUsbFx2 sample in the WDK and I am currently am facing two problems.
PROBLEM #1: The mouse will only work if I comment out my keyboard TLC in my report descriptor and my keyboard report function will only work (up to the point I have it completed) if I comment out the mouse TLC in my report descriptor. I have no idea why this is happening. Here is my report descriptor: `// Mouse Collection
0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0
0x09, 0x02, // USAGE (Mouse) 2
0xa1, 0x01, // COLLECTION (Application) 4
0x85, REPORTID_MOUSE, // REPORT_ID (Mouse) 6
0x09, 0x01, // USAGE (Pointer) 8
0xa1, 0x00, // COLLECTION (Physical) 10
0x05, 0x09, // USAGE_PAGE (Button) 12
0x19, 0x01, // USAGE_MINIMUM (Button 1) 14
0x29, 0x02, // USAGE_MAXIMUM (Button 2) 16
0x15, 0x00, // LOGICAL_MINIMUM (0) 18
0x25, 0x01, // LOGICAL_MAXIMUM (1) 20
0x75, 0x01, // REPORT_SIZE (1) 22
0x95, 0x02, // REPORT_COUNT (2) 24
0x81, 0x02, // INPUT (Data,Var,Abs) 26
0x95, 0x06, // REPORT_COUNT (6) 28
0x81, 0x03, // INPUT (Cnst,Var,Abs) 30
0x05, 0x01, // USAGE_PAGE (Generic Desktop) 32
0x09, 0x30, // USAGE (X) 34
0x09, 0x31, // USAGE (Y) 36
0x75, 0x10, // REPORT_SIZE (16) 38
0x95, 0x02, // REPORT_COUNT (2) 40
0x16, 0x01, 0x80, // LOGICAL_MINIMUM (-32767) 42
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767) 44
0x81, 0x06, // INPUT (Data,Var,Rel) 47
0xc0, // END_COLLECTION 49
0xc0, // END_COLLECTION 50/51
// Keyboard Collection
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x85, REPORTID_KEYBOARD, // REPORT_ID (2)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x09, 0xe0, // USAGE (Keyboard LeftControl)
0x09, 0xe7, // USAGE (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x08, // REPORT_COUNT (8)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x08, // REPORT_COUNT (8)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x01, // INPUT (Cnst,Ary,Abs)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x09, 0x01, // USAGE (Num Lock)
0x09, 0x03, // USAGE (Scroll Lock)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x91, 0x01, // OUTPUT (Cnst,Ary,Abs)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x09, 0x00, // USAGE (Reserved (no event indicated))
0x09, 0xff, // USAGE (Unkown)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0 // END_COLLECTION
`
PROBLEM #2: According to my debug print statements the function that I use to send my keyboard input report seems to be sending the data to the system but the keystrokes never show up. The driver completes the keyboard report function successfully and the system continues to send read requests to the driver. The only problem is that the characters I attempt to send never show up in notepad (I am using notepad to test whether or not I successfully send keystrokes). Here is the function I am using to send keyboard input reports:
VOID Gh_HidKeyboardReport(PDEVICE_EXTENSION devContext, UCHAR eventID){
PHID_KEYBOARD_REPORT report;
NTSTATUS status;
WDFREQUEST request;
int i;
//size_t bufferLength = 0;
DbgPrint("Gh_HidKeyboardReport\n");
status = WdfIoQueueRetrieveNextRequest(devContext->InterruptMsgQueue, &request);
//status = WdfIoQueueRetrieveNextRequest(devContext->ReadRequestQueue, &request);
if(!NT_SUCCESS(status)){
DbgPrint(" WdfIoQueueRetrieveNextRequest failed with status: 0x%x\n",status);
}else if (NT_SUCCESS(status)){
//DbgPrint("Check 1\n");
//
// 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_KEYBOARD_REPORT),
&report,
NULL); //bufferLength
//DbgPrint("Check 2: 0x%x\n", status);
//DbgPrint("Check 3: 0x%x\n", status);
if (!NT_SUCCESS(status)) { // should never happen
DbgPrint("Went Wrong!\n");
TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL,
"WdfRequestRetrieveOutputBuffer failed with status: 0x%x\n", status);
DbgPrint(("Retrieve error: 0x%x\n", status));
WdfRequestComplete(request, status);
}else{
//DbgPrint("Check 4\n");
report->reportID = REPORTID_KEYBOARD;
report->modifierKeys = NO_MODIFIERS;
report->reserved = 0x00;
for(i=0; i<6; i++){
report->key[i] = 0x00;
}
if(eventID == NO_EVENT){
goto leave;
}else if(eventID == PUSH_BUTTON){
report->key[0] = KEY_A;
report->key[1] = KEY_B;
report->key[2] = KEY_C;
report->key[3] = KEY_D;
report->key[4] = KEY_E;
report->key[5] = KEY_F;
}else if(eventID == RING_ENCODER_CLKWSE){
report->key[0] = KEY_A;
report->key[1] = KEY_B;
report->key[2] = KEY_C;
report->key[3] = KEY_D;
report->key[4] = KEY_E;
report->key[5] = KEY_F;
}else if(eventID == RING_ENCODER_CNTRCLKWSE){
report->key[0] = KEY_A;
report->key[1] = KEY_B;
report->key[2] = KEY_C;
report->key[3] = KEY_D;
report->key[4] = KEY_E;
report->key[5] = KEY_F;
}else if(eventID == ROTARY_CLKWSE){
report->key[0] = KEY_A;
report->key[1] = KEY_B;
report->key[2] = KEY_C;
report->key[3] = KEY_D;
report->key[4] = KEY_E;
report->key[5] = KEY_F;
}else if(eventID == ROTARY_CNTRCLKWSE){
report->key[0] = KEY_A;
report->key[1] = KEY_B;
report->key[2] = KEY_C;
report->key[3] = KEY_D;
report->key[4] = KEY_E;
report->key[5] = KEY_F;
}else if(eventID == ROTATE_CLKWSE){
report->key[0] = KEY_A;
//report->key[1] = KEY_B;
//report->key[2] = KEY_C;
//report->key[3] = KEY_D;
//report->key[4] = KEY_E;
//report->key[5] = KEY_F;
}else if(eventID == ROTATE_CNTRCLKWSE){
report->key[0] = KEY_A;
report->key[1] = KEY_B;
report->key[2] = KEY_C;
report->key[3] = KEY_D;
report->key[4] = KEY_E;
report->key[5] = KEY_F;
}else if(eventID == SWIPE_UP){
report->key[0] = KEY_A;
report->key[1] = KEY_B;
report->key[2] = KEY_C;
report->key[3] = KEY_D;
report->key[4] = KEY_E;
report->key[5] = KEY_F;
}else if(eventID == SWIPE_DOWN){
report->key[0] = KEY_A;
report->key[1] = KEY_B;
report->key[2] = KEY_C;
report->key[3] = KEY_D;
report->key[4] = KEY_E;
report->key[5] = KEY_F;
}else if(eventID == SWIPE_RIGHT){
report->key[0] = KEY_A;
report->key[1] = KEY_B;
report->key[2] = KEY_C;
report->key[3] = KEY_D;
report->key[4] = KEY_E;
report->key[5] = KEY_F;
}else if(eventID == SWIPE_LEFT){
report->key[0] = KEY_A;
report->key[1] = KEY_B;
report->key[2] = KEY_C;
report->key[3] = KEY_D;
report->key[4] = KEY_E;
report->key[5] = KEY_F;
}else if(eventID == ZOOM_IN){
report->key[0] = KEY_A;
report->key[1] = KEY_B;
report->key[2] = KEY_C;
report->key[3] = KEY_D;
report->key[4] = KEY_E;
report->key[5] = KEY_F;
}else if(eventID == ZOOM_OUT){
report->key[0] = KEY_A;
report->key[1] = KEY_B;
report->key[2] = KEY_C;
report->key[3] = KEY_D;
report->key[4] = KEY_E;
report->key[5] = KEY_F;
}
leave: DbgPrint("0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, status: %d\n", report->reportID, report->reserved, report->modifierKeys, report->key[0], report->key[1], report->key[2], report->key[3], report->key[4], report->key[5], status);
WdfRequestCompleteWithInformation(request,status,sizeof(HID_KEYBOARD_REPORT));
// WdfRequestComplete(request, status);
}
}
}
*NOTE: I will be changing the keystrokes that I send for each event once I can actually get them to send successfully. I use these key codes: KEY_A = 4, KEY_B = 5...KEYPAD_PERIOD = 99.
Any information on either of these issues would be greatly appreciated. I have been stuck for a while and need to get this thing done by the end of August.