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