@@ -192,7 +192,7 @@ static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name)
192192 * Returns 1 if successful, 0 if an invalid key
193193 * Sets data_len and key_size when successful
194194 */
195- static int get_hid_item_size (__u8 * report_descriptor , unsigned int pos , __u32 size , int * data_len , int * key_size )
195+ static int get_hid_item_size (const __u8 * report_descriptor , __u32 size , unsigned int pos , int * data_len , int * key_size )
196196{
197197 int key = report_descriptor [pos ];
198198 int size_code ;
@@ -248,7 +248,7 @@ static int get_hid_item_size(__u8 *report_descriptor, unsigned int pos, __u32 si
248248 * Get bytes from a HID Report Descriptor.
249249 * Only call with a num_bytes of 0, 1, 2, or 4.
250250 */
251- static __u32 get_hid_report_bytes (__u8 * rpt , size_t len , size_t num_bytes , size_t cur )
251+ static __u32 get_hid_report_bytes (const __u8 * rpt , size_t len , size_t num_bytes , size_t cur )
252252{
253253 /* Return if there aren't enough bytes. */
254254 if (cur + num_bytes >= len )
@@ -271,6 +271,60 @@ static __u32 get_hid_report_bytes(__u8 *rpt, size_t len, size_t num_bytes, size_
271271 return 0 ;
272272}
273273
274+ /*
275+ * Iterates until the end of a Collection.
276+ * Assumes that *pos is exactly at the beginning of a Collection.
277+ * Skips all nested Collection, i.e. iterates until the end of current level Collection.
278+ *
279+ * The return value is non-0 when an end of current Collection is found,
280+ * 0 when error is occured (broken Descriptor, end of a Collection is found before its begin,
281+ * or no Collection is found at all).
282+ */
283+ static int hid_iterate_over_collection (const __u8 * report_descriptor , __u32 size , unsigned int * pos , int * data_len , int * key_size )
284+ {
285+ int collection_level = 0 ;
286+
287+ while (* pos < size ) {
288+ int key = report_descriptor [* pos ];
289+ int key_cmd = key & 0xfc ;
290+
291+ /* Determine data_len and key_size */
292+ if (!get_hid_item_size (report_descriptor , size , * pos , data_len , key_size ))
293+ return 0 ; /* malformed report */
294+
295+ switch (key_cmd ) {
296+ case 0xa0 : /* Collection 6.2.2.4 (Main) */
297+ collection_level ++ ;
298+ break ;
299+ case 0xc0 : /* End Collection 6.2.2.4 (Main) */
300+ collection_level -- ;
301+ break ;
302+ }
303+
304+ if (collection_level < 0 ) {
305+ /* Broken descriptor or someone is using this function wrong,
306+ * i.e. should be called exactly at the collection start */
307+ return 0 ;
308+ }
309+
310+ if (collection_level == 0 ) {
311+ /* Found it!
312+ * Also possible when called not at the collection start, but should not happen if used correctly */
313+ return 1 ;
314+ }
315+
316+ * pos += * data_len + * key_size ;
317+ }
318+
319+ return 0 ; /* Did not find the end of a Collection */
320+ }
321+
322+ struct hid_usage_iterator {
323+ unsigned int pos ;
324+ int usage_page_found ;
325+ unsigned short usage_page ;
326+ };
327+
274328/*
275329 * Retrieves the device's Usage Page and Usage from the report descriptor.
276330 * The algorithm returns the current Usage Page/Usage pair whenever a new
@@ -288,63 +342,64 @@ static __u32 get_hid_report_bytes(__u8 *rpt, size_t len, size_t num_bytes, size_
288342 * 1 when finished processing descriptor.
289343 * -1 on a malformed report.
290344 */
291- static int get_next_hid_usage (__u8 * report_descriptor , __u32 size , unsigned int * pos , unsigned short * usage_page , unsigned short * usage )
345+ static int get_next_hid_usage (const __u8 * report_descriptor , __u32 size , struct hid_usage_iterator * ctx , unsigned short * usage_page , unsigned short * usage )
292346{
293347 int data_len , key_size ;
294- int initial = * pos == 0 ; /* Used to handle case where no top-level application collection is defined */
295- int usage_pair_ready = 0 ;
348+ int initial = ctx -> pos == 0 ; /* Used to handle case where no top-level application collection is defined */
296349
297- /* Usage is a Local Item, it must be set before each Main Item (Collection) before a pair is returned */
298350 int usage_found = 0 ;
299351
300- while (* pos < size ) {
301- int key = report_descriptor [* pos ];
352+ while (ctx -> pos < size ) {
353+ int key = report_descriptor [ctx -> pos ];
302354 int key_cmd = key & 0xfc ;
303355
304356 /* Determine data_len and key_size */
305- if (!get_hid_item_size (report_descriptor , * pos , size , & data_len , & key_size ))
357+ if (!get_hid_item_size (report_descriptor , size , ctx -> pos , & data_len , & key_size ))
306358 return -1 ; /* malformed report */
307359
308360 switch (key_cmd ) {
309361 case 0x4 : /* Usage Page 6.2.2.7 (Global) */
310- * usage_page = get_hid_report_bytes (report_descriptor , size , data_len , * pos );
362+ ctx -> usage_page = get_hid_report_bytes (report_descriptor , size , data_len , ctx -> pos );
363+ ctx -> usage_page_found = 1 ;
311364 break ;
312365
313366 case 0x8 : /* Usage 6.2.2.8 (Local) */
314- * usage = get_hid_report_bytes (report_descriptor , size , data_len , * pos );
315- usage_found = 1 ;
367+ if (data_len == 4 ) { /* Usages 5.5 / Usage Page 6.2.2.7 */
368+ ctx -> usage_page = get_hid_report_bytes (report_descriptor , size , 2 , ctx -> pos + 2 );
369+ ctx -> usage_page_found = 1 ;
370+ * usage = get_hid_report_bytes (report_descriptor , size , 2 , ctx -> pos );
371+ usage_found = 1 ;
372+ }
373+ else {
374+ * usage = get_hid_report_bytes (report_descriptor , size , data_len , ctx -> pos );
375+ usage_found = 1 ;
376+ }
316377 break ;
317378
318379 case 0xa0 : /* Collection 6.2.2.4 (Main) */
319- /* A Usage Item (Local) must be found for the pair to be valid */
320- if ( usage_found )
321- usage_pair_ready = 1 ;
380+ if (! hid_iterate_over_collection ( report_descriptor , size , & ctx -> pos , & data_len , & key_size )) {
381+ return -1 ;
382+ }
322383
323- /* Usage is a Local Item, unset it */
324- usage_found = 0 ;
325- break ;
384+ /* A pair is valid - to be reported when Collection is found */
385+ if (usage_found && ctx -> usage_page_found ) {
386+ * usage_page = ctx -> usage_page ;
387+ return 0 ;
388+ }
326389
327- case 0x80 : /* Input 6.2.2.4 (Main) */
328- case 0x90 : /* Output 6.2.2.4 (Main) */
329- case 0xb0 : /* Feature 6.2.2.4 (Main) */
330- case 0xc0 : /* End Collection 6.2.2.4 (Main) */
331- /* Usage is a Local Item, unset it */
332- usage_found = 0 ;
333390 break ;
334391 }
335392
336393 /* Skip over this key and its associated data */
337- * pos += data_len + key_size ;
338-
339- /* Return usage pair */
340- if (usage_pair_ready )
341- return 0 ;
394+ ctx -> pos += data_len + key_size ;
342395 }
343396
344397 /* If no top-level application collection is found and usage page/usage pair is found, pair is valid
345398 https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/top-level-collections */
346- if (initial && usage_found )
347- return 0 ; /* success */
399+ if (initial && usage_found && ctx -> usage_page_found ) {
400+ * usage_page = ctx -> usage_page ;
401+ return 0 ; /* success */
402+ }
348403
349404 return 1 ; /* finished processing */
350405}
@@ -727,12 +782,14 @@ static struct hid_device_info * create_device_info_for_device(struct udev_device
727782 result = get_hid_report_descriptor_from_sysfs (sysfs_path , & report_desc );
728783 if (result >= 0 ) {
729784 unsigned short page = 0 , usage = 0 ;
730- unsigned int pos = 0 ;
785+ struct hid_usage_iterator usage_iterator ;
786+ memset (& usage_iterator , 0 , sizeof (usage_iterator ));
787+
731788 /*
732789 * Parse the first usage and usage page
733790 * out of the report descriptor.
734791 */
735- if (!get_next_hid_usage (report_desc .value , report_desc .size , & pos , & page , & usage )) {
792+ if (!get_next_hid_usage (report_desc .value , report_desc .size , & usage_iterator , & page , & usage )) {
736793 cur_dev -> usage_page = page ;
737794 cur_dev -> usage = usage ;
738795 }
@@ -741,7 +798,7 @@ static struct hid_device_info * create_device_info_for_device(struct udev_device
741798 * Parse any additional usage and usage pages
742799 * out of the report descriptor.
743800 */
744- while (!get_next_hid_usage (report_desc .value , report_desc .size , & pos , & page , & usage )) {
801+ while (!get_next_hid_usage (report_desc .value , report_desc .size , & usage_iterator , & page , & usage )) {
745802 /* Create new record for additional usage pairs */
746803 struct hid_device_info * tmp = (struct hid_device_info * ) calloc (1 , sizeof (struct hid_device_info ));
747804 struct hid_device_info * prev_dev = cur_dev ;
0 commit comments