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