]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libusb/libusb20_desc.c
Add two missing eventhandler.h headers
[FreeBSD/FreeBSD.git] / lib / libusb / libusb20_desc.c
1 /* $FreeBSD$ */
2 /*-
3  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4  *
5  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #ifdef LIBUSB_GLOBAL_INCLUDE_FILE
30 #include LIBUSB_GLOBAL_INCLUDE_FILE
31 #else
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36 #include <sys/queue.h>
37 #endif
38
39 #include "libusb20.h"
40 #include "libusb20_desc.h"
41 #include "libusb20_int.h"
42
43 static const uint32_t libusb20_me_encode_empty[2];      /* dummy */
44
45 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_DEVICE_DESC);
46 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_ENDPOINT_DESC);
47 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_DESC);
48 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONFIG_DESC);
49 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONTROL_SETUP);
50 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_ENDPT_COMP_DESC);
51 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_USB_20_DEVCAP_DESC);
52 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_USB_DEVCAP_DESC);
53 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_BOS_DESCRIPTOR);
54
55 /*------------------------------------------------------------------------*
56  *      libusb20_parse_config_desc
57  *
58  * Return values:
59  * NULL: Out of memory.
60  * Else: A valid config structure pointer which must be passed to "free()"
61  *------------------------------------------------------------------------*/
62 struct libusb20_config *
63 libusb20_parse_config_desc(const void *config_desc)
64 {
65         struct libusb20_config *lub_config;
66         struct libusb20_interface *lub_interface;
67         struct libusb20_interface *lub_alt_interface;
68         struct libusb20_interface *last_if;
69         struct libusb20_endpoint *lub_endpoint;
70         struct libusb20_endpoint *last_ep;
71
72         struct libusb20_me_struct pcdesc;
73         const uint8_t *ptr;
74         uint32_t size;
75         uint16_t niface_no_alt;
76         uint16_t niface;
77         uint16_t nendpoint;
78         uint16_t iface_no;
79
80         ptr = config_desc;
81         if (ptr[1] != LIBUSB20_DT_CONFIG) {
82                 return (NULL);          /* not config descriptor */
83         }
84         /*
85          * The first "bInterfaceNumber" should never have the value 0xff.
86          * Then it is corrupt.
87          */
88         niface_no_alt = 0;
89         nendpoint = 0;
90         niface = 0;
91         iface_no = 0xFFFF;
92         ptr = NULL;
93
94         /* get "wTotalLength" and setup "pcdesc" */
95         pcdesc.ptr = LIBUSB20_ADD_BYTES(config_desc, 0);
96         pcdesc.len =
97             ((const uint8_t *)config_desc)[2] |
98             (((const uint8_t *)config_desc)[3] << 8);
99         pcdesc.type = LIBUSB20_ME_IS_RAW;
100
101         /* descriptor pre-scan */
102         while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
103                 if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
104                         nendpoint++;
105                 } else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
106                         niface++;
107                         /* check "bInterfaceNumber" */
108                         if (ptr[2] != iface_no) {
109                                 iface_no = ptr[2];
110                                 niface_no_alt++;
111                         }
112                 }
113         }
114
115         /* sanity checking */
116         if (niface >= 256) {
117                 return (NULL);          /* corrupt */
118         }
119         if (nendpoint >= 256) {
120                 return (NULL);          /* corrupt */
121         }
122         size = sizeof(*lub_config) +
123             (niface * sizeof(*lub_interface)) +
124             (nendpoint * sizeof(*lub_endpoint)) +
125             pcdesc.len;
126
127         lub_config = malloc(size);
128         if (lub_config == NULL) {
129                 return (NULL);          /* out of memory */
130         }
131         /* make sure memory is initialised */
132         memset(lub_config, 0, size);
133
134         lub_interface = (void *)(lub_config + 1);
135         lub_alt_interface = (void *)(lub_interface + niface_no_alt);
136         lub_endpoint = (void *)(lub_interface + niface);
137
138         /*
139          * Make a copy of the config descriptor, so that the caller can free
140          * the initial config descriptor pointer!
141          */
142         memcpy((void *)(lub_endpoint + nendpoint), config_desc, pcdesc.len);
143
144         ptr = (const void *)(lub_endpoint + nendpoint);
145         pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0);
146
147         /* init config structure */
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                                                 (void) libusb20_me_encode(buf,
521                                                     0xFFFF - 3, ps->ptr);
522                                         } else {
523                                                 bcopy(src_ptr, buf, src_len);
524                                         }
525                                         buf += src_len;
526                                 }
527                                 pd_offset += sizeof(struct libusb20_me_struct);
528                         }
529                         break;
530
531                 default:
532                         goto done;
533                 }
534         }
535 done:
536         return (len_old - len);
537 }
538
539 /*------------------------------------------------------------------------*
540  *      libusb20_me_decode - decode a message into a decoded structure
541  *
542  * Description of parameters:
543  * "ptr" - message pointer
544  * "len" - message length
545  * "pd" - pointer to decoded structure
546  *
547  * Returns:
548  * "0..65535" - number of bytes decoded, limited by "len"
549  *------------------------------------------------------------------------*/
550 uint16_t
551 libusb20_me_decode(const void *ptr, uint16_t len, void *pd)
552 {
553         const uint8_t *pf;              /* pointer to format data */
554         const uint8_t *buf;             /* pointer to input buffer */
555
556         uint32_t pd_offset;             /* decoded structure offset */
557         uint16_t len_old;               /* old length */
558         uint16_t pd_count;              /* decoded element count */
559         uint8_t me;                     /* message element */
560
561         /* initialise */
562
563         len_old = len;
564         buf = ptr;
565         pd_offset = sizeof(void *);
566         pf = (*((struct libusb20_me_format **)pd))->format;
567
568         /* scan */
569
570         while (1) {
571
572                 /* get information element */
573
574                 me = (pf[0]) & LIBUSB20_ME_MASK;
575                 pd_count = pf[1] | (pf[2] << 8);
576                 pf += 3;
577
578                 /* decode the message element by type */
579
580                 switch (me) {
581                 case LIBUSB20_ME_INT8:
582                         while (pd_count--) {
583                                 uint8_t temp;
584
585                                 if (len < 1) {
586                                         len = 0;
587                                         temp = 0;
588                                 } else {
589                                         len -= 1;
590                                         temp = buf[0];
591                                         buf++;
592                                 }
593                                 *((uint8_t *)LIBUSB20_ADD_BYTES(pd,
594                                     pd_offset)) = temp;
595                                 pd_offset += 1;
596                         }
597                         break;
598
599                 case LIBUSB20_ME_INT16:
600                         pd_offset = -((-pd_offset) & ~1);       /* align */
601                         while (pd_count--) {
602                                 uint16_t temp;
603
604                                 if (len < 2) {
605                                         len = 0;
606                                         temp = 0;
607                                 } else {
608                                         len -= 2;
609                                         temp = buf[1] << 8;
610                                         temp |= buf[0];
611                                         buf += 2;
612                                 }
613                                 *((uint16_t *)LIBUSB20_ADD_BYTES(pd,
614                                     pd_offset)) = temp;
615                                 pd_offset += 2;
616                         }
617                         break;
618
619                 case LIBUSB20_ME_INT32:
620                         pd_offset = -((-pd_offset) & ~3);       /* align */
621                         while (pd_count--) {
622                                 uint32_t temp;
623
624                                 if (len < 4) {
625                                         len = 0;
626                                         temp = 0;
627                                 } else {
628                                         len -= 4;
629                                         temp = buf[3] << 24;
630                                         temp |= buf[2] << 16;
631                                         temp |= buf[1] << 8;
632                                         temp |= buf[0];
633                                         buf += 4;
634                                 }
635
636                                 *((uint32_t *)LIBUSB20_ADD_BYTES(pd,
637                                     pd_offset)) = temp;
638                                 pd_offset += 4;
639                         }
640                         break;
641
642                 case LIBUSB20_ME_INT64:
643                         pd_offset = -((-pd_offset) & ~7);       /* align */
644                         while (pd_count--) {
645                                 uint64_t temp;
646
647                                 if (len < 8) {
648                                         len = 0;
649                                         temp = 0;
650                                 } else {
651                                         len -= 8;
652                                         temp = ((uint64_t)buf[7]) << 56;
653                                         temp |= ((uint64_t)buf[6]) << 48;
654                                         temp |= ((uint64_t)buf[5]) << 40;
655                                         temp |= ((uint64_t)buf[4]) << 32;
656                                         temp |= buf[3] << 24;
657                                         temp |= buf[2] << 16;
658                                         temp |= buf[1] << 8;
659                                         temp |= buf[0];
660                                         buf += 8;
661                                 }
662
663                                 *((uint64_t *)LIBUSB20_ADD_BYTES(pd,
664                                     pd_offset)) = temp;
665                                 pd_offset += 8;
666                         }
667                         break;
668
669                 case LIBUSB20_ME_STRUCT:
670                         pd_offset = -((-pd_offset) &
671                             ~(LIBUSB20_ME_STRUCT_ALIGN - 1));   /* align */
672                         while (pd_count--) {
673                                 uint16_t temp;
674                                 struct libusb20_me_struct *ps;
675
676                                 ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
677
678                                 if (ps->type == LIBUSB20_ME_IS_ENCODED) {
679                                         /*
680                                          * Pre-store a de-constified
681                                          * pointer to the raw
682                                          * structure:
683                                          */
684                                         ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
685
686                                         /*
687                                          * Get the correct number of
688                                          * length bytes:
689                                          */
690                                         if (len != 0) {
691                                                 if (buf[0] == 0xFF) {
692                                                         ps->len = 3;
693                                                 } else {
694                                                         ps->len = 1;
695                                                 }
696                                         } else {
697                                                 ps->len = 0;
698                                         }
699                                 }
700                                 /* get the structure length */
701
702                                 if (len != 0) {
703                                         if (buf[0] == 0xFF) {
704                                                 if (len < 3) {
705                                                         len = 0;
706                                                         temp = 0;
707                                                 } else {
708                                                         len -= 3;
709                                                         temp = buf[1] |
710                                                             (buf[2] << 8);
711                                                         buf += 3;
712                                                 }
713                                         } else {
714                                                 len -= 1;
715                                                 temp = buf[0];
716                                                 buf += 1;
717                                         }
718                                 } else {
719                                         len = 0;
720                                         temp = 0;
721                                 }
722                                 /* check for invalid length */
723
724                                 if (temp > len) {
725                                         len = 0;
726                                         temp = 0;
727                                 }
728                                 /* check wanted structure type */
729
730                                 switch (ps->type) {
731                                 case LIBUSB20_ME_IS_ENCODED:
732                                         /* check for zero length */
733                                         if (temp == 0) {
734                                                 /*
735                                                  * The pointer must
736                                                  * be valid:
737                                                  */
738                                                 ps->ptr = LIBUSB20_ADD_BYTES(
739                                                     libusb20_me_encode_empty, 0);
740                                                 ps->len = 1;
741                                         } else {
742                                                 ps->len += temp;
743                                         }
744                                         break;
745
746                                 case LIBUSB20_ME_IS_RAW:
747                                         /* update length and pointer */
748                                         ps->len = temp;
749                                         ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
750                                         break;
751
752                                 case LIBUSB20_ME_IS_EMPTY:
753                                 case LIBUSB20_ME_IS_DECODED:
754                                         /* check for non-zero length */
755                                         if (temp != 0) {
756                                                 /* update type */
757                                                 ps->type = LIBUSB20_ME_IS_DECODED;
758                                                 ps->len = 0;
759                                                 /*
760                                                  * Recursivly decode
761                                                  * the next structure
762                                                  */
763                                                 (void) libusb20_me_decode(buf,
764                                                     temp, ps->ptr);
765                                         } else {
766                                                 /* update type */
767                                                 ps->type = LIBUSB20_ME_IS_EMPTY;
768                                                 ps->len = 0;
769                                         }
770                                         break;
771
772                                 default:
773                                         /*
774                                          * nothing to do - should
775                                          * not happen
776                                          */
777                                         ps->ptr = NULL;
778                                         ps->len = 0;
779                                         break;
780                                 }
781                                 buf += temp;
782                                 len -= temp;
783                                 pd_offset += sizeof(struct libusb20_me_struct);
784                         }
785                         break;
786
787                 default:
788                         goto done;
789                 }
790         }
791 done:
792         return (len_old - len);
793 }