Inital commit
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
// hid_discover.c — enumerate HID devices, find EdgeTx radio, dump caps
|
||||
//
|
||||
// Build via CMake (target: hid_discover)
|
||||
//
|
||||
// Usage:
|
||||
// hid_discover.exe (lists all HID devices)
|
||||
// hid_discover.exe 1209 4F54 (find EdgeTx radio, dump input caps)
|
||||
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
#include <hidsdi.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// ENUM_GUID from devguid.h — used to enumerate HID class devices
|
||||
static const GUID GUID_DEVINTERFACE_HID = {
|
||||
0x4d1e55b2, 0xf16f, 0x11cf,
|
||||
{ 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 }
|
||||
};
|
||||
#define ENUM_GUID GUID_DEVINTERFACE_HID
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Capability listing (works even when the raw report descriptor IOCTL fails)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static const char* usage_page_name(USAGE page) {
|
||||
switch (page) {
|
||||
case 0x01: return "Generic Desktop";
|
||||
case 0x02: return "Simulation Controls";
|
||||
case 0x09: return "Button";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void print_value_caps(PHIDP_PREPARSED_DATA preparsed,
|
||||
const HIDP_CAPS* caps) {
|
||||
USHORT count = caps->NumberInputValueCaps;
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
PHIDP_VALUE_CAPS vcaps =
|
||||
HeapAlloc(GetProcessHeap(), 0, count * sizeof(HIDP_VALUE_CAPS));
|
||||
if (!vcaps)
|
||||
return;
|
||||
|
||||
if (HidP_GetValueCaps(HidP_Input, vcaps, &count, preparsed) == HIDP_STATUS_SUCCESS) {
|
||||
printf("\nInput value caps (%u):\n", count);
|
||||
for (USHORT i = 0; i < count; i++) {
|
||||
const HIDP_VALUE_CAPS* v = &vcaps[i];
|
||||
if (v->IsRange) {
|
||||
printf(" [%u] UsagePage=0x%02X (%s) Usage=0x%02X..0x%02X "
|
||||
"ReportID=%u BitSize=%u ReportCount=%u "
|
||||
"LogicalMin=%ld LogicalMax=%ld\n",
|
||||
i, v->UsagePage, usage_page_name(v->UsagePage),
|
||||
v->Range.UsageMin, v->Range.UsageMax,
|
||||
v->ReportID, v->BitSize, v->ReportCount,
|
||||
(long)v->LogicalMin, (long)v->LogicalMax);
|
||||
} else {
|
||||
printf(" [%u] UsagePage=0x%02X (%s) Usage=0x%02X "
|
||||
"ReportID=%u BitSize=%u ReportCount=%u "
|
||||
"LogicalMin=%ld LogicalMax=%ld\n",
|
||||
i, v->UsagePage, usage_page_name(v->UsagePage),
|
||||
v->NotRange.Usage,
|
||||
v->ReportID, v->BitSize, v->ReportCount,
|
||||
(long)v->LogicalMin, (long)v->LogicalMax);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "HidP_GetValueCaps failed\n");
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, vcaps);
|
||||
}
|
||||
|
||||
static void print_button_caps(PHIDP_PREPARSED_DATA preparsed,
|
||||
const HIDP_CAPS* caps) {
|
||||
USHORT count = caps->NumberInputButtonCaps;
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
PHIDP_BUTTON_CAPS bcaps =
|
||||
HeapAlloc(GetProcessHeap(), 0, count * sizeof(HIDP_BUTTON_CAPS));
|
||||
if (!bcaps)
|
||||
return;
|
||||
|
||||
if (HidP_GetButtonCaps(HidP_Input, bcaps, &count, preparsed) == HIDP_STATUS_SUCCESS) {
|
||||
printf("\nInput button caps (%u):\n", count);
|
||||
for (USHORT i = 0; i < count; i++) {
|
||||
const HIDP_BUTTON_CAPS* b = &bcaps[i];
|
||||
if (b->IsRange) {
|
||||
printf(" [%u] UsagePage=0x%02X (%s) Usage=0x%02X..0x%02X "
|
||||
"ReportID=%u\n",
|
||||
i, b->UsagePage, usage_page_name(b->UsagePage),
|
||||
b->Range.UsageMin, b->Range.UsageMax, b->ReportID);
|
||||
} else {
|
||||
printf(" [%u] UsagePage=0x%02X (%s) Usage=0x%02X ReportID=%u\n",
|
||||
i, b->UsagePage, usage_page_name(b->UsagePage),
|
||||
b->NotRange.Usage, b->ReportID);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "HidP_GetButtonCaps failed\n");
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, bcaps);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Device probe
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static int device_num = 0;
|
||||
|
||||
static int probe_device(HANDLE handle, const wchar_t* path,
|
||||
uint16_t filter_vid, uint16_t filter_pid) {
|
||||
HIDD_ATTRIBUTES attrs = { .Size = sizeof(attrs) };
|
||||
if (!HidD_GetAttributes(handle, &attrs))
|
||||
return 0;
|
||||
|
||||
if (filter_vid && filter_pid) {
|
||||
if (attrs.VendorID != filter_vid || attrs.ProductID != filter_pid) {
|
||||
fprintf(stderr, "Skipping VID:PID 0x%04X:0x%04X\n", attrs.VendorID, attrs.ProductID);
|
||||
return 0; // not our target
|
||||
}
|
||||
}
|
||||
|
||||
// Get HID caps via preparsed data
|
||||
HIDP_CAPS caps = { 0 };
|
||||
PHIDP_PREPARSED_DATA preparsed = NULL;
|
||||
BOOL has_caps = FALSE;
|
||||
if (HidD_GetPreparsedData(handle, &preparsed)) {
|
||||
if (HidP_GetCaps(preparsed, &caps) == HIDP_STATUS_SUCCESS)
|
||||
has_caps = TRUE;
|
||||
}
|
||||
|
||||
device_num++;
|
||||
printf("\n=== Device %d ===\n", device_num);
|
||||
printf("Path: %ls\n", path);
|
||||
printf("VID:PID: 0x%04X:0x%04X Version: 0x%04X\n",
|
||||
attrs.VendorID, attrs.ProductID, attrs.VersionNumber);
|
||||
|
||||
if (has_caps) {
|
||||
printf("Input report length: %u\n", caps.InputReportByteLength);
|
||||
printf("Output report length: %u\n", caps.OutputReportByteLength);
|
||||
printf("Feature report length: %u\n", caps.FeatureReportByteLength);
|
||||
printf("Input buttons: %u\n", caps.NumberInputButtonCaps);
|
||||
printf("Input values: %u\n", caps.NumberInputValueCaps);
|
||||
|
||||
print_value_caps(preparsed, &caps);
|
||||
print_button_caps(preparsed, &caps);
|
||||
}
|
||||
|
||||
if (preparsed)
|
||||
HidD_FreePreparsedData(preparsed);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Enumeration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static int enumerate_hid_devices(uint16_t filter_vid, uint16_t filter_pid) {
|
||||
HDEVINFO info = SetupDiGetClassDevsW(&ENUM_GUID, NULL, NULL,
|
||||
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||
if (info == INVALID_HANDLE_VALUE) {
|
||||
fprintf(stderr, "SetupDiGetClassDevs failed: %lu\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int found = 0;
|
||||
for (DWORD i = 0; ; i++) {
|
||||
SP_DEVICE_INTERFACE_DATA iface = { .cbSize = sizeof(iface) };
|
||||
BOOL ok = SetupDiEnumDeviceInterfaces(info, NULL, &ENUM_GUID, i, &iface);
|
||||
if (!ok) {
|
||||
fprintf(stderr, "Enumerated %lu interfaces (last error: %lu)\n",
|
||||
(unsigned long)i, GetLastError());
|
||||
break;
|
||||
}
|
||||
|
||||
DWORD buflen = 0;
|
||||
SetupDiGetDeviceInterfaceDetailW(info, &iface, NULL, 0, &buflen, NULL);
|
||||
if (buflen == 0) continue;
|
||||
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA_W detail =
|
||||
(PSP_DEVICE_INTERFACE_DETAIL_DATA_W)HeapAlloc(GetProcessHeap(), 0, buflen);
|
||||
detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
|
||||
|
||||
if (!SetupDiGetDeviceInterfaceDetailW(info, &iface, detail, buflen,
|
||||
&buflen, NULL)) {
|
||||
HeapFree(GetProcessHeap(), 0, detail);
|
||||
continue;
|
||||
}
|
||||
|
||||
HANDLE handle = CreateFileW(detail->DevicePath,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, 0,
|
||||
NULL);
|
||||
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
if (probe_device(handle, detail->DevicePath, filter_vid, filter_pid))
|
||||
found++;
|
||||
CloseHandle(handle);
|
||||
if (filter_vid && filter_pid && found > 0) break; // found our target
|
||||
} else {
|
||||
if (filter_vid && filter_pid)
|
||||
fprintf(stderr, "CreateFile failed for %ls: %lu\n",
|
||||
detail->DevicePath, GetLastError());
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, detail);
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(info);
|
||||
return found;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
uint16_t vid = 0, pid = 0;
|
||||
if (argc >= 3) {
|
||||
vid = (uint16_t)strtoul(argv[1], NULL, 16);
|
||||
pid = (uint16_t)strtoul(argv[2], NULL, 16);
|
||||
printf("Looking for VID:PID 0x%04X:0x%04X\n", vid, pid);
|
||||
} else {
|
||||
printf("Listing all HID devices (pass VID PID to filter)\n");
|
||||
}
|
||||
return enumerate_hid_devices(vid, pid) == 0 ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user