Sync libusb with FreeBSD.
[dragonfly.git] / lib / libusb / libusb20_desc.c
1 /* $FreeBSD: head/lib/libusb/libusb20_desc.c 248236 2013-03-13 12:23:14Z hselasky $ */
2 /*-
3  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #ifdef LIBUSB_GLOBAL_INCLUDE_FILE
28 #include LIBUSB_GLOBAL_INCLUDE_FILE
29 #else
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <time.h>
34 #include <sys/queue.h>
35 #endif
36
37 #include "libusb20.h"
38 #include "libusb20_desc.h"
39 #include "libusb20_int.h"
40
41 static const uint32_t libusb20_me_encode_empty[2];      /* dummy */
42
43 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_DEVICE_DESC);
44 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_ENDPOINT_DESC);
45 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_DESC);
46 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONFIG_DESC);
47 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONTROL_SETUP);
48 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_ENDPT_COMP_DESC);
49 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_USB_20_DEVCAP_DESC);
50 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_USB_DEVCAP_DESC);
51 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_BOS_DESCRIPTOR);
52
53 /*------------------------------------------------------------------------*
54  *      libusb20_parse_config_desc
55  *
56  * Return values:
57  * NULL: Out of memory.
58  * Else: A valid config structure pointer which must be passed to "free()"
59  *------------------------------------------------------------------------*/
60 struct libusb20_config *
61 libusb20_parse_config_desc(const void *config_desc)
62 {
63         struct libusb20_config *lub_config;
64         struct libusb20_interface *lub_interface;
65         struct libusb20_interface *lub_alt_interface;
66         struct libusb20_interface *last_if;
67         struct libusb20_endpoint *lub_endpoint;
68         struct libusb20_endpoint *last_ep;
69
70         struct libusb20_me_struct pcdesc;
71         const uint8_t *ptr;
72         uint32_t size;
73         uint16_t niface_no_alt;
74         uint16_t niface;
75         uint16_t nendpoint;
76         uint16_t iface_no;
77
78         ptr = config_desc;
79         if (ptr[1] != LIBUSB20_DT_CONFIG) {
80                 return (NULL);          /* not config descriptor */
81         }
82         /*
83          * The first "bInterfaceNumber" should never have the value 0xff.
84          * Then it is corrupt.
85          */
86         niface_no_alt = 0;
87         nendpoint = 0;
88         niface = 0;
89         iface_no = 0xFFFF;
90         ptr = NULL;
91
92         /* get "wTotalLength" and setup "pcdesc" */
93         pcdesc.ptr = LIBUSB20_ADD_BYTES(config_desc, 0);
94         pcdesc.len =
95             ((const uint8_t *)config_desc)[2] |
96             (((const uint8_t *)config_desc)[3] << 8);
97         pcdesc.type = LIBUSB20_ME_IS_RAW;
98
99         /* descriptor pre-scan */
100         while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
101                 if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
102                         nendpoint++;
103                 } else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
104                         niface++;
105                         /* check "bInterfaceNumber" */
106                         if (ptr[2] != iface_no) {
107                                 iface_no = ptr[2];
108                                 niface_no_alt++;
109                         }
110                 }
111         }
112
113         /* sanity checking */
114         if (niface >= 256) {
115                 return (NULL);          /* corrupt */
116         }
117         if (nendpoint >= 256) {
118                 return (NULL);          /* corrupt */
119         }
120         size = sizeof(*lub_config) +
121             (niface * sizeof(*lub_interface)) +
122             (nendpoint * sizeof(*lub_endpoint)) +
123             pcdesc.len;
124
125         lub_config = malloc(size);
126         if (lub_config == NULL) {
127                 return (NULL);          /* out of memory */
128         }
129         /* make sure memory is initialised */
130         memset(lub_config, 0, size);
131
132         lub_interface = (void *)(lub_config + 1);
133         lub_alt_interface = (void *)(lub_interface + niface_no_alt);
134         lub_endpoint = (void *)(lub_interface + niface);
135
136         /*
137          * Make a copy of the config descriptor, so that the caller can free
138          * the inital config descriptor pointer!
139          */
140         ptr = (void *)(lub_endpoint + nendpoint);
141         memcpy(LIBUSB20_ADD_BYTES(ptr, 0), config_desc, pcdesc.len);
142         pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0);
143         config_desc = LIBUSB20_ADD_BYTES(ptr, 0);
144
145         /* init config structure */
146
147         ptr = config_desc;
148
149         LIBUSB20_INIT(LIBUSB20_CONFIG_DESC, &lub_config->desc);
150
151         if (libusb20_me_decode(ptr, ptr[0], &lub_config->desc)) {
152                 /* ignore */
153         }
154         lub_config->num_interface = 0;
155         lub_config->interface = lub_interface;
156         lub_config->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
157         lub_config->extra.len = -ptr[0];
158         lub_config->extra.type = LIBUSB20_ME_IS_RAW;
159
160         /* reset states */
161         niface = 0;
162         iface_no = 0xFFFF;
163         ptr = NULL;
164         lub_interface--;
165         lub_endpoint--;
166         last_if = NULL;
167         last_ep = NULL;
168
169         /* descriptor pre-scan */
170         while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
171                 if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
172                         if (last_if) {
173                                 lub_endpoint++;
174                                 last_ep = lub_endpoint;
175                                 last_if->num_endpoints++;
176
177                                 LIBUSB20_INIT(LIBUSB20_ENDPOINT_DESC, &last_ep->desc);
178
179                                 if (libusb20_me_decode(ptr, ptr[0], &last_ep->desc)) {
180                                         /* ignore */
181                                 }
182                                 last_ep->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
183                                 last_ep->extra.len = 0;
184                                 last_ep->extra.type = LIBUSB20_ME_IS_RAW;
185                         } else {
186                                 lub_config->extra.len += ptr[0];
187                         }
188
189                 } else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
190                         if (ptr[2] != iface_no) {
191                                 /* new interface */
192                                 iface_no = ptr[2];
193                                 lub_interface++;
194                                 lub_config->num_interface++;
195                                 last_if = lub_interface;
196                                 niface++;
197                         } else {
198                                 /* one more alternate setting */
199                                 lub_interface->num_altsetting++;
200                                 last_if = lub_alt_interface;
201                                 lub_alt_interface++;
202                         }
203
204                         LIBUSB20_INIT(LIBUSB20_INTERFACE_DESC, &last_if->desc);
205
206                         if (libusb20_me_decode(ptr, ptr[0], &last_if->desc)) {
207                                 /* ignore */
208                         }
209                         /*
210                          * Sometimes USB devices have corrupt interface
211                          * descriptors and we need to overwrite the provided
212                          * interface number!
213                          */
214                         last_if->desc.bInterfaceNumber = niface - 1;
215                         last_if->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
216                         last_if->extra.len = 0;
217                         last_if->extra.type = LIBUSB20_ME_IS_RAW;
218                         last_if->endpoints = lub_endpoint + 1;
219                         last_if->altsetting = lub_alt_interface;
220                         last_if->num_altsetting = 0;
221                         last_if->num_endpoints = 0;
222                         last_ep = NULL;
223                 } else {
224                         /* unknown descriptor */
225                         if (last_if) {
226                                 if (last_ep) {
227                                         last_ep->extra.len += ptr[0];
228                                 } else {
229                                         last_if->extra.len += ptr[0];
230                                 }
231                         } else {
232                                 lub_config->extra.len += ptr[0];
233                         }
234                 }
235         }
236         return (lub_config);
237 }
238
239 /*------------------------------------------------------------------------*
240  *      libusb20_desc_foreach
241  *
242  * Safe traversal of USB descriptors.
243  *
244  * Return values:
245  * NULL: End of descriptors
246  * Else: Pointer to next descriptor
247  *------------------------------------------------------------------------*/
248 const uint8_t *
249 libusb20_desc_foreach(const struct libusb20_me_struct *pdesc,
250     const uint8_t *psubdesc)
251 {
252         const uint8_t *start;
253         const uint8_t *end;
254         const uint8_t *desc_next;
255
256         /* be NULL safe */
257         if (pdesc == NULL)
258                 return (NULL);
259
260         start = (const uint8_t *)pdesc->ptr;
261         end = LIBUSB20_ADD_BYTES(start, pdesc->len);
262
263         /* get start of next descriptor */
264         if (psubdesc == NULL)
265                 psubdesc = start;
266         else
267                 psubdesc = psubdesc + psubdesc[0];
268
269         /* check that the next USB descriptor is within the range */
270         if ((psubdesc < start) || (psubdesc >= end))
271                 return (NULL);          /* out of range, or EOD */
272
273         /* check start of the second next USB descriptor, if any */
274         desc_next = psubdesc + psubdesc[0];
275         if ((desc_next < start) || (desc_next > end))
276                 return (NULL);          /* out of range */
277
278         /* check minimum descriptor length */
279         if (psubdesc[0] < 3)
280                 return (NULL);          /* too short descriptor */
281
282         return (psubdesc);              /* return start of next descriptor */
283 }
284
285 /*------------------------------------------------------------------------*
286  *      libusb20_me_get_1 - safety wrapper to read out one byte
287  *------------------------------------------------------------------------*/
288 uint8_t
289 libusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset)
290 {
291         if (offset < ie->len) {
292                 return (*((uint8_t *)LIBUSB20_ADD_BYTES(ie->ptr, offset)));
293         }
294         return (0);
295 }
296
297 /*------------------------------------------------------------------------*
298  *      libusb20_me_get_2 - safety wrapper to read out one word
299  *------------------------------------------------------------------------*/
300 uint16_t
301 libusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset)
302 {
303         return (libusb20_me_get_1(ie, offset) |
304             (libusb20_me_get_1(ie, offset + 1) << 8));
305 }
306
307 /*------------------------------------------------------------------------*
308  *      libusb20_me_encode - encode a message structure
309  *
310  * Description of parameters:
311  * "len" - maximum length of output buffer
312  * "ptr" - pointer to output buffer. If NULL, no data will be written
313  * "pd" - source structure
314  *
315  * Return values:
316  * 0..65535 - Number of bytes used, limited by the "len" input parameter.
317  *------------------------------------------------------------------------*/
318 uint16_t
319 libusb20_me_encode(void *ptr, uint16_t len, const void *pd)
320 {
321         const uint8_t *pf;              /* pointer to format data */
322         uint8_t *buf;                   /* pointer to output buffer */
323
324         uint32_t pd_offset;             /* decoded structure offset */
325         uint16_t len_old;               /* old length */
326         uint16_t pd_count;              /* decoded element count */
327         uint8_t me;                     /* message element */
328
329         /* initialise */
330
331         len_old = len;
332         buf = ptr;
333         pd_offset = sizeof(void *);
334         pf = (*((struct libusb20_me_format *const *)pd))->format;
335
336         /* scan */
337
338         while (1) {
339
340                 /* get information element */
341
342                 me = (pf[0]) & LIBUSB20_ME_MASK;
343                 pd_count = pf[1] | (pf[2] << 8);
344                 pf += 3;
345
346                 /* encode the message element */
347
348                 switch (me) {
349                 case LIBUSB20_ME_INT8:
350                         while (pd_count--) {
351                                 uint8_t temp;
352
353                                 if (len < 1)    /* overflow */
354                                         goto done;
355                                 if (buf) {
356                                         temp = *((const uint8_t *)
357                                             LIBUSB20_ADD_BYTES(pd, pd_offset));
358                                         buf[0] = temp;
359                                         buf += 1;
360                                 }
361                                 pd_offset += 1;
362                                 len -= 1;
363                         }
364                         break;
365
366                 case LIBUSB20_ME_INT16:
367                         pd_offset = -((-pd_offset) & ~1);       /* align */
368                         while (pd_count--) {
369                                 uint16_t temp;
370
371                                 if (len < 2)    /* overflow */
372                                         goto done;
373
374                                 if (buf) {
375                                         temp = *((const uint16_t *)
376                                             LIBUSB20_ADD_BYTES(pd, pd_offset));
377                                         buf[1] = (temp >> 8) & 0xFF;
378                                         buf[0] = temp & 0xFF;
379                                         buf += 2;
380                                 }
381                                 pd_offset += 2;
382                                 len -= 2;
383                         }
384                         break;
385
386                 case LIBUSB20_ME_INT32:
387                         pd_offset = -((-pd_offset) & ~3);       /* align */
388                         while (pd_count--) {
389                                 uint32_t temp;
390
391                                 if (len < 4)    /* overflow */
392                                         goto done;
393                                 if (buf) {
394                                         temp = *((const uint32_t *)
395                                             LIBUSB20_ADD_BYTES(pd, pd_offset));
396                                         buf[3] = (temp >> 24) & 0xFF;
397                                         buf[2] = (temp >> 16) & 0xFF;
398                                         buf[1] = (temp >> 8) & 0xFF;
399                                         buf[0] = temp & 0xFF;
400                                         buf += 4;
401                                 }
402                                 pd_offset += 4;
403                                 len -= 4;
404                         }
405                         break;
406
407                 case LIBUSB20_ME_INT64:
408                         pd_offset = -((-pd_offset) & ~7);       /* align */
409                         while (pd_count--) {
410                                 uint64_t temp;
411
412                                 if (len < 8)    /* overflow */
413                                         goto done;
414                                 if (buf) {
415
416                                         temp = *((const uint64_t *)
417                                             LIBUSB20_ADD_BYTES(pd, pd_offset));
418                                         buf[7] = (temp >> 56) & 0xFF;
419                                         buf[6] = (temp >> 48) & 0xFF;
420                                         buf[5] = (temp >> 40) & 0xFF;
421                                         buf[4] = (temp >> 32) & 0xFF;
422                                         buf[3] = (temp >> 24) & 0xFF;
423                                         buf[2] = (temp >> 16) & 0xFF;
424                                         buf[1] = (temp >> 8) & 0xFF;
425                                         buf[0] = temp & 0xFF;
426                                         buf += 8;
427                                 }
428                                 pd_offset += 8;
429                                 len -= 8;
430                         }
431                         break;
432
433                 case LIBUSB20_ME_STRUCT:
434                         pd_offset = -((-pd_offset) &
435                             ~(LIBUSB20_ME_STRUCT_ALIGN - 1));   /* align */
436                         while (pd_count--) {
437                                 void *src_ptr;
438                                 uint16_t src_len;
439                                 struct libusb20_me_struct *ps;
440
441                                 ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
442
443                                 switch (ps->type) {
444                                 case LIBUSB20_ME_IS_RAW:
445                                         src_len = ps->len;
446                                         src_ptr = ps->ptr;
447                                         break;
448
449                                 case LIBUSB20_ME_IS_ENCODED:
450                                         if (ps->len == 0) {
451                                                 /*
452                                                  * Length is encoded
453                                                  * in the data itself
454                                                  * and should be
455                                                  * correct:
456                                                  */
457                                                 ps->len = 0xFFFF;
458                                         }
459                                         src_len = libusb20_me_get_1(pd, 0);
460                                         src_ptr = LIBUSB20_ADD_BYTES(ps->ptr, 1);
461                                         if (src_len == 0xFF) {
462                                                 /* length is escaped */
463                                                 src_len = libusb20_me_get_2(pd, 1);
464                                                 src_ptr =
465                                                     LIBUSB20_ADD_BYTES(ps->ptr, 3);
466                                         }
467                                         break;
468
469                                 case LIBUSB20_ME_IS_DECODED:
470                                         /* reserve 3 length bytes */
471                                         src_len = libusb20_me_encode(NULL,
472                                             0xFFFF - 3, ps->ptr);
473                                         src_ptr = NULL;
474                                         break;
475
476                                 default:        /* empty structure */
477                                         src_len = 0;
478                                         src_ptr = NULL;
479                                         break;
480                                 }
481
482                                 if (src_len > 0xFE) {
483                                         if (src_len > (0xFFFF - 3))
484                                                 /* overflow */
485                                                 goto done;
486
487                                         if (len < (src_len + 3))
488                                                 /* overflow */
489                                                 goto done;
490
491                                         if (buf) {
492                                                 buf[0] = 0xFF;
493                                                 buf[1] = (src_len & 0xFF);
494                                                 buf[2] = (src_len >> 8) & 0xFF;
495                                                 buf += 3;
496                                         }
497                                         len -= (src_len + 3);
498                                 } else {
499                                         if (len < (src_len + 1))
500                                                 /* overflow */
501                                                 goto done;
502
503                                         if (buf) {
504                                                 buf[0] = (src_len & 0xFF);
505                                                 buf += 1;
506                                         }
507                                         len -= (src_len + 1);
508                                 }
509
510                                 /* check for buffer and non-zero length */
511
512                                 if (buf && src_len) {
513                                         if (ps->type == LIBUSB20_ME_IS_DECODED) {
514                                                 /*
515                                                  * Repeat encode
516                                                  * procedure - we have
517                                                  * room for the
518                                                  * complete structure:
519                                                  */
520                                                 uint16_t dummy;
521
522                                                 dummy = libusb20_me_encode(buf,
523                                                     0xFFFF - 3, ps->ptr);
524                                         } else {
525                                                 bcopy(src_ptr, buf, src_len);
526                                         }
527                                         buf += src_len;
528                                 }
529                                 pd_offset += sizeof(struct libusb20_me_struct);
530                         }
531                         break;
532
533                 default:
534                         goto done;
535                 }
536         }
537 done:
538         return (len_old - len);
539 }
540
541 /*------------------------------------------------------------------------*
542  *      libusb20_me_decode - decode a message into a decoded structure
543  *
544  * Description of parameters:
545  * "ptr" - message pointer
546  * "len" - message length
547  * "pd" - pointer to decoded structure
548  *
549  * Returns:
550  * "0..65535" - number of bytes decoded, limited by "len"
551  *------------------------------------------------------------------------*/
552 uint16_t
553 libusb20_me_decode(const void *ptr, uint16_t len, void *pd)
554 {
555         const uint8_t *pf;              /* pointer to format data */
556         const uint8_t *buf;             /* pointer to input buffer */
557
558         uint32_t pd_offset;             /* decoded structure offset */
559         uint16_t len_old;               /* old length */
560         uint16_t pd_count;              /* decoded element count */
561         uint8_t me;                     /* message element */
562
563         /* initialise */
564
565         len_old = len;
566         buf = ptr;
567         pd_offset = sizeof(void *);
568         pf = (*((struct libusb20_me_format **)pd))->format;
569
570         /* scan */
571
572         while (1) {
573
574                 /* get information element */
575
576                 me = (pf[0]) & LIBUSB20_ME_MASK;
577                 pd_count = pf[1] | (pf[2] << 8);
578                 pf += 3;
579
580                 /* decode the message element by type */
581
582                 switch (me) {
583                 case LIBUSB20_ME_INT8:
584                         while (pd_count--) {
585                                 uint8_t temp;
586
587                                 if (len < 1) {
588                                         len = 0;
589                                         temp = 0;
590                                 } else {
591                                         len -= 1;
592                                         temp = buf[0];
593                                         buf++;
594                                 }
595                                 *((uint8_t *)LIBUSB20_ADD_BYTES(pd,
596                                     pd_offset)) = temp;
597                                 pd_offset += 1;
598                         }
599                         break;
600
601                 case LIBUSB20_ME_INT16:
602                         pd_offset = -((-pd_offset) & ~1);       /* align */
603                         while (pd_count--) {
604                                 uint16_t temp;
605
606                                 if (len < 2) {
607                                         len = 0;
608                                         temp = 0;
609                                 } else {
610                                         len -= 2;
611                                         temp = buf[1] << 8;
612                                         temp |= buf[0];
613                                         buf += 2;
614                                 }
615                                 *((uint16_t *)LIBUSB20_ADD_BYTES(pd,
616                                     pd_offset)) = temp;
617                                 pd_offset += 2;
618                         }
619                         break;
620
621                 case LIBUSB20_ME_INT32:
622                         pd_offset = -((-pd_offset) & ~3);       /* align */
623                         while (pd_count--) {
624                                 uint32_t temp;
625
626                                 if (len < 4) {
627                                         len = 0;
628                                         temp = 0;
629                                 } else {
630                                         len -= 4;
631                                         temp = buf[3] << 24;
632                                         temp |= buf[2] << 16;
633                                         temp |= buf[1] << 8;
634                                         temp |= buf[0];
635                                         buf += 4;
636                                 }
637
638                                 *((uint32_t *)LIBUSB20_ADD_BYTES(pd,
639                                     pd_offset)) = temp;
640                                 pd_offset += 4;
641                         }
642                         break;
643
644                 case LIBUSB20_ME_INT64:
645                         pd_offset = -((-pd_offset) & ~7);       /* align */
646                         while (pd_count--) {
647                                 uint64_t temp;
648
649                                 if (len < 8) {
650                                         len = 0;
651                                         temp = 0;
652                                 } else {
653                                         len -= 8;
654                                         temp = ((uint64_t)buf[7]) << 56;
655                                         temp |= ((uint64_t)buf[6]) << 48;
656                                         temp |= ((uint64_t)buf[5]) << 40;
657                                         temp |= ((uint64_t)buf[4]) << 32;
658                                         temp |= buf[3] << 24;
659                                         temp |= buf[2] << 16;
660                                         temp |= buf[1] << 8;
661                                         temp |= buf[0];
662                                         buf += 8;
663                                 }
664
665                                 *((uint64_t *)LIBUSB20_ADD_BYTES(pd,
666                                     pd_offset)) = temp;
667                                 pd_offset += 8;
668                         }
669                         break;
670
671                 case LIBUSB20_ME_STRUCT:
672                         pd_offset = -((-pd_offset) &
673                             ~(LIBUSB20_ME_STRUCT_ALIGN - 1));   /* align */
674                         while (pd_count--) {
675                                 uint16_t temp;
676                                 uint16_t dummy;
677                                 struct libusb20_me_struct *ps;
678
679                                 ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
680
681                                 if (ps->type == LIBUSB20_ME_IS_ENCODED) {
682                                         /*
683                                          * Pre-store a de-constified
684                                          * pointer to the raw
685                                          * structure:
686                                          */
687                                         ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
688
689                                         /*
690                                          * Get the correct number of
691                                          * length bytes:
692                                          */
693                                         if (len != 0) {
694                                                 if (buf[0] == 0xFF) {
695                                                         ps->len = 3;
696                                                 } else {
697                                                         ps->len = 1;
698                                                 }
699                                         } else {
700                                                 ps->len = 0;
701                                         }
702                                 }
703                                 /* get the structure length */
704
705                                 if (len != 0) {
706                                         if (buf[0] == 0xFF) {
707                                                 if (len < 3) {
708                                                         len = 0;
709                                                         temp = 0;
710                                                 } else {
711                                                         len -= 3;
712                                                         temp = buf[1] |
713                                                             (buf[2] << 8);
714                                                         buf += 3;
715                                                 }
716                                         } else {
717                                                 len -= 1;
718                                                 temp = buf[0];
719                                                 buf += 1;
720                                         }
721                                 } else {
722                                         len = 0;
723                                         temp = 0;
724                                 }
725                                 /* check for invalid length */
726
727                                 if (temp > len) {
728                                         len = 0;
729                                         temp = 0;
730                                 }
731                                 /* check wanted structure type */
732
733                                 switch (ps->type) {
734                                 case LIBUSB20_ME_IS_ENCODED:
735                                         /* check for zero length */
736                                         if (temp == 0) {
737                                                 /*
738                                                  * The pointer must
739                                                  * be valid:
740                                                  */
741                                                 ps->ptr = LIBUSB20_ADD_BYTES(
742                                                     libusb20_me_encode_empty, 0);
743                                                 ps->len = 1;
744                                         } else {
745                                                 ps->len += temp;
746                                         }
747                                         break;
748
749                                 case LIBUSB20_ME_IS_RAW:
750                                         /* update length and pointer */
751                                         ps->len = temp;
752                                         ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
753                                         break;
754
755                                 case LIBUSB20_ME_IS_EMPTY:
756                                 case LIBUSB20_ME_IS_DECODED:
757                                         /* check for non-zero length */
758                                         if (temp != 0) {
759                                                 /* update type */
760                                                 ps->type = LIBUSB20_ME_IS_DECODED;
761                                                 ps->len = 0;
762                                                 /*
763                                                  * Recursivly decode
764                                                  * the next structure
765                                                  */
766                                                 dummy = libusb20_me_decode(buf,
767                                                     temp, ps->ptr);
768                                         } else {
769                                                 /* update type */
770                                                 ps->type = LIBUSB20_ME_IS_EMPTY;
771                                                 ps->len = 0;
772                                         }
773                                         break;
774
775                                 default:
776                                         /*
777                                          * nothing to do - should
778                                          * not happen
779                                          */
780                                         ps->ptr = NULL;
781                                         ps->len = 0;
782                                         break;
783                                 }
784                                 buf += temp;
785                                 len -= temp;
786                                 pd_offset += sizeof(struct libusb20_me_struct);
787                         }
788                         break;
789
790                 default:
791                         goto done;
792                 }
793         }
794 done:
795         return (len_old - len);
796 }