3535#include <sys/utsname.h>
3636#include <fcntl.h>
3737#include <poll.h>
38+ #include <pthread.h>
3839
3940/* Linux */
4041#include <linux/hidraw.h>
7172#define HIDIOCSOUTPUT (len ) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0B, len)
7273#endif
7374
75+ /* The value of the first callback handle to be given upon registration */
76+ /* Can be any arbitrary positive integer */
77+ #define FIRST_HOTPLUG_CALLBACK_HANDLE 1
78+
7479struct hid_device_ {
7580 int device_handle ;
7681 int blocking ;
@@ -896,6 +901,96 @@ HID_API_EXPORT const char* HID_API_CALL hid_version_str(void)
896901 return HID_API_VERSION_STR ;
897902}
898903
904+ static struct hid_hotplug_context {
905+ /* UDEV context that handles the monitor */
906+ struct udev * udev_ctx ;
907+
908+ /* UDEV monitor that receives events */
909+ struct udev_monitor * mon ;
910+
911+ /* File descriptor for the UDEV monitor that allows to check for new events with select() */
912+ int monitor_fd ;
913+
914+ /* Thread for the UDEV monitor */
915+ pthread_t thread ;
916+
917+ pthread_mutex_t mutex ;
918+
919+ int mutex_ready ;
920+
921+ /* HIDAPI unique callback handle counter */
922+ hid_hotplug_callback_handle next_handle ;
923+
924+ /* Linked list of the hotplug callbacks */
925+ struct hid_hotplug_callback * hotplug_cbs ;
926+
927+ /* Linked list of the device infos (mandatory when the device is disconnected) */
928+ struct hid_device_info * devs ;
929+ } hid_hotplug_context = {
930+ .udev_ctx = NULL ,
931+ .monitor_fd = -1 ,
932+ .next_handle = FIRST_HOTPLUG_CALLBACK_HANDLE ,
933+ .mutex_ready = 0 ,
934+ .hotplug_cbs = NULL ,
935+ .devs = NULL
936+ };
937+
938+ struct hid_hotplug_callback {
939+ hid_hotplug_callback_handle handle ;
940+ unsigned short vendor_id ;
941+ unsigned short product_id ;
942+ hid_hotplug_event events ;
943+ void * user_data ;
944+ hid_hotplug_callback_fn callback ;
945+
946+ /* Pointer to the next notification */
947+ struct hid_hotplug_callback * next ;
948+ };
949+
950+ static void hid_internal_hotplug_cleanup ()
951+ {
952+ if (hid_hotplug_context .hotplug_cbs != NULL ) {
953+ return ;
954+ }
955+
956+ pthread_join (hid_hotplug_context .thread , NULL );
957+
958+ /* Cleanup connected device list */
959+ hid_free_enumeration (hid_hotplug_context .devs );
960+ hid_hotplug_context .devs = NULL ;
961+ /* Disarm the udev monitor */
962+ udev_monitor_unref (hid_hotplug_context .mon );
963+ udev_unref (hid_hotplug_context .udev_ctx );
964+ }
965+
966+ static void hid_internal_hotplug_init ()
967+ {
968+ if (!hid_hotplug_context .mutex_ready ) {
969+ pthread_mutex_init (& hid_hotplug_context .mutex , NULL );
970+ hid_hotplug_context .mutex_ready = 1 ;
971+ }
972+ }
973+
974+ static void hid_internal_hotplug_exit ()
975+ {
976+ if (!hid_hotplug_context .mutex_ready ) {
977+ return ;
978+ }
979+
980+ pthread_mutex_lock (& hid_hotplug_context .mutex );
981+ struct hid_hotplug_callback * * current = & hid_hotplug_context .hotplug_cbs ;
982+ /* Remove all callbacks from the list */
983+ while (* current ) {
984+ struct hid_hotplug_callback * next = (* current )-> next ;
985+ free (* current );
986+ * current = next ;
987+ }
988+ hid_internal_hotplug_cleanup ();
989+ pthread_mutex_unlock (& hid_hotplug_context .mutex );
990+ hid_hotplug_context .mutex_ready = 0 ;
991+ pthread_mutex_destroy (& hid_hotplug_context .mutex );
992+ }
993+
899994int HID_API_EXPORT hid_init (void )
900995{
901996 const char * locale ;
@@ -911,14 +1006,22 @@ int HID_API_EXPORT hid_init(void)
9111006 return 0 ;
9121007}
9131008
1009+
9141010int HID_API_EXPORT hid_exit (void )
9151011{
9161012 /* Free global error message */
9171013 register_global_error (NULL );
9181014
1015+ hid_internal_hotplug_exit ();
1016+
9191017 return 0 ;
9201018}
9211019
1020+ static int hid_internal_match_device_id (unsigned short vendor_id , unsigned short product_id , unsigned short expected_vendor_id , unsigned short expected_product_id )
1021+ {
1022+ return (expected_vendor_id == 0x0 || vendor_id == expected_vendor_id ) && (expected_product_id == 0x0 || product_id == expected_product_id );
1023+ }
1024+
9221025struct hid_device_info HID_API_EXPORT * hid_enumerate (unsigned short vendor_id , unsigned short product_id )
9231026{
9241027 struct udev * udev ;
@@ -1020,26 +1123,224 @@ void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
10201123 }
10211124}
10221125
1126+ static void hid_internal_invoke_callbacks (struct hid_device_info * info , hid_hotplug_event event )
1127+ {
1128+ struct hid_hotplug_callback * * current = & hid_hotplug_context .hotplug_cbs ;
1129+ while (* current ) {
1130+ struct hid_hotplug_callback * callback = * current ;
1131+ if ((callback -> events & event ) && hid_internal_match_device_id (info -> vendor_id , info -> product_id ,
1132+ callback -> vendor_id , callback -> product_id )) {
1133+ int result = callback -> callback (callback -> handle , info , event , callback -> user_data );
1134+ /* If the result is non-zero, we remove the callback and proceed */
1135+ /* Do not use the deregister call as it locks the mutex, and we are currently in a lock */
1136+ if (result ) {
1137+ struct hid_hotplug_callback * callback = * current ;
1138+ * current = (* current )-> next ;
1139+ free (callback );
1140+ continue ;
1141+ }
1142+ }
1143+ current = & callback -> next ;
1144+ }
1145+ }
1146+
1147+ static int match_udev_to_info (struct udev_device * raw_dev , struct hid_device_info * info )
1148+ {
1149+ const char * path = udev_device_get_devnode (raw_dev );
1150+ if (!strcmp (path , info -> path )) {
1151+ return 1 ;
1152+ }
1153+ return 0 ;
1154+ }
1155+
1156+ static void * hotplug_thread (void * user_data )
1157+ {
1158+ (void ) user_data ;
1159+
1160+ while (hid_hotplug_context .monitor_fd > 0 ) {
1161+ fd_set fds ;
1162+ struct timeval tv ;
1163+ int ret ;
1164+
1165+ FD_ZERO (& fds );
1166+ FD_SET (hid_hotplug_context .monitor_fd , & fds );
1167+ /* 5 msec timeout seems reasonable; don't set too low to avoid high CPU usage */
1168+ /* This timeout only affects how much time it takes to stop the thread */
1169+ tv .tv_sec = 0 ;
1170+ tv .tv_usec = 5000 ;
1171+
1172+ ret = select (hid_hotplug_context .monitor_fd + 1 , & fds , NULL , NULL , & tv );
1173+
1174+ /* Check if our file descriptor has received data. */
1175+ if (ret > 0 && FD_ISSET (hid_hotplug_context .monitor_fd , & fds )) {
1176+
1177+ /* Make the call to receive the device.
1178+ select() ensured that this will not block. */
1179+ struct udev_device * raw_dev = udev_monitor_receive_device (hid_hotplug_context .mon );
1180+ if (raw_dev ) {
1181+ /* Lock the mutex so callback/device lists don't change elsewhere from here on */
1182+ pthread_mutex_lock (& hid_hotplug_context .mutex );
1183+
1184+ const char * action = udev_device_get_action (raw_dev );
1185+ if (!strcmp (action , "add" )) {
1186+ // We create a list of all usages on this UDEV device
1187+ struct hid_device_info * info = create_device_info_for_device (raw_dev );
1188+ struct hid_device_info * info_cur = info ;
1189+ while (info_cur ) {
1190+ /* For each device, call all matching callbacks */
1191+ /* TODO: possibly make the `next` field NULL to match the behavior on other systems */
1192+ hid_internal_invoke_callbacks (info_cur , HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED );
1193+ info_cur = info_cur -> next ;
1194+ }
1195+
1196+ /* Append all we got to the end of the device list */
1197+ if (info ) {
1198+ if (hid_hotplug_context .devs != NULL ) {
1199+ struct hid_device_info * last = hid_hotplug_context .devs ;
1200+ while (last -> next != NULL ) {
1201+ last = last -> next ;
1202+ }
1203+ last -> next = info ;
1204+ } else {
1205+ hid_hotplug_context .devs = info ;
1206+ }
1207+ }
1208+ } else if (!strcmp (action , "remove" )) {
1209+ for (struct hid_device_info * * current = & hid_hotplug_context .devs ; * current ;) {
1210+ struct hid_device_info * info = * current ;
1211+ if (match_udev_to_info (raw_dev , * current )) {
1212+ /* If the libusb device that's left matches this HID device, we detach it from the list */
1213+ * current = (* current )-> next ;
1214+ info -> next = NULL ;
1215+ hid_internal_invoke_callbacks (info , HID_API_HOTPLUG_EVENT_DEVICE_LEFT );
1216+ /* Free every removed device */
1217+ free (info );
1218+ } else {
1219+ current = & info -> next ;
1220+ }
1221+ }
1222+ }
1223+ udev_device_unref (raw_dev );
1224+ pthread_mutex_unlock (& hid_hotplug_context .mutex );
1225+ }
1226+ }
1227+ }
1228+ return NULL ;
1229+ }
1230+
10231231int HID_API_EXPORT HID_API_CALL hid_hotplug_register_callback (unsigned short vendor_id , unsigned short product_id , int events , int flags , hid_hotplug_callback_fn callback , void * user_data , hid_hotplug_callback_handle * callback_handle )
10241232{
1025- /* Stub */
1026- (void )vendor_id ;
1027- (void )product_id ;
1028- (void )events ;
1029- (void )flags ;
1030- (void )callback ;
1031- (void )user_data ;
1032- (void )callback_handle ;
1233+ struct hid_hotplug_callback * hotplug_cb ;
10331234
1034- return -1 ;
1235+ /* Check params */
1236+ if (events == 0
1237+ || (events & ~(HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED | HID_API_HOTPLUG_EVENT_DEVICE_LEFT ))
1238+ || (flags & ~(HID_API_HOTPLUG_ENUMERATE ))
1239+ || callback == NULL ) {
1240+ return -1 ;
1241+ }
1242+
1243+ hotplug_cb = (struct hid_hotplug_callback * )calloc (1 , sizeof (struct hid_hotplug_callback ));
1244+
1245+ if (hotplug_cb == NULL ) {
1246+ return -1 ;
1247+ }
1248+
1249+ /* Fill out the record */
1250+ hotplug_cb -> next = NULL ;
1251+ hotplug_cb -> vendor_id = vendor_id ;
1252+ hotplug_cb -> product_id = product_id ;
1253+ hotplug_cb -> events = events ;
1254+ hotplug_cb -> user_data = user_data ;
1255+ hotplug_cb -> callback = callback ;
1256+
1257+ /* Ensure we are ready to actually use the mutex */
1258+ hid_internal_hotplug_init ();
1259+
1260+ /* Lock the mutex to avoid race conditions */
1261+ pthread_mutex_lock (& hid_hotplug_context .mutex );
1262+
1263+ hotplug_cb -> handle = hid_hotplug_context .next_handle ++ ;
1264+
1265+ /* handle the unlikely case of handle overflow */
1266+ if (hid_hotplug_context .next_handle < 0 )
1267+ {
1268+ hid_hotplug_context .next_handle = 1 ;
1269+ }
1270+
1271+ /* Return allocated handle */
1272+ if (callback_handle != NULL ) {
1273+ * callback_handle = hotplug_cb -> handle ;
1274+ }
1275+
1276+ /* Append a new callback to the end */
1277+ if (hid_hotplug_context .hotplug_cbs != NULL ) {
1278+ struct hid_hotplug_callback * last = hid_hotplug_context .hotplug_cbs ;
1279+ while (last -> next != NULL ) {
1280+ last = last -> next ;
1281+ }
1282+ last -> next = hotplug_cb ;
1283+ }
1284+ else {
1285+ // Prepare a UDEV context to run monitoring on
1286+ hid_hotplug_context .udev_ctx = udev_new ();
1287+ if (!hid_hotplug_context .udev_ctx )
1288+ {
1289+ pthread_mutex_unlock (& hid_hotplug_context .mutex );
1290+ return -1 ;
1291+ }
1292+
1293+ hid_hotplug_context .mon = udev_monitor_new_from_netlink (hid_hotplug_context .udev_ctx , "udev" );
1294+ udev_monitor_filter_add_match_subsystem_devtype (hid_hotplug_context .mon , "hidraw" , NULL );
1295+ udev_monitor_enable_receiving (hid_hotplug_context .mon );
1296+ hid_hotplug_context .monitor_fd = udev_monitor_get_fd (hid_hotplug_context .mon );
1297+
1298+ /* After monitoring is all set up, enumerate all devices */
1299+ hid_hotplug_context .devs = hid_enumerate (0 , 0 );
1300+
1301+ /* Don't forget to actually register the callback */
1302+ hid_hotplug_context .hotplug_cbs = hotplug_cb ;
1303+
1304+ /* Start the thread that will be doing the event scanning */
1305+ pthread_create (& hid_hotplug_context .thread , NULL , & hotplug_thread , NULL );
1306+ }
1307+
1308+ pthread_mutex_unlock (& hid_hotplug_context .mutex );
1309+
1310+ return 0 ;
10351311}
10361312
10371313int HID_API_EXPORT HID_API_CALL hid_hotplug_deregister_callback (hid_hotplug_callback_handle callback_handle )
10381314{
1039- /* Stub */
1040- (void )callback_handle ;
1315+ if (!hid_hotplug_context .mutex_ready ) {
1316+ return -1 ;
1317+ }
10411318
1042- return -1 ;
1319+ pthread_mutex_lock (& hid_hotplug_context .mutex );
1320+
1321+ if (hid_hotplug_context .hotplug_cbs == NULL ) {
1322+ pthread_mutex_unlock (& hid_hotplug_context .mutex );
1323+ return -1 ;
1324+ }
1325+
1326+ int result = -1 ;
1327+
1328+ /* Remove this notification */
1329+ for (struct hid_hotplug_callback * * current = & hid_hotplug_context .hotplug_cbs ; * current != NULL ; current = & (* current )-> next ) {
1330+ if ((* current )-> handle == callback_handle ) {
1331+ struct hid_hotplug_callback * next = (* current )-> next ;
1332+ free (* current );
1333+ * current = next ;
1334+ result = 0 ;
1335+ break ;
1336+ }
1337+ }
1338+
1339+ hid_internal_hotplug_cleanup ();
1340+
1341+ pthread_mutex_unlock (& hid_hotplug_context .mutex );
1342+
1343+ return result ;
10431344}
10441345
10451346hid_device * hid_open (unsigned short vendor_id , unsigned short product_id , const wchar_t * serial_number )
@@ -1222,7 +1523,6 @@ int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
12221523 return 0 ; /* Success */
12231524}
12241525
1225-
12261526int HID_API_EXPORT hid_send_feature_report (hid_device * dev , const unsigned char * data , size_t length )
12271527{
12281528 int res ;
0 commit comments