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?