VOID HIDAppend2(PDYNAMIC_ARRAY pArray, UCHAR tag, LONG value) { if (value >= -MINCHAR && value <= MAXCHAR) { HIDAppend1(pArray, tag | 0x01); DynamicArrayAppend(pArray, &value, 1); } else if (value >= -MINSHORT && value <= MAXSHORT) { HIDAppend1(pArray, tag | 0x02); DynamicArrayAppend(pArray, &value, 2); } else { HIDAppend1(pArray, tag | 0x03); DynamicArrayAppend(pArray, &value, 4); } }
NTSTATUS HIDKeyboardProbe( PINPUT_DEVICE pContext, PDYNAMIC_ARRAY pHidDesc, PVIRTIO_INPUT_CFG_DATA pKeys, PVIRTIO_INPUT_CFG_DATA pLeds) { PINPUT_CLASS_KEYBOARD pKeyboardDesc = NULL; NTSTATUS status = STATUS_SUCCESS; UCHAR i, uValue, uMaxKey, uMaxLed; BOOLEAN bGotKey, bGotLed; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "--> %s\n", __FUNCTION__); uMaxKey = 0; bGotKey = FALSE; for (i = 0; i < pKeys->size; i++) { UCHAR uNonKeys = 0; while (DecodeNextBit(&pKeys->u.bitmap[i], &uValue)) { USHORT uKeyCode = uValue + 8 * i; ULONG uCode = HIDKeyboardEventCodeToUsageCode(uKeyCode); if (uCode != 0 && (uCode & KEY_TYPE_MASK) == KEY_TYPE_KEYBOARD) { bGotKey = TRUE; if (uCode < 0xE0 || uCode > 0xE7) { uMaxKey = max(uMaxKey, (UCHAR)uCode); } } else { // we have not recognized this EV_KEY code as a keyboard key uNonKeys |= (1 << uValue); } } pKeys->u.bitmap[i] = uNonKeys; } if (!bGotKey) { // no keys in the array means that we're done TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "No keyboard key found\n"); goto Exit; } // allocate and initialize pKeyboardDesc pKeyboardDesc = VIOInputAlloc(sizeof(INPUT_CLASS_KEYBOARD)); if (pKeyboardDesc == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } pKeyboardDesc->Common.EventToReportFunc = HIDKeyboardEventToReport; pKeyboardDesc->Common.ReportToEventFunc = HIDKeyboardReportToEvent; pKeyboardDesc->Common.CleanupFunc = HIDKeyboardCleanup; pKeyboardDesc->Common.uReportID = (UCHAR)(pContext->uNumOfClasses + 1); HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_GENERIC); HIDAppend2(pHidDesc, HID_TAG_USAGE, HID_USAGE_GENERIC_KEYBOARD); HIDAppend2(pHidDesc, HID_TAG_COLLECTION, HID_COLLECTION_APPLICATION); HIDAppend2(pHidDesc, HID_TAG_REPORT_ID, pKeyboardDesc->Common.uReportID); // one bit per modifier HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_KEYBOARD); HIDAppend2(pHidDesc, HID_TAG_USAGE_MINIMUM, 0xE0); HIDAppend2(pHidDesc, HID_TAG_USAGE_MAXIMUM, 0xE7); HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MINIMUM, 0x00); HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MAXIMUM, 0x01); HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x01); HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x08); HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_VARIABLE); // reserved byte HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x01); HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x08); HIDAppend2(pHidDesc, HID_TAG_INPUT, HID_DATA_FLAG_CONSTANT); // LEDs uMaxLed = 0; bGotLed = FALSE; for (i = 0; i < pLeds->size; i++) { while (DecodeNextBit(&pLeds->u.bitmap[i], &uValue)) { USHORT uLedCode = uValue + 8 * i; UCHAR uCode = HIDLEDEventCodeToUsageCode(uLedCode); if (uCode != 0) { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Got LED %d\n", uLedCode); uMaxLed = max(uMaxLed, uCode); bGotLed = TRUE; } } } if (bGotLed) { ULONG uNumOfLEDs = uMaxLed + 1; pKeyboardDesc->cbOutputReport = HID_REPORT_DATA_OFFSET + (uNumOfLEDs + 7) / 8; // one bit per LED as usual in a keyboard device HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x01); HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, uMaxLed + 1); HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_LED); HIDAppend2(pHidDesc, HID_TAG_USAGE_MINIMUM, 1); HIDAppend2(pHidDesc, HID_TAG_USAGE_MAXIMUM, uMaxLed + 1); HIDAppend2(pHidDesc, HID_TAG_OUTPUT, HID_DATA_FLAG_VARIABLE); // pad to the nearest whole byte if (uNumOfLEDs % 8) { HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, 0x01); HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, (ULONG)(pKeyboardDesc->cbOutputReport * 8) - uNumOfLEDs); HIDAppend2(pHidDesc, HID_TAG_OUTPUT, HID_DATA_FLAG_CONSTANT); } // allocate and initialize a buffer holding the last seen output report pKeyboardDesc->pLastOutputReport = ExAllocatePoolWithTag( NonPagedPool, pKeyboardDesc->cbOutputReport, VIOINPUT_DRIVER_MEMORY_TAG); if (pKeyboardDesc->pLastOutputReport == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(pKeyboardDesc->pLastOutputReport, pKeyboardDesc->cbOutputReport); } // keys HIDAppend2(pHidDesc, HID_TAG_REPORT_SIZE, 0x08); HIDAppend2(pHidDesc, HID_TAG_REPORT_COUNT, HID_KEYBOARD_KEY_SLOTS); HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MINIMUM, 0x00); HIDAppend2(pHidDesc, HID_TAG_LOGICAL_MAXIMUM, uMaxKey); HIDAppend2(pHidDesc, HID_TAG_USAGE_PAGE, HID_USAGE_PAGE_KEYBOARD); HIDAppend2(pHidDesc, HID_TAG_USAGE_MINIMUM, 0x00); HIDAppend2(pHidDesc, HID_TAG_USAGE_MAXIMUM, uMaxKey); HIDAppend2(pHidDesc, HID_TAG_INPUT, 0); HIDAppend1(pHidDesc, HID_TAG_END_COLLECTION); // allocate and initialize the bitmap of currently pressed keys pKeyboardDesc->cbKeysPressedLen = ((uMaxKey + 1) + 7) / 8; pKeyboardDesc->pKeysPressed = VIOInputAlloc(pKeyboardDesc->cbKeysPressedLen); if (pKeyboardDesc->pKeysPressed == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Created HID keyboard report descriptor with %d keys and %d LEDs\n", uMaxKey + 1, bGotLed ? (uMaxLed + 1) : 0); // calculate the keyboard HID report size pKeyboardDesc->Common.cbHidReportSize = HID_REPORT_DATA_OFFSET + 2 + // modifiers and padding HID_KEYBOARD_KEY_SLOTS; // register the keyboard class status = RegisterClass(pContext, &pKeyboardDesc->Common); Exit: if (!NT_SUCCESS(status) && pKeyboardDesc != NULL) { pKeyboardDesc->Common.CleanupFunc(&pKeyboardDesc->Common); VIOInputFree(&pKeyboardDesc); } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- %s (%08x)\n", __FUNCTION__, status); return status; }