Beispiel #1
0
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;
}