Skip to content

Commit 785c8ab

Browse files
committed
HID device connection/disconnection proof-of-concept code.
1 parent 69f5bf7 commit 785c8ab

File tree

4 files changed

+261
-6
lines changed

4 files changed

+261
-6
lines changed

hidapi/hidapi.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,39 @@ extern "C" {
178178
*/
179179
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);
180180

181+
struct hid_callback_token_;
182+
typedef struct hid_callback_token_ hid_callback_token; /**< opaque hidapi structure */
183+
184+
typedef void (*hid_device_callback)(
185+
hid_callback_token* callback_token,
186+
void* context,
187+
struct hid_device_info* dev,
188+
int is_connected);
189+
190+
/** @brief Register HID device notification callback.
191+
192+
If @p vendor_id is set to 0 then any vendor matches.
193+
If @p product_id is set to 0 then any product matches.
194+
If @p vendor_id and @p product_id are both set to 0, then
195+
all HID devices will be notified.
196+
197+
@ingroup API
198+
@param vendor_id The Vendor ID (VID) of the types of device
199+
to notify about.
200+
@param product_id The Product ID (PID) of the types of
201+
device to notify about.
202+
@param context The Context you wanted to provide to
203+
your callback function.
204+
@param callback The Callback function that will be called
205+
on device connection/disconnection.
206+
207+
@returns
208+
This function returns callback token number on success or NULL on failure.
209+
*/
210+
HID_API_EXPORT hid_callback_token * HID_API_CALL hid_register_callback(unsigned short vendor_id, unsigned short product_id, void* context, hid_device_callback callback);
211+
212+
int HID_API_EXPORT HID_API_CALL hid_unregister_callback(hid_callback_token* token);
213+
181214
/** @brief Open a HID device using a Vendor ID (VID), Product ID
182215
(PID) and optionally a serial number.
183216

hidtest/test.c

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,19 @@
2727
#include <unistd.h>
2828
#endif
2929

30+
void device_callback(
31+
hid_callback_token* callback_token,
32+
void* context,
33+
struct hid_device_info* dev,
34+
int is_connected)
35+
{
36+
if (is_connected)
37+
printf("New device is connected: %s.\n", dev->path);
38+
else
39+
printf("Device was disconnected: %s.\n", dev->path);
40+
41+
}
42+
3043
int main(int argc, char* argv[])
3144
{
3245
(void)argc;
@@ -38,6 +51,7 @@ int main(int argc, char* argv[])
3851
wchar_t wstr[MAX_STR];
3952
hid_device *handle;
4053
int i;
54+
hid_callback_token *token1, *token2;
4155

4256
struct hid_device_info *devs, *cur_dev;
4357

@@ -67,6 +81,18 @@ int main(int argc, char* argv[])
6781
}
6882
hid_free_enumeration(devs);
6983

84+
token1 = hid_register_callback(0, 0, NULL, device_callback);
85+
token2 = hid_register_callback(0x054c, 0x0ce6, NULL, device_callback);
86+
87+
while (1)
88+
{
89+
90+
}
91+
92+
hid_unregister_callback(token2);
93+
hid_unregister_callback(token1);
94+
95+
/*
7096
// Set up the command buffer.
7197
memset(buf,0x00,sizeof(buf));
7298
buf[0] = 0x01;
@@ -76,7 +102,7 @@ int main(int argc, char* argv[])
76102
// Open the device using the VID, PID,
77103
// and optionally the Serial number.
78104
////handle = hid_open(0x4d8, 0x3f, L"12345");
79-
handle = hid_open(0x4d8, 0x3f, NULL);
105+
handle = hid_open(0x54c, 0x0ce6, NULL);
80106
if (!handle) {
81107
printf("unable to open device\n");
82108
return 1;
@@ -188,7 +214,7 @@ int main(int argc, char* argv[])
188214
printf("%02hhx ", buf[i]);
189215
printf("\n");
190216
191-
hid_close(handle);
217+
hid_close(handle);*/
192218

193219
/* Free static HIDAPI objects. */
194220
hid_exit();

windows/hid.c

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ typedef LONG NTSTATUS;
4646
extern "C" {
4747
#endif
4848
#include <setupapi.h>
49+
#include <cfgmgr32.h>
4950
#include <winioctl.h>
5051
#ifdef HIDAPI_USE_DDK
5152
#include <hidsdi.h>
@@ -507,6 +508,9 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor
507508

508509
//wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath);
509510

511+
/* Normalize the path */
512+
for (char* p = device_interface_detail_data->DevicePath; *p; ++p) *p = tolower(*p);
513+
510514
/* Open read-only handle to the device */
511515
read_handle = open_device(device_interface_detail_data->DevicePath, FALSE);
512516

@@ -573,6 +577,198 @@ void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *d
573577
}
574578
}
575579

580+
struct hid_notify {
581+
unsigned short vendor_id;
582+
unsigned short product_id;
583+
void* context;
584+
hid_device_callback callback;
585+
586+
/** Pointer to the next notification */
587+
struct hid_notify* next;
588+
};
589+
590+
static struct notify_context {
591+
HCMNOTIFICATION notify_handle;
592+
struct hid_notify* notifys;
593+
struct hid_device_info* devs;
594+
} notify_context = {
595+
.notify_handle = NULL,
596+
.notifys = NULL,
597+
.devs = NULL
598+
};
599+
600+
DWORD WINAPI interface_notify_callback(HCMNOTIFICATION notify, PVOID context, CM_NOTIFY_ACTION action, PCM_NOTIFY_EVENT_DATA event_data, DWORD event_data_size)
601+
{
602+
switch (action) {
603+
case CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL:
604+
case CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL:
605+
{
606+
char* path;
607+
size_t len;
608+
struct hid_device_info* dev = NULL;
609+
int is_connected = (action == CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL) ? TRUE : FALSE;
610+
611+
len = wcslen(event_data->u.DeviceInterface.SymbolicLink);
612+
path = (char*)calloc(len + 1, sizeof(char));
613+
614+
if (wcstombs(path, event_data->u.DeviceInterface.SymbolicLink, len) == (size_t)-1)
615+
goto close;
616+
617+
/* Normalize the path */
618+
for (char* p = path; *p; ++p) *p = tolower(*p);
619+
620+
if (is_connected) {
621+
dev = hid_get_device_info(path);
622+
623+
/* Append to the end of the device list */
624+
if (notify_context.devs) {
625+
struct hid_device_info* last = notify_context.devs;
626+
while (last->next) {
627+
last = last->next;
628+
}
629+
last->next = dev;
630+
}
631+
else {
632+
notify_context.devs = dev;
633+
}
634+
}
635+
else {
636+
/* Remove this device from the device list */
637+
for (struct hid_device_info** current = &notify_context.devs; *current; current = &(*current)->next) {
638+
if (strcmp((*current)->path, path) == 0) {
639+
struct hid_device_info* next = (*current)->next;
640+
dev = *current;
641+
*current = next;
642+
break;
643+
}
644+
}
645+
}
646+
647+
if (dev) {
648+
/* Call the notifications for this device */
649+
struct hid_notify* notify = notify_context.notifys;
650+
while (notify) {
651+
if ((notify->vendor_id == 0x0 || notify->vendor_id == dev->vendor_id) &&
652+
(notify->product_id == 0x0 || notify->product_id == dev->product_id)) {
653+
/* Call notification callback */
654+
(*notify->callback)((hid_callback_token*)notify, notify->context, dev, is_connected);
655+
}
656+
notify = notify->next;
657+
}
658+
659+
/* Clean removed device info */
660+
if (!is_connected)
661+
free(dev);
662+
}
663+
664+
close:
665+
free(path);
666+
}
667+
break;
668+
}
669+
670+
return ERROR_SUCCESS;
671+
}
672+
673+
HID_API_EXPORT hid_callback_token* HID_API_CALL hid_register_callback(unsigned short vendor_id, unsigned short product_id, void* context, hid_device_callback callback)
674+
{
675+
/* Create the record. */
676+
struct hid_notify* notify = (struct hid_notify*)calloc(1, sizeof(struct hid_notify));
677+
678+
/* Fill out the record */
679+
notify->next = NULL;
680+
notify->vendor_id = vendor_id;
681+
notify->product_id = product_id;
682+
notify->context = context;
683+
notify->callback = callback;
684+
685+
/* Append to the end */
686+
if (notify_context.notifys) {
687+
struct hid_notify* last = notify_context.notifys;
688+
while (last->next) {
689+
last = last->next;
690+
}
691+
last->next = notify;
692+
}
693+
else {
694+
struct hid_device_info* dev;
695+
696+
/* Register device connection notification after adding first callback */
697+
if (notify_context.notify_handle == NULL) {
698+
GUID interface_class_guid;
699+
CONFIGRET cr = CR_SUCCESS;
700+
CM_NOTIFY_FILTER notify_filter = { 0 };
701+
702+
HidD_GetHidGuid(&interface_class_guid);
703+
704+
notify_filter.cbSize = sizeof(notify_filter);
705+
notify_filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
706+
notify_filter.u.DeviceInterface.ClassGuid = interface_class_guid;
707+
cr = CM_Register_Notification(&notify_filter, NULL, interface_notify_callback, &notify_context.notify_handle);
708+
if (cr != CR_SUCCESS) {
709+
return NULL;
710+
}
711+
}
712+
713+
notify_context.notifys = notify;
714+
715+
/* Fill already connected devices */
716+
dev = hid_enumerate(0, 0);
717+
notify_context.devs = dev;
718+
719+
/* Notify about already connected devices */
720+
while (dev) {
721+
if ((notify->vendor_id == 0x0 || notify->vendor_id == dev->vendor_id) &&
722+
(notify->product_id == 0x0 || notify->product_id == dev->product_id)) {
723+
(*notify->callback)((hid_callback_token*)notify, notify->callback, dev, TRUE);
724+
}
725+
dev = dev->next;
726+
}
727+
}
728+
729+
return (hid_callback_token*)notify;
730+
}
731+
732+
int HID_API_EXPORT HID_API_CALL hid_unregister_callback(hid_callback_token* token)
733+
{
734+
struct hid_notify* notify = NULL;
735+
736+
if (token <= 0 || notify_context.notifys == NULL)
737+
return 1;
738+
739+
/* Remove this notification */
740+
for (struct hid_notify** current = &notify_context.notifys; *current; current = &(*current)->next) {
741+
if (*current == (struct hid_notify*)token) {
742+
struct hid_notify* next = (*current)->next;
743+
notify = *current;
744+
*current = next;
745+
break;
746+
}
747+
}
748+
749+
if (!notify)
750+
return 1;
751+
752+
free(notify);
753+
754+
/* Unregister device connection notification on removing last callback */
755+
if (notify_context.notifys == NULL) {
756+
/* Cleanup connected device list */
757+
hid_free_enumeration(notify_context.devs);
758+
notify_context.devs = NULL;
759+
760+
if (notify_context.notify_handle != NULL) {
761+
CONFIGRET cr = CM_Unregister_Notification(notify_context.notify_handle);
762+
notify_context.notify_handle = NULL;
763+
if (cr != CR_SUCCESS) {
764+
return 1;
765+
}
766+
}
767+
}
768+
769+
return 0;
770+
}
771+
576772

577773
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
578774
{

windows/hidapi.vcxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
9999
</ClCompile>
100100
<Link>
101-
<AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
101+
<AdditionalDependencies>setupapi.lib;cfgmgr32.lib;%(AdditionalDependencies)</AdditionalDependencies>
102102
<GenerateDebugInformation>true</GenerateDebugInformation>
103103
<SubSystem>Windows</SubSystem>
104104
<TargetMachine>MachineX86</TargetMachine>
@@ -117,7 +117,7 @@
117117
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
118118
</ClCompile>
119119
<Link>
120-
<AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
120+
<AdditionalDependencies>setupapi.lib;cfgmgr32.lib;%(AdditionalDependencies)</AdditionalDependencies>
121121
<GenerateDebugInformation>true</GenerateDebugInformation>
122122
<SubSystem>Windows</SubSystem>
123123
</Link>
@@ -135,7 +135,7 @@
135135
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
136136
</ClCompile>
137137
<Link>
138-
<AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
138+
<AdditionalDependencies>setupapi.lib;cfgmgr32.lib;%(AdditionalDependencies)</AdditionalDependencies>
139139
<GenerateDebugInformation>true</GenerateDebugInformation>
140140
<SubSystem>Windows</SubSystem>
141141
<OptimizeReferences>true</OptimizeReferences>
@@ -157,7 +157,7 @@
157157
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
158158
</ClCompile>
159159
<Link>
160-
<AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
160+
<AdditionalDependencies>setupapi.lib;cfgmgr32.lib;%(AdditionalDependencies)</AdditionalDependencies>
161161
<GenerateDebugInformation>true</GenerateDebugInformation>
162162
<SubSystem>Windows</SubSystem>
163163
<OptimizeReferences>true</OptimizeReferences>

0 commit comments

Comments
 (0)