1 /*!
2     \file    usbh_enum.c
3     \brief   USB host mode enumeration driver
4 
5     \version 2020-08-04, V1.1.0, firmware for GD32VF103
6 */
7 
8 /*
9     Copyright (c) 2020, GigaDevice Semiconductor Inc.
10 
11     Redistribution and use in source and binary forms, with or without modification,
12 are permitted provided that the following conditions are met:
13 
14     1. Redistributions of source code must retain the above copyright notice, this
15        list of conditions and the following disclaimer.
16     2. Redistributions in binary form must reproduce the above copyright notice,
17        this list of conditions and the following disclaimer in the documentation
18        and/or other materials provided with the distribution.
19     3. Neither the name of the copyright holder nor the names of its contributors
20        may be used to endorse or promote products derived from this software without
21        specific prior written permission.
22 
23     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
32 OF SUCH DAMAGE.
33 */
34 
35 #include "usbh_pipe.h"
36 #include "usbh_transc.h"
37 #include "usbh_enum.h"
38 
39 /* local function prototypes ('static') */
40 static void usbh_devdesc_parse (usb_desc_dev *dev_desc, uint8_t *buf, uint16_t len);
41 static void usbh_cfgdesc_parse (usb_desc_config *cfg_desc, uint8_t *buf);
42 static void usbh_cfgset_parse  (usb_dev_prop *udev, uint8_t *buf);
43 static void usbh_itfdesc_parse (usb_desc_itf *itf_desc, uint8_t *buf);
44 static void usbh_epdesc_parse  (usb_desc_ep *ep_desc, uint8_t *buf);
45 static void usbh_strdesc_parse (uint8_t *psrc, uint8_t *pdest, uint16_t len);
46 
47 /*!
48     \brief      configure USB control status parameters
49     \param[in]  puhost: pointer to usb host
50     \param[in]  buf: control transfer data buffer pointer
51     \param[in]  len: length of the data buffer
52     \param[out] none
53     \retval     none
54 */
usbh_ctlstate_config(usbh_host * puhost,uint8_t * buf,uint16_t len)55 void usbh_ctlstate_config (usbh_host *puhost, uint8_t *buf, uint16_t len)
56 {
57     /* prepare the transactions */
58     puhost->control.buf = buf;
59     puhost->control.ctl_len = len;
60 
61     puhost->control.ctl_state = CTL_SETUP;
62 }
63 
64 /*!
65     \brief      get device descriptor from the USB device
66     \param[in]  puhost: pointer to usb host
67     \param[in]  len: length of the descriptor
68     \param[out] none
69     \retval     operation status
70 */
usbh_devdesc_get(usbh_host * puhost,uint8_t len)71 usbh_status usbh_devdesc_get (usbh_host *puhost, uint8_t len)
72 {
73     usbh_status status = USBH_BUSY;
74 
75     usbh_control *usb_ctl = &puhost->control;
76 
77     if (CTL_IDLE == usb_ctl->ctl_state) {
78         usb_ctl->setup.req = (usb_req) {
79             .bmRequestType = USB_TRX_IN | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
80             .bRequest      = USB_GET_DESCRIPTOR,
81             .wValue        = USBH_DESC(USB_DESCTYPE_DEV),
82             .wIndex        = 0U,
83             .wLength       = len
84         };
85 
86         usbh_ctlstate_config (puhost, puhost->dev_prop.data, (uint16_t)len);
87     }
88 
89     status = usbh_ctl_handler (puhost);
90 
91     if (USBH_OK == status) {
92         /* commands successfully sent and response received */
93         usbh_devdesc_parse (&puhost->dev_prop.dev_desc, puhost->dev_prop.data, (uint16_t)len);
94     }
95 
96     return status;
97 }
98 
99 /*!
100     \brief      get configuration descriptor from the USB device
101     \param[in]  puhost: pointer to usb host
102     \param[in]  len: length of the descriptor
103     \param[out] none
104     \retval     operation status
105 */
usbh_cfgdesc_get(usbh_host * puhost,uint16_t len)106 usbh_status usbh_cfgdesc_get (usbh_host *puhost, uint16_t len)
107 {
108     uint8_t *pdata = NULL;
109 
110     usbh_status status = USBH_BUSY;
111 
112     usbh_control *usb_ctl = &puhost->control;
113 
114 #if (USBH_KEEP_CFG_DESCRIPTOR == 1U)
115     pdata = puhost->dev_prop.cfgdesc_rawdata;
116 #else
117     pdata = puhost->dev_prop.data;
118 #endif
119 
120     if (CTL_IDLE == usb_ctl->ctl_state) {
121         usb_ctl->setup.req = (usb_req) {
122             .bmRequestType = USB_TRX_IN | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
123             .bRequest      = USB_GET_DESCRIPTOR,
124             .wValue        = USBH_DESC(USB_DESCTYPE_CONFIG),
125             .wIndex        = 0U,
126             .wLength       = len
127         };
128 
129         usbh_ctlstate_config (puhost, pdata, len);
130     }
131 
132     status = usbh_ctl_handler (puhost);
133 
134     if (USBH_OK == status) {
135         if (len <= USB_CFG_DESC_LEN) {
136             usbh_cfgdesc_parse (&puhost->dev_prop.cfg_desc_set.cfg_desc, pdata);
137         } else {
138             usbh_cfgset_parse (&puhost->dev_prop, pdata);
139         }
140     }
141 
142     return status;
143 }
144 
145 /*!
146     \brief      get string descriptor from the USB device
147     \param[in]  puhost: pointer to usb host
148     \param[in]  str_index: index for the string descriptor
149     \param[in]  buf: buffer pointer to the string descriptor
150     \param[in]  len: length of the descriptor
151     \param[out] none
152     \retval     operation status
153 */
usbh_strdesc_get(usbh_host * puhost,uint8_t str_index,uint8_t * buf,uint16_t len)154 usbh_status usbh_strdesc_get (usbh_host *puhost,
155                               uint8_t str_index,
156                               uint8_t *buf,
157                               uint16_t len)
158 {
159     usbh_status status = USBH_BUSY;
160 
161     usbh_control *usb_ctl = &puhost->control;
162 
163     if (CTL_IDLE == usb_ctl->ctl_state) {
164         usb_ctl->setup.req = (usb_req) {
165             .bmRequestType = USB_TRX_IN | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
166             .bRequest      = USB_GET_DESCRIPTOR,
167             .wValue        = USBH_DESC(USB_DESCTYPE_STR) | str_index,
168             .wIndex        = 0x0409U,
169             .wLength       = len
170         };
171 
172         usbh_ctlstate_config (puhost, puhost->dev_prop.data, len);
173     }
174 
175     status = usbh_ctl_handler (puhost);
176 
177     if (USBH_OK == status) {
178         /* commands successfully sent and response received */
179         usbh_strdesc_parse (puhost->dev_prop.data, buf, len);
180     }
181 
182     return status;
183 }
184 
185 /*!
186     \brief      set the address to the connected device
187     \param[in]  puhost: pointer to usb host
188     \param[in]  dev_addr: device address to assign
189     \param[out] none
190     \retval     operation status
191 */
usbh_setaddress(usbh_host * puhost,uint8_t dev_addr)192 usbh_status usbh_setaddress (usbh_host *puhost, uint8_t dev_addr)
193 {
194     usbh_status status = USBH_BUSY;
195 
196     usbh_control *usb_ctl = &puhost->control;
197 
198     if (CTL_IDLE == usb_ctl->ctl_state) {
199         usb_ctl->setup.req = (usb_req) {
200             .bmRequestType = USB_TRX_OUT | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
201             .bRequest      = USB_SET_ADDRESS,
202             .wValue        = (uint16_t)dev_addr,
203             .wIndex        = 0U,
204             .wLength       = 0U
205         };
206 
207         usbh_ctlstate_config (puhost, NULL, 0U);
208     }
209 
210     status = usbh_ctl_handler (puhost);
211 
212     return status;
213 }
214 
215 /*!
216     \brief      set the configuration value to the connected device
217     \param[in]  puhost: pointer to usb host
218     \param[in]  config_index: configuration value
219     \param[out] none
220     \retval     operation status
221 */
usbh_setcfg(usbh_host * puhost,uint16_t config_index)222 usbh_status usbh_setcfg (usbh_host *puhost, uint16_t config_index)
223 {
224     usbh_status status = USBH_BUSY;
225 
226     usbh_control *usb_ctl = &puhost->control;
227 
228     if (CTL_IDLE == usb_ctl->ctl_state) {
229         usb_ctl->setup.req = (usb_req) {
230             .bmRequestType = USB_TRX_OUT | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
231             .bRequest      = USB_SET_CONFIGURATION,
232             .wValue        = config_index,
233             .wIndex        = 0U,
234             .wLength       = 0U
235         };
236 
237         usbh_ctlstate_config (puhost, NULL, 0U);
238     }
239 
240     status = usbh_ctl_handler (puhost);
241 
242     return status;
243 }
244 
245 /*!
246     \brief      set the interface value to the connected device
247     \param[in]  puhost: pointer to usb host
248     \param[in]  itf_num: interface number
249     \param[in]  set: alternated setting value
250     \param[out] none
251     \retval     operation status
252 */
usbh_setinterface(usbh_host * puhost,uint8_t itf_num,uint8_t set)253 usbh_status usbh_setinterface (usbh_host *puhost, uint8_t itf_num, uint8_t set)
254 {
255     usbh_status status = USBH_BUSY;
256 
257     usbh_control *usb_ctl = &puhost->control;
258 
259     if (CTL_IDLE == usb_ctl->ctl_state) {
260         usb_ctl->setup.req = (usb_req) {
261             .bmRequestType = USB_TRX_OUT | USB_RECPTYPE_ITF | USB_REQTYPE_STRD,
262             .bRequest      = USB_SET_INTERFACE,
263             .wValue        = set,
264             .wIndex        = itf_num,
265             .wLength       = 0U
266         };
267 
268         usbh_ctlstate_config (puhost, NULL, 0U);
269     }
270 
271     status = usbh_ctl_handler (puhost);
272 
273     return status;
274 }
275 
276 /*!
277     \brief      set the interface value to the connected device
278     \param[in]  puhost: pointer to usb host
279     \param[in]  feature_selector: feature selector
280     \param[in]  windex: index value
281     \param[out] none
282     \retval     operation status
283 */
usbh_setdevfeature(usbh_host * puhost,uint8_t feature_selector,uint16_t windex)284 usbh_status usbh_setdevfeature (usbh_host *puhost, uint8_t feature_selector, uint16_t windex)
285 {
286     usbh_status status = USBH_BUSY;
287 
288     usbh_control *usb_ctl = &puhost->control;
289 
290     if (CTL_IDLE == usb_ctl->ctl_state) {
291         usb_ctl->setup.req = (usb_req) {
292             .bmRequestType = USB_TRX_OUT | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
293             .bRequest      = USB_SET_FEATURE,
294             .wValue        = feature_selector,
295             .wIndex        = windex,
296             .wLength       = 0U
297         };
298 
299         usbh_ctlstate_config (puhost, NULL, 0U);
300     }
301 
302     status = usbh_ctl_handler (puhost);
303 
304     return status;
305 }
306 
307 /*!
308     \brief      clear the interface value to the connected device
309     \param[in]  puhost: pointer to usb host
310     \param[in]  feature_selector: feature selector
311     \param[in]  windex: index value
312     \param[out] none
313     \retval     operation status
314 */
usbh_clrdevfeature(usbh_host * puhost,uint8_t feature_selector,uint16_t windex)315 usbh_status usbh_clrdevfeature (usbh_host *puhost, uint8_t feature_selector, uint16_t windex)
316 {
317     usbh_status status = USBH_BUSY;
318 
319     usbh_control *usb_ctl = &puhost->control;
320 
321     if (CTL_IDLE == usb_ctl->ctl_state) {
322         usb_ctl->setup.req = (usb_req) {
323             .bmRequestType = USB_TRX_OUT | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
324             .bRequest      = USB_CLEAR_FEATURE,
325             .wValue        = feature_selector,
326             .wIndex        = windex,
327             .wLength       = 0U
328         };
329 
330         usbh_ctlstate_config (puhost, NULL, 0U);
331     }
332 
333     status = usbh_ctl_handler (puhost);
334 
335     return status;
336 }
337 
338 /*!
339     \brief      clear or disable a specific feature
340     \param[in]  puhost: pointer to usb host
341     \param[in]  ep_addr: endpoint address
342     \param[in]  pp_num: pipe number
343     \param[out] none
344     \retval     operation status
345 */
usbh_clrfeature(usbh_host * puhost,uint8_t ep_addr,uint8_t pp_num)346 usbh_status usbh_clrfeature (usbh_host *puhost, uint8_t ep_addr, uint8_t pp_num)
347 {
348     usbh_status status = USBH_BUSY;
349     usbh_control *usb_ctl = &puhost->control;
350     usb_core_driver *pudev = (usb_core_driver *)puhost->data;
351 
352     if (CTL_IDLE == usb_ctl->ctl_state) {
353         usb_ctl->setup.req = (usb_req) {
354             .bmRequestType = USB_TRX_OUT | USB_RECPTYPE_EP | USB_REQTYPE_STRD,
355             .bRequest      = USB_CLEAR_FEATURE,
356             .wValue        = FEATURE_SELECTOR_EP,
357             .wIndex        = ep_addr,
358             .wLength       = 0U
359         };
360 
361         if (EP_ID(ep_addr) == pudev->host.pipe[pp_num].ep.num) {
362             usbh_pipe_toggle_set(pudev, pp_num, 0U);
363         } else {
364             return USBH_FAIL;
365         }
366 
367         usbh_ctlstate_config (puhost, NULL, 0U);
368     }
369 
370     status = usbh_ctl_handler (puhost);
371 
372     return status;
373 }
374 
375 /*!
376     \brief      get the next descriptor header
377     \param[in]  pbuf: pointer to buffer where the configuration descriptor set is available
378     \param[in]  ptr: data pointer inside the configuration descriptor set
379     \param[out] none
380     \retval     return descriptor header
381 */
usbh_nextdesc_get(uint8_t * pbuf,uint16_t * ptr)382 usb_desc_header *usbh_nextdesc_get (uint8_t *pbuf, uint16_t *ptr)
383 {
384     usb_desc_header *pnext;
385 
386     *ptr += ((usb_desc_header *)pbuf)->bLength;
387 
388     pnext = (usb_desc_header *)((uint8_t *)pbuf + ((usb_desc_header *)pbuf)->bLength);
389 
390     return (pnext);
391 }
392 
393 /*!
394     \brief      get the next descriptor header
395     \param[in]  udev: pointer to device property
396     \param[in]  interface: interface number
397     \param[out] none
398     \retval     operation status
399 */
usbh_interface_select(usb_dev_prop * udev,uint8_t interface)400 usbh_status usbh_interface_select (usb_dev_prop *udev, uint8_t interface)
401 {
402     usbh_status status = USBH_OK;
403 
404     if (interface < udev->cfg_desc_set.cfg_desc.bNumInterfaces) {
405         udev->cur_itf = interface;
406     } else {
407         status = USBH_FAIL;
408     }
409 
410     return status;
411 }
412 
413 /*!
414     \brief      find the interface index for a specific class
415     \param[in]  udev: pointer to device property
416     \param[in]  main_class: class code
417     \param[in]  sub_class: subclass code
418     \param[in]  protocol: Protocol code
419     \param[out] none
420     \retval     interface index in the configuration structure
421     \note       interface index 0xFF means interface index not found
422 */
usbh_interface_find(usb_dev_prop * udev,uint8_t main_class,uint8_t sub_class,uint8_t protocol)423 uint8_t usbh_interface_find (usb_dev_prop *udev, uint8_t main_class, uint8_t sub_class, uint8_t protocol)
424 {
425     usb_desc_itf *pif;
426 
427     uint8_t if_ix = 0U;
428 
429     pif = (usb_desc_itf *)0;
430 
431     while (if_ix < udev->cfg_desc_set.cfg_desc.bNumInterfaces) {
432         pif = &udev->cfg_desc_set.itf_desc_set[if_ix][0].itf_desc;
433 
434         if (((pif->bInterfaceClass == main_class) || (main_class == 0xFFU))&&
435              ((pif->bInterfaceSubClass == sub_class) || (sub_class == 0xFFU))&&
436                ((pif->bInterfaceProtocol == protocol) || (protocol == 0xFFU))) {
437             return if_ix;
438         }
439 
440         if_ix++;
441     }
442 
443     return 0xFFU;
444 }
445 
446 /*!
447     \brief      find the interface index for a specific class interface and alternate setting number
448     \param[in]  udev: pointer to device property
449     \param[in]  interface_number: interface number
450     \param[in]  alt_settings: alternate setting number
451     \param[out] none
452     \retval     interface index in the configuration structure
453     \note       interface index 0xFF means interface index not found
454 */
usbh_interfaceindex_find(usb_dev_prop * udev,uint8_t interface_number,uint8_t alt_settings)455 uint8_t usbh_interfaceindex_find (usb_dev_prop *udev, uint8_t interface_number, uint8_t alt_settings)
456 {
457     usb_desc_itf *pif;
458 
459     uint8_t if_ix = 0U;
460 
461     pif = (usb_desc_itf *)0;
462 
463     while (if_ix < USBH_MAX_INTERFACES_NUM) {
464         pif = &udev->cfg_desc_set.itf_desc_set[if_ix][alt_settings].itf_desc;
465 
466         if ((pif->bInterfaceNumber == interface_number) && (pif->bAlternateSetting == alt_settings)) {
467             return if_ix;
468         }
469 
470         if_ix++;
471     }
472 
473     return 0xFFU;
474 }
475 
476 /*!
477     \brief      parse the device descriptor
478     \param[in]  dev_desc: pointer to usb device descriptor buffer
479     \param[in]  buf: pointer to the source descriptor buffer
480     \param[in]  len: length of the descriptor
481     \param[out] none
482     \retval     none
483 */
usbh_devdesc_parse(usb_desc_dev * dev_desc,uint8_t * buf,uint16_t len)484 static void usbh_devdesc_parse (usb_desc_dev *dev_desc, uint8_t *buf, uint16_t len)
485 {
486     *dev_desc = (usb_desc_dev) {
487         .header = {
488             .bLength         = *(uint8_t *)(buf + 0U),
489             .bDescriptorType = *(uint8_t *)(buf + 1U)
490         },
491 
492         .bcdUSB              = BYTE_SWAP(buf + 2U),
493         .bDeviceClass        = *(uint8_t *)(buf + 4U),
494         .bDeviceSubClass     = *(uint8_t *)(buf + 5U),
495         .bDeviceProtocol     = *(uint8_t *)(buf + 6U),
496         .bMaxPacketSize0     = *(uint8_t *)(buf + 7U)
497     };
498 
499     if (len > 8U) {
500         /* for 1st time after device connection, host may issue only 8 bytes for device descriptor length  */
501         dev_desc->idVendor              = BYTE_SWAP(buf + 8U);
502         dev_desc->idProduct             = BYTE_SWAP(buf + 10U);
503         dev_desc->bcdDevice             = BYTE_SWAP(buf + 12U);
504         dev_desc->iManufacturer         = *(uint8_t *)(buf + 14U);
505         dev_desc->iProduct              = *(uint8_t *)(buf + 15U);
506         dev_desc->iSerialNumber         = *(uint8_t *)(buf + 16U);
507         dev_desc->bNumberConfigurations = *(uint8_t *)(buf + 17U);
508     }
509 }
510 
511 /*!
512     \brief      parse the configuration descriptor
513     \param[in]  cfg_desc: pointer to usb configuration descriptor buffer
514     \param[in]  buf: pointer to the source descriptor buffer
515     \param[out] none
516     \retval     none
517 */
usbh_cfgdesc_parse(usb_desc_config * cfg_desc,uint8_t * buf)518 static void usbh_cfgdesc_parse (usb_desc_config *cfg_desc, uint8_t *buf)
519 {
520     /* parse configuration descriptor */
521     *cfg_desc = (usb_desc_config) {
522         .header = {
523             .bLength         = *(uint8_t *)(buf + 0U),
524             .bDescriptorType = *(uint8_t *)(buf + 1U),
525         },
526 
527         .wTotalLength        = BYTE_SWAP(buf + 2U),
528         .bNumInterfaces      = *(uint8_t *)(buf + 4U),
529         .bConfigurationValue = *(uint8_t *)(buf + 5U),
530         .iConfiguration      = *(uint8_t *)(buf + 6U),
531         .bmAttributes        = *(uint8_t *)(buf + 7U),
532         .bMaxPower           = *(uint8_t *)(buf + 8U)
533     };
534 }
535 
536 /*!
537     \brief      parse the configuration descriptor set
538     \param[in]  udev:  pointer to device property
539     \param[in]  buf: pointer to the source descriptor buffer
540     \param[out] none
541     \retval     none
542 */
usbh_cfgset_parse(usb_dev_prop * udev,uint8_t * buf)543 static void  usbh_cfgset_parse (usb_dev_prop *udev, uint8_t *buf)
544 {
545     usb_desc_ep *ep = NULL;
546     usb_desc_itf_set *itf = NULL;
547     usb_desc_itf itf_value;
548     usb_desc_config *cfg = NULL;
549 
550     usb_desc_header *pdesc = (usb_desc_header *)buf;
551 
552     uint8_t itf_index = 0U, ep_index = 0U, alt_setting = 0U;
553     uint8_t pre_itf_index = 0U;
554     uint16_t ptr;
555 
556     /* parse configuration descriptor */
557     usbh_cfgdesc_parse (&udev->cfg_desc_set.cfg_desc, buf);
558     cfg = &udev->cfg_desc_set.cfg_desc;
559     ptr = USB_CFG_DESC_LEN;
560 
561     if (cfg->bNumInterfaces > USBH_MAX_INTERFACES_NUM) {
562         return;
563     }
564 
565     while (ptr < cfg->wTotalLength) {
566         pdesc = usbh_nextdesc_get ((uint8_t *)pdesc, &ptr);
567 
568         if (pdesc->bDescriptorType == USB_DESCTYPE_ITF) {
569             itf_index = *(((uint8_t *)pdesc) + 2U);
570 
571             if (pre_itf_index != itf_index) {
572                 alt_setting = 0U;
573             }
574 
575             itf = &udev->cfg_desc_set.itf_desc_set[itf_index][alt_setting];
576 
577             alt_setting++;
578 
579             if ((*((uint8_t *)pdesc + 3U)) < 3U) {
580                 usbh_itfdesc_parse (&itf_value, (uint8_t *)pdesc);
581 
582                 /* parse endpoint descriptors relative to the current interface */
583                 if (itf_value.bNumEndpoints > USBH_MAX_EP_NUM) {
584                     return;
585                 }
586 
587                 usbh_itfdesc_parse (&itf->itf_desc, (uint8_t *)&itf_value);
588 
589                 /* store the previous interface index */
590                 pre_itf_index = itf_index;
591 
592                 if (0U == itf_value.bNumEndpoints) {
593                     continue;
594                 }
595 
596                 for (ep_index = 0U; ep_index < itf_value.bNumEndpoints; ) {
597                     pdesc = usbh_nextdesc_get ((void*)pdesc, &ptr);
598 
599                     if (pdesc->bDescriptorType == USB_DESCTYPE_EP) {
600                         ep = &itf->ep_desc[ep_index];
601 
602                         usbh_epdesc_parse (ep, (uint8_t *)pdesc);
603 
604                         ep_index++;
605                     }
606                 }
607             }
608         }
609     }
610 }
611 
612 /*!
613     \brief      parse the interface descriptor
614     \param[in]  itf_desc: pointer to usb interface descriptor buffer
615     \param[in]  buf: pointer to the source descriptor buffer
616     \param[out] none
617     \retval     none
618 */
usbh_itfdesc_parse(usb_desc_itf * itf_desc,uint8_t * buf)619 static void  usbh_itfdesc_parse (usb_desc_itf *itf_desc, uint8_t *buf)
620 {
621     *itf_desc = (usb_desc_itf) {
622         .header = {
623             .bLength         = *(uint8_t *)(buf + 0U),
624             .bDescriptorType = *(uint8_t *)(buf + 1U),
625         },
626 
627         .bInterfaceNumber    = *(uint8_t *)(buf + 2U),
628         .bAlternateSetting   = *(uint8_t *)(buf + 3U),
629         .bNumEndpoints       = *(uint8_t *)(buf + 4U),
630         .bInterfaceClass     = *(uint8_t *)(buf + 5U),
631         .bInterfaceSubClass  = *(uint8_t *)(buf + 6U),
632         .bInterfaceProtocol  = *(uint8_t *)(buf + 7U),
633         .iInterface          = *(uint8_t *)(buf + 8U)
634     };
635 }
636 
637 /*!
638     \brief      parse the endpoint descriptor
639     \param[in]  ep_desc: pointer to usb endpoint descriptor buffer
640     \param[in]  buf: pointer to the source descriptor buffer
641     \param[out] none
642     \retval     none
643 */
usbh_epdesc_parse(usb_desc_ep * ep_desc,uint8_t * buf)644 static void  usbh_epdesc_parse (usb_desc_ep *ep_desc, uint8_t *buf)
645 {
646     *ep_desc = (usb_desc_ep) {
647         .header = {
648             .bLength         = *(uint8_t *)(buf + 0U),
649             .bDescriptorType = *(uint8_t *)(buf + 1U)
650         },
651 
652         .bEndpointAddress    = *(uint8_t *)(buf + 2U),
653         .bmAttributes        = *(uint8_t *)(buf + 3U),
654         .wMaxPacketSize      = BYTE_SWAP(buf + 4U),
655         .bInterval           = *(uint8_t *)(buf + 6U)
656     };
657 }
658 
659 /*!
660     \brief      parse the string descriptor
661     \param[in]  psrc: source pointer containing the descriptor data
662     \param[in]  pdest: destination address pointer
663     \param[in]  len: length of the descriptor
664     \param[out] none
665     \retval     none
666 */
usbh_strdesc_parse(uint8_t * psrc,uint8_t * pdest,uint16_t len)667 static void usbh_strdesc_parse (uint8_t *psrc, uint8_t *pdest, uint16_t len)
668 {
669     uint16_t str_len = 0U, index = 0U;
670 
671     /* the unicode string descriptor is not NULL-terminated. The string length is
672      * computed by substracting two from the value of the first byte of the descriptor.
673      */
674 
675     /* check which is lower size, the size of string or the length of bytes read from the device */
676     if (USB_DESCTYPE_STR == psrc[1]) {
677         /* make sure the descriptor is string type */
678 
679         /* psrc[0] contains Size of Descriptor, subtract 2 to get the length of string */
680         str_len = USB_MIN((uint16_t)psrc[0] - 2U, len);
681 
682         psrc += 2U; /* adjust the offset ignoring the string len and descriptor type */
683 
684         for (index = 0U; index < str_len; index += 2U) {
685             /* copy only the string and ignore the unicode id, hence add the src */
686             *pdest = psrc[index];
687 
688             pdest++;
689         }
690 
691         *pdest = 0U; /* mark end of string */
692     }
693 }
694