]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libusb/libusb20_desc.c
OpenSSL: update to 3.0.11
[FreeBSD/FreeBSD.git] / lib / libusb / libusb20_desc.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #ifdef LIBUSB_GLOBAL_INCLUDE_FILE
29 #include LIBUSB_GLOBAL_INCLUDE_FILE
30 #else
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35 #include <sys/queue.h>
36 #endif
37
38 #include "libusb20.h"
39 #include "libusb20_desc.h"
40 #include "libusb20_int.h"
41
42 static const uint32_t libusb20_me_encode_empty[2];      /* dummy */
43
44 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_DEVICE_DESC);
45 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_ENDPOINT_DESC);
46 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_DESC);
47 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONFIG_DESC);
48 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONTROL_SETUP);
49 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_ENDPT_COMP_DESC);
50 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_USB_20_DEVCAP_DESC);
51 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_USB_DEVCAP_DESC);
52 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_BOS_DESCRIPTOR);
53
54 /*------------------------------------------------------------------------*
55  *      libusb20_parse_config_desc
56  *
57  * Return values:
58  * NULL: Out of memory.
59  * Else: A valid config structure pointer which must be passed to "free()"
60  *------------------------------------------------------------------------*/
61 struct libusb20_config *
62 libusb20_parse_config_desc(const void *config_desc)
63 {
64         struct libusb20_config *lub_config;
65         struct libusb20_interface *lub_interface;
66         struct libusb20_interface *lub_alt_interface;
67         struct libusb20_interface *last_if;
68         struct libusb20_endpoint *lub_endpoint;
69         struct libusb20_endpoint *last_ep;
70
71         struct libusb20_me_struct pcdesc;
72         const uint8_t *ptr;
73         uint32_t size;
74         uint16_t niface_no_alt;
75         uint16_t niface;
76         uint16_t nendpoint;
77         uint16_t iface_no;
78
79         ptr = config_desc;
80         if (ptr[1] != LIBUSB20_DT_CONFIG) {
81                 return (NULL);          /* not config descriptor */
82         }
83
84         /*
85          * The first "bInterfaceNumber" cannot start at 0xFFFF
86          * because the field is 8-bit.
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                         /* detect broken USB descriptors when USB debugging is enabled */
211                         if (last_if->desc.bInterfaceNumber != (uint8_t)(niface - 1)) {
212                                 const char *str = getenv("LIBUSB_DEBUG");
213                                 if (str != NULL && str[0] != '\0' && str[0] != '0') {
214                                         printf("LIBUSB_DEBUG: bInterfaceNumber(%u) is not sequential(%u)\n",
215                                             last_if->desc.bInterfaceNumber, niface - 1);
216                                 }
217                         }
218                         last_if->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
219                         last_if->extra.len = 0;
220                         last_if->extra.type = LIBUSB20_ME_IS_RAW;
221                         last_if->endpoints = lub_endpoint + 1;
222                         last_if->altsetting = lub_alt_interface;
223                         last_if->num_altsetting = 0;
224                         last_if->num_endpoints = 0;
225                         last_ep = NULL;
226                 } else {
227                         /* unknown descriptor */
228                         if (last_if) {
229                                 if (last_ep) {
230                                         last_ep->extra.len += ptr[0];
231                                 } else {
232                                         last_if->extra.len += ptr[0];
233                                 }
234                         } else {
235                                 lub_config->extra.len += ptr[0];
236                         }
237                 }
238         }
239         return (lub_config);
240 }
241
242 /*------------------------------------------------------------------------*
243  *      libusb20_desc_foreach
244  *
245  * Safe traversal of USB descriptors.
246  *
247  * Return values:
248  * NULL: End of descriptors
249  * Else: Pointer to next descriptor
250  *------------------------------------------------------------------------*/
251 const uint8_t *
252 libusb20_desc_foreach(const struct libusb20_me_struct *pdesc,
253     const uint8_t *psubdesc)
254 {
255         const uint8_t *start;
256         const uint8_t *end;
257         const uint8_t *desc_next;
258
259         /* be NULL safe */
260         if (pdesc == NULL)
261                 return (NULL);
262
263         start = (const uint8_t *)pdesc->ptr;
264         end = LIBUSB20_ADD_BYTES(start, pdesc->len);
265
266         /* get start of next descriptor */
267         if (psubdesc == NULL)
268                 psubdesc = start;
269         else
270                 psubdesc = psubdesc + psubdesc[0];
271
272         /* check that the next USB descriptor is within the range */
273         if ((psubdesc < start) || (psubdesc >= end))
274                 return (NULL);          /* out of range, or EOD */
275
276         /* check start of the second next USB descriptor, if any */
277         desc_next = psubdesc + psubdesc[0];
278         if ((desc_next < start) || (desc_next > end))
279                 return (NULL);          /* out of range */
280
281         /* check minimum descriptor length */
282         if (psubdesc[0] < 3)
283                 return (NULL);          /* too short descriptor */
284
285         return (psubdesc);              /* return start of next descriptor */
286 }
287
288 /*------------------------------------------------------------------------*
289  *      libusb20_me_get_1 - safety wrapper to read out one byte
290  *------------------------------------------------------------------------*/
291 uint8_t
292 libusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset)
293 {
294         if (offset < ie->len) {
295                 return (*((uint8_t *)LIBUSB20_ADD_BYTES(ie->ptr, offset)));
296         }
297         return (0);
298 }
299
300 /*------------------------------------------------------------------------*
301  *      libusb20_me_get_2 - safety wrapper to read out one word
302  *------------------------------------------------------------------------*/
303 uint16_t
304 libusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset)
305 {
306         return (libusb20_me_get_1(ie, offset) |
307             (libusb20_me_get_1(ie, offset + 1) << 8));
308 }
309
310 /*------------------------------------------------------------------------*
311  *      libusb20_me_encode - encode a message structure
312  *
313  * Description of parameters:
314  * "len" - maximum length of output buffer
315  * "ptr" - pointer to output buffer. If NULL, no data will be written
316  * "pd" - source structure
317  *
318  * Return values:
319  * 0..65535 - Number of bytes used, limited by the "len" input parameter.
320  *------------------------------------------------------------------------*/
321 uint16_t
322 libusb20_me_encode(void *ptr, uint16_t len, const void *pd)
323 {
324         const uint8_t *pf;              /* pointer to format data */
325         uint8_t *buf;                   /* pointer to output buffer */
326
327         uint32_t pd_offset;             /* decoded structure offset */
328         uint16_t len_old;               /* old length */
329         uint16_t pd_count;              /* decoded element count */
330         uint8_t me;                     /* message element */
331
332         /* initialise */
333
334         len_old = len;
335         buf = ptr;
336         pd_offset = sizeof(void *);
337         pf = (*((struct libusb20_me_format *const *)pd))->format;
338
339         /* scan */
340
341         while (1) {
342
343                 /* get information element */
344
345                 me = (pf[0]) & LIBUSB20_ME_MASK;
346                 pd_count = pf[1] | (pf[2] << 8);
347                 pf += 3;
348
349                 /* encode the message element */
350
351                 switch (me) {
352                 case LIBUSB20_ME_INT8:
353                         while (pd_count--) {
354                                 uint8_t temp;
355
356                                 if (len < 1)    /* overflow */
357                                         goto done;
358                                 if (buf) {
359                                         temp = *((const uint8_t *)
360                                             LIBUSB20_ADD_BYTES(pd, pd_offset));
361                                         buf[0] = temp;
362                                         buf += 1;
363                                 }
364                                 pd_offset += 1;
365                                 len -= 1;
366                         }
367                         break;
368
369                 case LIBUSB20_ME_INT16:
370                         pd_offset = -((-pd_offset) & ~1);       /* align */
371                         while (pd_count--) {
372                                 uint16_t temp;
373
374                                 if (len < 2)    /* overflow */
375                                         goto done;
376
377                                 if (buf) {
378                                         temp = *((const uint16_t *)
379                                             LIBUSB20_ADD_BYTES(pd, pd_offset));
380                                         buf[1] = (temp >> 8) & 0xFF;
381                                         buf[0] = temp & 0xFF;
382                                         buf += 2;
383                                 }
384                                 pd_offset += 2;
385                                 len -= 2;
386                         }
387                         break;
388
389                 case LIBUSB20_ME_INT32:
390                         pd_offset = -((-pd_offset) & ~3);       /* align */
391                         while (pd_count--) {
392                                 uint32_t temp;
393
394                                 if (len < 4)    /* overflow */
395                                         goto done;
396                                 if (buf) {
397                                         temp = *((const uint32_t *)
398                                             LIBUSB20_ADD_BYTES(pd, pd_offset));
399                                         buf[3] = (temp >> 24) & 0xFF;
400                                         buf[2] = (temp >> 16) & 0xFF;
401                                         buf[1] = (temp >> 8) & 0xFF;
402                                         buf[0] = temp & 0xFF;
403                                         buf += 4;
404                                 }
405                                 pd_offset += 4;
406                                 len -= 4;
407                         }
408                         break;
409
410                 case LIBUSB20_ME_INT64:
411                         pd_offset = -((-pd_offset) & ~7);       /* align */
412                         while (pd_count--) {
413                                 uint64_t temp;
414
415                                 if (len < 8)    /* overflow */
416                                         goto done;
417                                 if (buf) {
418
419                                         temp = *((const uint64_t *)
420                                             LIBUSB20_ADD_BYTES(pd, pd_offset));
421                                         buf[7] = (temp >> 56) & 0xFF;
422                                         buf[6] = (temp >> 48) & 0xFF;
423                                         buf[5] = (temp >> 40) & 0xFF;
424                                         buf[4] = (temp >> 32) & 0xFF;
425                                         buf[3] = (temp >> 24) & 0xFF;
426                                         buf[2] = (temp >> 16) & 0xFF;
427                                         buf[1] = (temp >> 8) & 0xFF;
428                                         buf[0] = temp & 0xFF;
429                                         buf += 8;
430                                 }
431                                 pd_offset += 8;
432                                 len -= 8;
433                         }
434                         break;
435
436                 case LIBUSB20_ME_STRUCT:
437                         pd_offset = -((-pd_offset) &
438                             ~(LIBUSB20_ME_STRUCT_ALIGN - 1));   /* align */
439                         while (pd_count--) {
440                                 void *src_ptr;
441                                 uint16_t src_len;
442                                 struct libusb20_me_struct *ps;
443
444                                 ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
445
446                                 switch (ps->type) {
447                                 case LIBUSB20_ME_IS_RAW:
448                                         src_len = ps->len;
449                                         src_ptr = ps->ptr;
450                                         break;
451
452                                 case LIBUSB20_ME_IS_ENCODED:
453                                         if (ps->len == 0) {
454                                                 /*
455                                                  * Length is encoded
456                                                  * in the data itself
457                                                  * and should be
458                                                  * correct:
459                                                  */
460                                                 ps->len = 0xFFFF;
461                                         }
462                                         src_len = libusb20_me_get_1(pd, 0);
463                                         src_ptr = LIBUSB20_ADD_BYTES(ps->ptr, 1);
464                                         if (src_len == 0xFF) {
465                                                 /* length is escaped */
466                                                 src_len = libusb20_me_get_2(pd, 1);
467                                                 src_ptr =
468                                                     LIBUSB20_ADD_BYTES(ps->ptr, 3);
469                                         }
470                                         break;
471
472                                 case LIBUSB20_ME_IS_DECODED:
473                                         /* reserve 3 length bytes */
474                                         src_len = libusb20_me_encode(NULL,
475                                             0xFFFF - 3, ps->ptr);
476                                         src_ptr = NULL;
477                                         break;
478
479                                 default:        /* empty structure */
480                                         src_len = 0;
481                                         src_ptr = NULL;
482                                         break;
483                                 }
484
485                                 if (src_len > 0xFE) {
486                                         if (src_len > (0xFFFF - 3))
487                                                 /* overflow */
488                                                 goto done;
489
490                                         if (len < (src_len + 3))
491                                                 /* overflow */
492                                                 goto done;
493
494                                         if (buf) {
495                                                 buf[0] = 0xFF;
496                                                 buf[1] = (src_len & 0xFF);
497                                                 buf[2] = (src_len >> 8) & 0xFF;
498                                                 buf += 3;
499                                         }
500                                         len -= (src_len + 3);
501                                 } else {
502                                         if (len < (src_len + 1))
503                                                 /* overflow */
504                                                 goto done;
505
506                                         if (buf) {
507                                                 buf[0] = (src_len & 0xFF);
508                                                 buf += 1;
509                                         }
510                                         len -= (src_len + 1);
511                                 }
512
513                                 /* check for buffer and non-zero length */
514
515                                 if (buf && src_len) {
516                                         if (ps->type == LIBUSB20_ME_IS_DECODED) {
517                                                 /*
518                                                  * Repeat encode
519                                                  * procedure - we have
520                                                  * room for the
521                                                  * complete structure:
522                                                  */
523                                                 (void) libusb20_me_encode(buf,
524                                                     0xFFFF - 3, ps->ptr);
525                                         } else {
526                                                 bcopy(src_ptr, buf, src_len);
527                                         }
528                                         buf += src_len;
529                                 }
530                                 pd_offset += sizeof(struct libusb20_me_struct);
531                         }
532                         break;
533
534                 default:
535                         goto done;
536                 }
537         }
538 done:
539         return (len_old - len);
540 }
541
542 /*------------------------------------------------------------------------*
543  *      libusb20_me_decode - decode a message into a decoded structure
544  *
545  * Description of parameters:
546  * "ptr" - message pointer
547  * "len" - message length
548  * "pd" - pointer to decoded structure
549  *
550  * Returns:
551  * "0..65535" - number of bytes decoded, limited by "len"
552  *------------------------------------------------------------------------*/
553 uint16_t
554 libusb20_me_decode(const void *ptr, uint16_t len, void *pd)
555 {
556         const uint8_t *pf;              /* pointer to format data */
557         const uint8_t *buf;             /* pointer to input buffer */
558
559         uint32_t pd_offset;             /* decoded structure offset */
560         uint16_t len_old;               /* old length */
561         uint16_t pd_count;              /* decoded element count */
562         uint8_t me;                     /* message element */
563
564         /* initialise */
565
566         len_old = len;
567         buf = ptr;
568         pd_offset = sizeof(void *);
569         pf = (*((struct libusb20_me_format **)pd))->format;
570
571         /* scan */
572
573         while (1) {
574
575                 /* get information element */
576
577                 me = (pf[0]) & LIBUSB20_ME_MASK;
578                 pd_count = pf[1] | (pf[2] << 8);
579                 pf += 3;
580
581                 /* decode the message element by type */
582
583                 switch (me) {
584                 case LIBUSB20_ME_INT8:
585                         while (pd_count--) {
586                                 uint8_t temp;
587
588                                 if (len < 1) {
589                                         len = 0;
590                                         temp = 0;
591                                 } else {
592                                         len -= 1;
593                                         temp = buf[0];
594                                         buf++;
595                                 }
596                                 *((uint8_t *)LIBUSB20_ADD_BYTES(pd,
597                                     pd_offset)) = temp;
598                                 pd_offset += 1;
599                         }
600                         break;
601
602                 case LIBUSB20_ME_INT16:
603                         pd_offset = -((-pd_offset) & ~1);       /* align */
604                         while (pd_count--) {
605                                 uint16_t temp;
606
607                                 if (len < 2) {
608                                         len = 0;
609                                         temp = 0;
610                                 } else {
611                                         len -= 2;
612                                         temp = buf[1] << 8;
613                                         temp |= buf[0];
614                                         buf += 2;
615                                 }
616                                 *((uint16_t *)LIBUSB20_ADD_BYTES(pd,
617                                     pd_offset)) = temp;
618                                 pd_offset += 2;
619                         }
620                         break;
621
622                 case LIBUSB20_ME_INT32:
623                         pd_offset = -((-pd_offset) & ~3);       /* align */
624                         while (pd_count--) {
625                                 uint32_t temp;
626
627                                 if (len < 4) {
628                                         len = 0;
629                                         temp = 0;
630                                 } else {
631                                         len -= 4;
632                                         temp = buf[3] << 24;
633                                         temp |= buf[2] << 16;
634                                         temp |= buf[1] << 8;
635                                         temp |= buf[0];
636                                         buf += 4;
637                                 }
638
639                                 *((uint32_t *)LIBUSB20_ADD_BYTES(pd,
640                                     pd_offset)) = temp;
641                                 pd_offset += 4;
642                         }
643                         break;
644
645                 case LIBUSB20_ME_INT64:
646                         pd_offset = -((-pd_offset) & ~7);       /* align */
647                         while (pd_count--) {
648                                 uint64_t temp;
649
650                                 if (len < 8) {
651                                         len = 0;
652                                         temp = 0;
653                                 } else {
654                                         len -= 8;
655                                         temp = ((uint64_t)buf[7]) << 56;
656                                         temp |= ((uint64_t)buf[6]) << 48;
657                                         temp |= ((uint64_t)buf[5]) << 40;
658                                         temp |= ((uint64_t)buf[4]) << 32;
659                                         temp |= buf[3] << 24;
660                                         temp |= buf[2] << 16;
661                                         temp |= buf[1] << 8;
662                                         temp |= buf[0];
663                                         buf += 8;
664                                 }
665
666                                 *((uint64_t *)LIBUSB20_ADD_BYTES(pd,
667                                     pd_offset)) = temp;
668                                 pd_offset += 8;
669                         }
670                         break;
671
672                 case LIBUSB20_ME_STRUCT:
673                         pd_offset = -((-pd_offset) &
674                             ~(LIBUSB20_ME_STRUCT_ALIGN - 1));   /* align */
675                         while (pd_count--) {
676                                 uint16_t temp;
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                                                 (void) 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 }