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