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>
6869#define HIDIOCGINPUT (len ) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0A, len)
6970#endif
7071
72+ /* The value of the first callback handle to be given upon registration */
73+ /* Can be any arbitrary positive integer */
74+ #define FIRST_HOTPLUG_CALLBACK_HANDLE 1
75+
7176struct hid_device_ {
7277 int device_handle ;
7378 int blocking ;
@@ -880,6 +885,96 @@ HID_API_EXPORT const char* HID_API_CALL hid_version_str(void)
880885 return HID_API_VERSION_STR ;
881886}
882887
888+ static struct hid_hotplug_context {
889+ /* UDEV context that handles the monitor */
890+ struct udev * udev_ctx ;
891+
892+ /* UDEV monitor that receives events */
893+ struct udev_monitor * mon ;
894+
895+ /* File descriptor for the UDEV monitor that allows to check for new events with select() */
896+ int monitor_fd ;
897+
898+ /* Thread for the UDEV monitor */
899+ pthread_t thread ;
900+
901+ pthread_mutex_t mutex ;
902+
903+ int mutex_ready ;
904+
905+ /* HIDAPI unique callback handle counter */
906+ hid_hotplug_callback_handle next_handle ;
907+
908+ /* Linked list of the hotplug callbacks */
909+ struct hid_hotplug_callback * hotplug_cbs ;
910+
911+ /* Linked list of the device infos (mandatory when the device is disconnected) */
912+ struct hid_device_info * devs ;
913+ } hid_hotplug_context = {
914+ .udev_ctx = NULL ,
915+ .monitor_fd = -1 ,
916+ .next_handle = FIRST_HOTPLUG_CALLBACK_HANDLE ,
917+ .mutex_ready = 0 ,
918+ .hotplug_cbs = NULL ,
919+ .devs = NULL
920+ };
921+
922+ struct hid_hotplug_callback {
923+ hid_hotplug_callback_handle handle ;
924+ unsigned short vendor_id ;
925+ unsigned short product_id ;
926+ hid_hotplug_event events ;
927+ void * user_data ;
928+ hid_hotplug_callback_fn callback ;
929+
930+ /* Pointer to the next notification */
931+ struct hid_hotplug_callback * next ;
932+ };
933+
934+ static void hid_internal_hotplug_cleanup ()
935+ {
936+ if (hid_hotplug_context .hotplug_cbs != NULL ) {
937+ return ;
938+ }
939+
940+ pthread_join (hid_hotplug_context .thread , NULL );
941+
942+ /* Cleanup connected device list */
943+ hid_free_enumeration (hid_hotplug_context .devs );
944+ hid_hotplug_context .devs = NULL ;
945+ /* Disarm the udev monitor */
946+ udev_monitor_unref (hid_hotplug_context .mon );
947+ udev_unref (hid_hotplug_context .udev_ctx );
948+ }
949+
950+ static void hid_internal_hotplug_init ()
951+ {
952+ if (!hid_hotplug_context .mutex_ready ) {
953+ pthread_mutex_init (& hid_hotplug_context .mutex , NULL );
954+ hid_hotplug_context .mutex_ready = 1 ;
955+ }
956+ }
957+
958+ static void hid_internal_hotplug_exit ()
959+ {
960+ if (!hid_hotplug_context .mutex_ready ) {
961+ return ;
962+ }
963+
964+ pthread_mutex_lock (& hid_hotplug_context .mutex );
965+ struct hid_hotplug_callback * * current = & hid_hotplug_context .hotplug_cbs ;
966+ /* Remove all callbacks from the list */
967+ while (* current ) {
968+ struct hid_hotplug_callback * next = (* current )-> next ;
969+ free (* current );
970+ * current = next ;
971+ }
972+ hid_internal_hotplug_cleanup ();
973+ pthread_mutex_unlock (& hid_hotplug_context .mutex );
974+ hid_hotplug_context .mutex_ready = 0 ;
975+ pthread_mutex_destroy (& hid_hotplug_context .mutex );
976+ }
977+
883978int HID_API_EXPORT hid_init (void )
884979{
885980 const char * locale ;
@@ -895,14 +990,22 @@ int HID_API_EXPORT hid_init(void)
895990 return 0 ;
896991}
897992
993+
898994int HID_API_EXPORT hid_exit (void )
899995{
900996 /* Free global error message */
901997 register_global_error (NULL );
902998
999+ hid_internal_hotplug_exit ();
1000+
9031001 return 0 ;
9041002}
9051003
1004+ 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 )
1005+ {
1006+ return (expected_vendor_id == 0x0 || vendor_id == expected_vendor_id ) && (expected_product_id == 0x0 || product_id == expected_product_id );
1007+ }
1008+
9061009struct hid_device_info HID_API_EXPORT * hid_enumerate (unsigned short vendor_id , unsigned short product_id )
9071010{
9081011 struct udev * udev ;
@@ -1004,26 +1107,224 @@ void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
10041107 }
10051108}
10061109
1110+ static void hid_internal_invoke_callbacks (struct hid_device_info * info , hid_hotplug_event event )
1111+ {
1112+ struct hid_hotplug_callback * * current = & hid_hotplug_context .hotplug_cbs ;
1113+ while (* current ) {
1114+ struct hid_hotplug_callback * callback = * current ;
1115+ if ((callback -> events & event ) && hid_internal_match_device_id (info -> vendor_id , info -> product_id ,
1116+ callback -> vendor_id , callback -> product_id )) {
1117+ int result = callback -> callback (callback -> handle , info , event , callback -> user_data );
1118+ /* If the result is non-zero, we remove the callback and proceed */
1119+ /* Do not use the deregister call as it locks the mutex, and we are currently in a lock */
1120+ if (result ) {
1121+ struct hid_hotplug_callback * callback = * current ;
1122+ * current = (* current )-> next ;
1123+ free (callback );
1124+ continue ;
1125+ }
1126+ }
1127+ current = & callback -> next ;
1128+ }
1129+ }
1130+
1131+ static int match_udev_to_info (struct udev_device * raw_dev , struct hid_device_info * info )
1132+ {
1133+ const char * path = udev_device_get_devnode (raw_dev );
1134+ if (!strcmp (path , info -> path )) {
1135+ return 1 ;
1136+ }
1137+ return 0 ;
1138+ }
1139+
1140+ static void * hotplug_thread (void * user_data )
1141+ {
1142+ (void ) user_data ;
1143+
1144+ while (hid_hotplug_context .monitor_fd > 0 ) {
1145+ fd_set fds ;
1146+ struct timeval tv ;
1147+ int ret ;
1148+
1149+ FD_ZERO (& fds );
1150+ FD_SET (hid_hotplug_context .monitor_fd , & fds );
1151+ /* 5 msec timeout seems reasonable; don't set too low to avoid high CPU usage */
1152+ /* This timeout only affects how much time it takes to stop the thread */
1153+ tv .tv_sec = 0 ;
1154+ tv .tv_usec = 5000 ;
1155+
1156+ ret = select (hid_hotplug_context .monitor_fd + 1 , & fds , NULL , NULL , & tv );
1157+
1158+ /* Check if our file descriptor has received data. */
1159+ if (ret > 0 && FD_ISSET (hid_hotplug_context .monitor_fd , & fds )) {
1160+
1161+ /* Make the call to receive the device.
1162+ select() ensured that this will not block. */
1163+ struct udev_device * raw_dev = udev_monitor_receive_device (hid_hotplug_context .mon );
1164+ if (raw_dev ) {
1165+ /* Lock the mutex so callback/device lists don't change elsewhere from here on */
1166+ pthread_mutex_lock (& hid_hotplug_context .mutex );
1167+
1168+ const char * action = udev_device_get_action (raw_dev );
1169+ if (!strcmp (action , "add" )) {
1170+ // We create a list of all usages on this UDEV device
1171+ struct hid_device_info * info = create_device_info_for_device (raw_dev );
1172+ struct hid_device_info * info_cur = info ;
1173+ while (info_cur ) {
1174+ /* For each device, call all matching callbacks */
1175+ /* TODO: possibly make the `next` field NULL to match the behavior on other systems */
1176+ hid_internal_invoke_callbacks (info_cur , HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED );
1177+ info_cur = info_cur -> next ;
1178+ }
1179+
1180+ /* Append all we got to the end of the device list */
1181+ if (info ) {
1182+ if (hid_hotplug_context .devs != NULL ) {
1183+ struct hid_device_info * last = hid_hotplug_context .devs ;
1184+ while (last -> next != NULL ) {
1185+ last = last -> next ;
1186+ }
1187+ last -> next = info ;
1188+ } else {
1189+ hid_hotplug_context .devs = info ;
1190+ }
1191+ }
1192+ } else if (!strcmp (action , "remove" )) {
1193+ for (struct hid_device_info * * current = & hid_hotplug_context .devs ; * current ;) {
1194+ struct hid_device_info * info = * current ;
1195+ if (match_udev_to_info (raw_dev , * current )) {
1196+ /* If the libusb device that's left matches this HID device, we detach it from the list */
1197+ * current = (* current )-> next ;
1198+ info -> next = NULL ;
1199+ hid_internal_invoke_callbacks (info , HID_API_HOTPLUG_EVENT_DEVICE_LEFT );
1200+ /* Free every removed device */
1201+ free (info );
1202+ } else {
1203+ current = & info -> next ;
1204+ }
1205+ }
1206+ }
1207+ udev_device_unref (raw_dev );
1208+ pthread_mutex_unlock (& hid_hotplug_context .mutex );
1209+ }
1210+ }
1211+ }
1212+ return NULL ;
1213+ }
1214+
10071215int 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 )
10081216{
1009- /* Stub */
1010- (void )vendor_id ;
1011- (void )product_id ;
1012- (void )events ;
1013- (void )flags ;
1014- (void )callback ;
1015- (void )user_data ;
1016- (void )callback_handle ;
1217+ struct hid_hotplug_callback * hotplug_cb ;
10171218
1018- return -1 ;
1219+ /* Check params */
1220+ if (events == 0
1221+ || (events & ~(HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED | HID_API_HOTPLUG_EVENT_DEVICE_LEFT ))
1222+ || (flags & ~(HID_API_HOTPLUG_ENUMERATE ))
1223+ || callback == NULL ) {
1224+ return -1 ;
1225+ }
1226+
1227+ hotplug_cb = (struct hid_hotplug_callback * )calloc (1 , sizeof (struct hid_hotplug_callback ));
1228+
1229+ if (hotplug_cb == NULL ) {
1230+ return -1 ;
1231+ }
1232+
1233+ /* Fill out the record */
1234+ hotplug_cb -> next = NULL ;
1235+ hotplug_cb -> vendor_id = vendor_id ;
1236+ hotplug_cb -> product_id = product_id ;
1237+ hotplug_cb -> events = events ;
1238+ hotplug_cb -> user_data = user_data ;
1239+ hotplug_cb -> callback = callback ;
1240+
1241+ /* Ensure we are ready to actually use the mutex */
1242+ hid_internal_hotplug_init ();
1243+
1244+ /* Lock the mutex to avoid race conditions */
1245+ pthread_mutex_lock (& hid_hotplug_context .mutex );
1246+
1247+ hotplug_cb -> handle = hid_hotplug_context .next_handle ++ ;
1248+
1249+ /* handle the unlikely case of handle overflow */
1250+ if (hid_hotplug_context .next_handle < 0 )
1251+ {
1252+ hid_hotplug_context .next_handle = 1 ;
1253+ }
1254+
1255+ /* Return allocated handle */
1256+ if (callback_handle != NULL ) {
1257+ * callback_handle = hotplug_cb -> handle ;
1258+ }
1259+
1260+ /* Append a new callback to the end */
1261+ if (hid_hotplug_context .hotplug_cbs != NULL ) {
1262+ struct hid_hotplug_callback * last = hid_hotplug_context .hotplug_cbs ;
1263+ while (last -> next != NULL ) {
1264+ last = last -> next ;
1265+ }
1266+ last -> next = hotplug_cb ;
1267+ }
1268+ else {
1269+ // Prepare a UDEV context to run monitoring on
1270+ hid_hotplug_context .udev_ctx = udev_new ();
1271+ if (!hid_hotplug_context .udev_ctx )
1272+ {
1273+ pthread_mutex_unlock (& hid_hotplug_context .mutex );
1274+ return -1 ;
1275+ }
1276+
1277+ hid_hotplug_context .mon = udev_monitor_new_from_netlink (hid_hotplug_context .udev_ctx , "udev" );
1278+ udev_monitor_filter_add_match_subsystem_devtype (hid_hotplug_context .mon , "hidraw" , NULL );
1279+ udev_monitor_enable_receiving (hid_hotplug_context .mon );
1280+ hid_hotplug_context .monitor_fd = udev_monitor_get_fd (hid_hotplug_context .mon );
1281+
1282+ /* After monitoring is all set up, enumerate all devices */
1283+ hid_hotplug_context .devs = hid_enumerate (0 , 0 );
1284+
1285+ /* Don't forget to actually register the callback */
1286+ hid_hotplug_context .hotplug_cbs = hotplug_cb ;
1287+
1288+ /* Start the thread that will be doing the event scanning */
1289+ pthread_create (& hid_hotplug_context .thread , NULL , & hotplug_thread , NULL );
1290+ }
1291+
1292+ pthread_mutex_unlock (& hid_hotplug_context .mutex );
1293+
1294+ return 0 ;
10191295}
10201296
10211297int HID_API_EXPORT HID_API_CALL hid_hotplug_deregister_callback (hid_hotplug_callback_handle callback_handle )
10221298{
1023- /* Stub */
1024- (void )callback_handle ;
1299+ if (!hid_hotplug_context .mutex_ready ) {
1300+ return -1 ;
1301+ }
10251302
1026- return -1 ;
1303+ pthread_mutex_lock (& hid_hotplug_context .mutex );
1304+
1305+ if (hid_hotplug_context .hotplug_cbs == NULL ) {
1306+ pthread_mutex_unlock (& hid_hotplug_context .mutex );
1307+ return -1 ;
1308+ }
1309+
1310+ int result = -1 ;
1311+
1312+ /* Remove this notification */
1313+ for (struct hid_hotplug_callback * * current = & hid_hotplug_context .hotplug_cbs ; * current != NULL ; current = & (* current )-> next ) {
1314+ if ((* current )-> handle == callback_handle ) {
1315+ struct hid_hotplug_callback * next = (* current )-> next ;
1316+ free (* current );
1317+ * current = next ;
1318+ result = 0 ;
1319+ break ;
1320+ }
1321+ }
1322+
1323+ hid_internal_hotplug_cleanup ();
1324+
1325+ pthread_mutex_unlock (& hid_hotplug_context .mutex );
1326+
1327+ return result ;
10271328}
10281329
10291330hid_device * hid_open (unsigned short vendor_id , unsigned short product_id , const wchar_t * serial_number )
@@ -1191,7 +1492,6 @@ int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
11911492 return 0 ; /* Success */
11921493}
11931494
1194-
11951495int HID_API_EXPORT hid_send_feature_report (hid_device * dev , const unsigned char * data , size_t length )
11961496{
11971497 int res ;
0 commit comments