@@ -123,6 +123,19 @@ static void register_global_error(const char *msg)
123123 last_global_error_str = utf8_to_wchar_t (msg );
124124}
125125
126+ /* See register_global_error, but you can pass a format string into this function. */
127+ static void register_global_error_format (const char * format , ...)
128+ {
129+ va_list args ;
130+ va_start (args , format );
131+
132+ char msg [100 ];
133+ vsnprintf (msg , sizeof (msg ), format , args );
134+
135+ va_end (args );
136+
137+ register_global_error (msg );
138+ }
126139
127140/* Set the last error for a device to be reported by hid_error(device).
128141 * The given error message will be copied (and decoded according to the
@@ -158,11 +171,67 @@ static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name)
158171 return utf8_to_wchar_t (udev_device_get_sysattr_value (dev , udev_name ));
159172}
160173
174+ /*
175+ * Gets the size of the HID item at the given position
176+ * Returns 1 if successful, 0 if an invalid key
177+ * Sets data_len and key_size when successful
178+ */
179+ static int get_hid_item_size (__u8 * report_descriptor , unsigned int pos , __u32 size , int * data_len , int * key_size )
180+ {
181+ int key = report_descriptor [pos ];
182+ int size_code ;
183+
184+ /*
185+ * This is a Long Item. The next byte contains the
186+ * length of the data section (value) for this key.
187+ * See the HID specification, version 1.11, section
188+ * 6.2.2.3, titled "Long Items."
189+ */
190+ if ((key & 0xf0 ) == 0xf0 ) {
191+ if (pos + 1 < size )
192+ {
193+ * data_len = report_descriptor [pos + 1 ];
194+ * key_size = 3 ;
195+ return 1 ;
196+ }
197+ * data_len = 0 ; /* malformed report */
198+ * key_size = 0 ;
199+ }
200+
201+ /*
202+ * This is a Short Item. The bottom two bits of the
203+ * key contain the size code for the data section
204+ * (value) for this key. Refer to the HID
205+ * specification, version 1.11, section 6.2.2.2,
206+ * titled "Short Items."
207+ */
208+ size_code = key & 0x3 ;
209+ switch (size_code ) {
210+ case 0 :
211+ case 1 :
212+ case 2 :
213+ * data_len = size_code ;
214+ * key_size = 1 ;
215+ return 1 ;
216+ case 3 :
217+ * data_len = 4 ;
218+ * key_size = 1 ;
219+ return 1 ;
220+ default :
221+ /* Can't ever happen since size_code is & 0x3 */
222+ * data_len = 0 ;
223+ * key_size = 0 ;
224+ break ;
225+ };
226+
227+ /* malformed report */
228+ return 0 ;
229+ }
230+
161231/* uses_numbered_reports() returns 1 if report_descriptor describes a device
162232 which contains numbered reports. */
163233static int uses_numbered_reports (__u8 * report_descriptor , __u32 size ) {
164234 unsigned int i = 0 ;
165- int size_code ;
166235 int data_len , key_size ;
167236
168237 while (i < size ) {
@@ -175,42 +244,9 @@ static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) {
175244 return 1 ;
176245 }
177246
178- //printf("key: %02hhx\n", key);
179-
180- if ((key & 0xf0 ) == 0xf0 ) {
181- /* This is a Long Item. The next byte contains the
182- length of the data section (value) for this key.
183- See the HID specification, version 1.11, section
184- 6.2.2.3, titled "Long Items." */
185- if (i + 1 < size )
186- data_len = report_descriptor [i + 1 ];
187- else
188- data_len = 0 ; /* malformed report */
189- key_size = 3 ;
190- }
191- else {
192- /* This is a Short Item. The bottom two bits of the
193- key contain the size code for the data section
194- (value) for this key. Refer to the HID
195- specification, version 1.11, section 6.2.2.2,
196- titled "Short Items." */
197- size_code = key & 0x3 ;
198- switch (size_code ) {
199- case 0 :
200- case 1 :
201- case 2 :
202- data_len = size_code ;
203- break ;
204- case 3 :
205- data_len = 4 ;
206- break ;
207- default :
208- /* Can't ever happen since size_code is & 0x3 */
209- data_len = 0 ;
210- break ;
211- };
212- key_size = 1 ;
213- }
247+ /* Determine data_len and key_size */
248+ if (!get_hid_item_size (report_descriptor , i , size , & data_len , & key_size ))
249+ return 0 ; /* malformed report */
214250
215251 /* Skip over this key and it's associated data */
216252 i += data_len + key_size ;
@@ -220,6 +256,157 @@ static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) {
220256 return 0 ;
221257}
222258
259+ /*
260+ * Get bytes from a HID Report Descriptor.
261+ * Only call with a num_bytes of 0, 1, 2, or 4.
262+ */
263+ static __u32 get_hid_report_bytes (__u8 * rpt , size_t len , size_t num_bytes , size_t cur )
264+ {
265+ /* Return if there aren't enough bytes. */
266+ if (cur + num_bytes >= len )
267+ return 0 ;
268+
269+ if (num_bytes == 0 )
270+ return 0 ;
271+ else if (num_bytes == 1 )
272+ return rpt [cur + 1 ];
273+ else if (num_bytes == 2 )
274+ return (rpt [cur + 2 ] * 256 + rpt [cur + 1 ]);
275+ else if (num_bytes == 4 )
276+ return (
277+ rpt [cur + 4 ] * 0x01000000 +
278+ rpt [cur + 3 ] * 0x00010000 +
279+ rpt [cur + 2 ] * 0x00000100 +
280+ rpt [cur + 1 ] * 0x00000001
281+ );
282+ else
283+ return 0 ;
284+ }
285+
286+ /*
287+ * Retrieves the device's Usage Page and Usage from the report descriptor.
288+ * The algorithm returns the current Usage Page/Usage pair whenever a new
289+ * Collection is found and a Usage Local Item is currently in scope.
290+ * Usage Local Items are consumed by each Main Item (See. 6.2.2.8).
291+ * The algorithm should give similar results as Apple's:
292+ * https://developer.apple.com/documentation/iokit/kiohiddeviceusagepairskey?language=objc
293+ * Physical Collections are also matched (macOS does the same).
294+ *
295+ * This function can be called repeatedly until it returns non-0
296+ * Usage is found. pos is the starting point (initially 0) and will be updated
297+ * to the next search position.
298+ *
299+ * The return value is 0 when a pair is found.
300+ * 1 when finished processing descriptor.
301+ * -1 on a malformed report.
302+ */
303+ static int get_next_hid_usage (__u8 * report_descriptor , __u32 size , unsigned int * pos , unsigned short * usage_page , unsigned short * usage )
304+ {
305+ int data_len , key_size ;
306+ int initial = * pos == 0 ; /* Used to handle case where no top-level application collection is defined */
307+ int usage_pair_ready = 0 ;
308+
309+ /* Usage is a Local Item, it must be set before each Main Item (Collection) before a pair is returned */
310+ int usage_found = 0 ;
311+
312+ while (* pos < size ) {
313+ int key = report_descriptor [* pos ];
314+ int key_cmd = key & 0xfc ;
315+
316+ /* Determine data_len and key_size */
317+ if (!get_hid_item_size (report_descriptor , * pos , size , & data_len , & key_size ))
318+ return -1 ; /* malformed report */
319+
320+ switch (key_cmd ) {
321+ case 0x4 : /* Usage Page 6.2.2.7 (Global) */
322+ * usage_page = get_hid_report_bytes (report_descriptor , size , data_len , * pos );
323+ break ;
324+
325+ case 0x8 : /* Usage 6.2.2.8 (Local) */
326+ * usage = get_hid_report_bytes (report_descriptor , size , data_len , * pos );
327+ usage_found = 1 ;
328+ break ;
329+
330+ case 0xa0 : /* Collection 6.2.2.4 (Main) */
331+ /* A Usage Item (Local) must be found for the pair to be valid */
332+ if (usage_found )
333+ usage_pair_ready = 1 ;
334+
335+ /* Usage is a Local Item, unset it */
336+ usage_found = 0 ;
337+ break ;
338+
339+ case 0x80 : /* Input 6.2.2.4 (Main) */
340+ case 0x90 : /* Output 6.2.2.4 (Main) */
341+ case 0xb0 : /* Feature 6.2.2.4 (Main) */
342+ case 0xc0 : /* End Collection 6.2.2.4 (Main) */
343+ /* Usage is a Local Item, unset it */
344+ usage_found = 0 ;
345+ break ;
346+ }
347+
348+ /* Skip over this key and it's associated data */
349+ * pos += data_len + key_size ;
350+
351+ /* Return usage pair */
352+ if (usage_pair_ready )
353+ return 0 ;
354+ }
355+
356+ /* If no top-level application collection is found and usage page/usage pair is found, pair is valid
357+ https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/top-level-collections */
358+ if (initial && usage_found )
359+ return 0 ; /* success */
360+
361+ return 1 ; /* finished processing */
362+ }
363+
364+ /*
365+ * Retrieves the hidraw report descriptor from a file.
366+ * When using this form, <sysfs_path>/device/report_descriptor, elevated priviledges are not required.
367+ */
368+ static int get_hid_report_descriptor (const char * rpt_path , struct hidraw_report_descriptor * rpt_desc )
369+ {
370+ int rpt_handle ;
371+ ssize_t res ;
372+
373+ rpt_handle = open (rpt_path , O_RDONLY );
374+ if (rpt_handle < 0 ) {
375+ register_global_error_format ("open failed (%s): %s" , rpt_path , strerror (errno ));
376+ return -1 ;
377+ }
378+
379+ /*
380+ * Read in the Report Descriptor
381+ * The sysfs file has a maximum size of 4096 (which is the same as HID_MAX_DESCRIPTOR_SIZE) so we should always
382+ * be ok when reading the descriptor.
383+ * In practice if the HID descriptor is any larger I suspect many other things will break.
384+ */
385+ memset (rpt_desc , 0x0 , sizeof (* rpt_desc ));
386+ res = read (rpt_handle , rpt_desc -> value , HID_MAX_DESCRIPTOR_SIZE );
387+ if (res < 0 ) {
388+ register_global_error_format ("read failed (%s): %s" , rpt_path , strerror (errno ));
389+ }
390+ rpt_desc -> size = (__u32 ) res ;
391+
392+ close (rpt_handle );
393+ return (int ) res ;
394+ }
395+
396+ static int get_hid_report_descriptor_from_sysfs (const char * sysfs_path , struct hidraw_report_descriptor * rpt_desc )
397+ {
398+ int res = -1 ;
399+ /* Construct <sysfs_path>/device/report_descriptor */
400+ size_t rpt_path_len = strlen (sysfs_path ) + 25 + 1 ;
401+ char * rpt_path = (char * ) calloc (1 , rpt_path_len );
402+ snprintf (rpt_path , rpt_path_len , "%s/device/report_descriptor" , sysfs_path );
403+
404+ res = get_hid_report_descriptor (rpt_path , rpt_desc );
405+ free (rpt_path );
406+
407+ return res ;
408+ }
409+
223410/*
224411 * The caller is responsible for free()ing the (newly-allocated) character
225412 * strings pointed to by serial_number_utf8 and product_name_utf8 after use.
@@ -451,6 +638,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
451638 char * product_name_utf8 = NULL ;
452639 int bus_type ;
453640 int result ;
641+ struct hidraw_report_descriptor report_desc ;
454642
455643 /* Get the filename of the /sys entry for the device
456644 and create a udev_device object (dev) representing it */
@@ -582,6 +770,45 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
582770 * check for USB and Bluetooth devices above */
583771 break ;
584772 }
773+
774+ /* Usage Page and Usage */
775+ result = get_hid_report_descriptor_from_sysfs (sysfs_path , & report_desc );
776+ if (result >= 0 ) {
777+ unsigned short page = 0 , usage = 0 ;
778+ unsigned int pos = 0 ;
779+ /*
780+ * Parse the first usage and usage page
781+ * out of the report descriptor.
782+ */
783+ if (!get_next_hid_usage (report_desc .value , report_desc .size , & pos , & page , & usage )) {
784+ cur_dev -> usage_page = page ;
785+ cur_dev -> usage = usage ;
786+ }
787+
788+ /*
789+ * Parse any additional usage and usage pages
790+ * out of the report descriptor.
791+ */
792+ while (!get_next_hid_usage (report_desc .value , report_desc .size , & pos , & page , & usage )) {
793+ /* Create new record for additional usage pairs */
794+ tmp = (struct hid_device_info * ) calloc (1 , sizeof (struct hid_device_info ));
795+ cur_dev -> next = tmp ;
796+ prev_dev = cur_dev ;
797+ cur_dev = tmp ;
798+
799+ /* Update fields */
800+ cur_dev -> path = strdup (dev_path );
801+ cur_dev -> vendor_id = dev_vid ;
802+ cur_dev -> product_id = dev_pid ;
803+ cur_dev -> serial_number = prev_dev -> serial_number ? wcsdup (prev_dev -> serial_number ): NULL ;
804+ cur_dev -> release_number = prev_dev -> release_number ;
805+ cur_dev -> interface_number = prev_dev -> interface_number ;
806+ cur_dev -> manufacturer_string = prev_dev -> manufacturer_string ? wcsdup (prev_dev -> manufacturer_string ): NULL ;
807+ cur_dev -> product_string = prev_dev -> product_string ? wcsdup (prev_dev -> product_string ): NULL ;
808+ cur_dev -> usage_page = page ;
809+ cur_dev -> usage = usage ;
810+ }
811+ }
585812 }
586813
587814 next :
0 commit comments