2

I'm working on creating a virtual HID device in Windows 10. To prepare myself for writing the drivers, I've been analyzing the sample provided here: https://github.com/Microsoft/Windows-driver-samples/tree/master/hid/vhidmini2.

In the file app/testvhid.c, HidD_GetAttributes is used to retrieve the attributes of the HID device. It appears that the attributes are initialized in driver/vhidmini.c (hidAttributes in the EvtDeviceAdd function). However, hidAttributes is stored inside of a DEVICE_CONTEXT structure, which (from my understanding) is defined by the driver.

If hidAttributes is a user defined attribute, then how does HidD_GetAttributes know to retrieve it from the device?

I've tried to replicate this for myself by creating a KMDF driver which is based mostly on the sample given. The relevant driver code is as follows (The majority of this was taken from the sample, with the exception of the call to WdfDeviceCreateDeviceInterface):

NTSTATUS
kmdfTest2CreateDevice(
    _Inout_ PWDFDEVICE_INIT DeviceInit
    )
{
    WDF_OBJECT_ATTRIBUTES   deviceAttributes;
    PDEVICE_CONTEXT deviceContext;
    WDFDEVICE device;
    NTSTATUS status;
    PHID_DEVICE_ATTRIBUTES  hidAttributes;

    WdfFdoInitSetFilter(DeviceInit);

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);

    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);

    if (NT_SUCCESS(status)) {
        deviceContext = DeviceGetContext(device);
        deviceContext->Device = device;
        deviceContext->DeviceData = 0;

        hidAttributes = &deviceContext->HidDeviceAttributes;
        RtlZeroMemory(hidAttributes, sizeof(HID_DEVICE_ATTRIBUTES));
        hidAttributes->Size = sizeof(HID_DEVICE_ATTRIBUTES);
        hidAttributes->VendorID = HIDMINI_VID;
        hidAttributes->ProductID = HIDMINI_PID;
        hidAttributes->VersionNumber = HIDMINI_VERSION;

        if (NT_SUCCESS(status)) {
            status = kmdfTest2QueueInitialize(device);
        }

        deviceContext->HidDescriptor = G_DefaultHidDescriptor;
        deviceContext->ReportDescriptor = G_DefaultReportDescriptor;

        status = WdfDeviceCreateDeviceInterface(
            device,
            &GUID_DEVINTERFACE_HID,
            NULL // ReferenceString
            );
    }

    return status;
}

The relevant section of code in the user mode test application is as follows (this code is virtually entirely from the given sample):

BOOLEAN
CheckIfOurDevice(
    HANDLE file)
{
    PHIDP_PREPARSED_DATA Ppd; // The opaque parser info describing this device
    HIDP_CAPS Caps; // The Capabilities of this hid device.
    HIDD_ATTRIBUTES attr; // Device attributes

    if (!HidD_GetAttributes(file, &attr))
    {
        printf("Error: HidD_GetAttributes failed \n");
        return FALSE;
    }

    printf("Device Attributes - PID: 0x%x, VID: 0x%x \n", attr.ProductID, attr.VendorID);
    if ((attr.VendorID != HIDMINI_VID) || (attr.ProductID != HIDMINI_PID))
    {
        printf("Device attributes doesn't match the sample \n");
        return FALSE;
    }

    if (!HidD_GetPreparsedData(file, &Ppd))
    {
        printf("Error: HidD_GetPreparsedData failed \n");
        return FALSE;
    }

    if (!HidP_GetCaps(Ppd, &Caps))
    {
        printf("Error: HidP_GetCaps failed \n");
        HidD_FreePreparsedData(Ppd);
        return FALSE;
    }

    if ((Caps.UsagePage == g_MyUsagePage) && (Caps.Usage == g_MyUsage)) {
        printf("Success: Found my device.. \n");
        return TRUE;
    }
    else {
        printf("failed: UsagePage: %d, Usage: %d \n", Caps.UsagePage, Caps.Usage);
    }

    return FALSE;

}

BOOLEAN
GetHidInterface(_Out_ HANDLE* Handle) {
    CONFIGRET cr = CR_SUCCESS;
    *Handle = INVALID_HANDLE_VALUE;
    ULONG deviceInterfaceListLength = 0;
    GUID InterfaceGuid;
    PSTR deviceInterfaceList = NULL;
    HANDLE devHandle = INVALID_HANDLE_VALUE;

    if (NULL == Handle) {
        printf("Error: Invalid device handle parameter\n");
        return FALSE;
    }

    HidD_GetHidGuid(&InterfaceGuid);

    cr = CM_Get_Device_Interface_List_Size(
        &deviceInterfaceListLength,
        &InterfaceGuid,
        NULL,
        CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
    if (cr != CR_SUCCESS) {
        printf("Error 0x%x retrieving device interface list size.\n", cr);
        return FALSE;
    }
    if (deviceInterfaceListLength <= 1) {
        printf("Error: No active device interfaces found.\n"
            " Is the sample driver loaded?");
        return FALSE;
    }
    deviceInterfaceList = (PSTR)malloc(deviceInterfaceListLength * sizeof(WCHAR));
    if (deviceInterfaceList == NULL) {
        printf("Error allocating memory for device interface list.\n");
        return FALSE;
    }
    ZeroMemory(deviceInterfaceList, deviceInterfaceListLength * sizeof(WCHAR));
    cr = CM_Get_Device_Interface_List(
        &InterfaceGuid,
        NULL,
        deviceInterfaceList,
        deviceInterfaceListLength,
        CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
    if (cr != CR_SUCCESS) {
        printf("Error 0x%x retrieving device interface list.\n", cr);
        return FALSE;
    }

    printf("\n....looking for our HID device (with UP=0x%04X "
        "and Usage=0x%02X)\n", g_MyUsagePage, g_MyUsage);

    PSTR currentInterface;
    for (currentInterface = deviceInterfaceList;
    *currentInterface;
        currentInterface += strlen(currentInterface) + 1) {

        devHandle = CreateFile(currentInterface,
            GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            NULL, // no SECURITY_ATTRIBUTES structure
            OPEN_EXISTING, // No special create flags
            0, // No special attributes
            NULL); // No template file

        if (INVALID_HANDLE_VALUE == devHandle) {
            printf("Warning: CreateFile failed: %d\n", GetLastError());
            continue;
        }

        if (CheckIfOurDevice(devHandle)) {          
            *Handle = devHandle;
            return TRUE;
        }
        else {
            CloseHandle(devHandle);
        }
    }
    return FALSE;
}

The HidD_GetAttributes function passes, but the CheckIfOurDevice function fails when checking the device attributes ("Device attributes doesn't match the sample"). I've checked some of the attribute values that are being set by HidD_GetAttributes, but they don't match up with the ones set by the driver. Is the sample making some other call that I'm missing?

bacowan
  • 165
  • 2
  • 12
  • You're supposed to fill in the size when calling the function. – PineForestRanch Mar 12 '16 at 00:37
  • No, I don't think so. The driver is already setting the size (hidAttributes->Size = sizeof(HID_DEVICE_ATTRIBUTES)). If you're saying that I should be setting the size immediately after calling HidD_GetAttributes, I don't think that's it either: the size should be retrieved from this function. As a quick test, I tried doing that, and got the same result. – bacowan Mar 12 '16 at 01:11
  • In many MS functions which fill in the structures in the buffer, you need to pre-fill the size member of the structure, so that the OS could know what is the size of the buffer (as the definition of the structure may be extended in future versions). If it is zero there, Windows may decide that your buffer is too small and not fill it at all. This has nothing to do with what you do in the driver. – PineForestRanch Mar 12 '16 at 15:29
  • So if I understand you correctly, you're saying that I should be setting attr.Size immediately before calling HidD_GetAttributes so that the OS knows how to fill in the structure? I tried that (by setting the size 32, which is what the driver sets the size to), and HidD_GetAttributes resets the size to 12, and I still get the same error. – bacowan Mar 13 '16 at 02:46
  • `attr.Size = sizeof attr;` – user1139455 Sep 19 '20 at 19:26

0 Answers0