1 /*!
2     \file    usbh_core.c
3     \brief   USB host core state machine 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 "drv_usb_hw.h"
36 #include "usbh_pipe.h"
37 #include "usbh_enum.h"
38 #include "usbh_core.h"
39 #include "drv_usbh_int.h"
40 #include <string.h>
41 
42 usb_core_driver usbh_core;
43 
44 /* local function prototypes ('static') */
45 static uint8_t usbh_sof           (usbh_host *puhost);
46 static uint8_t usbh_connect       (usbh_host *puhost);
47 static uint8_t usbh_disconnect    (usbh_host *puhost);
48 static uint8_t usbh_port_enabled  (usbh_host *puhost);
49 static uint8_t usbh_port_disabled (usbh_host *puhost);
50 static usbh_status usbh_enum_task (usbh_host *puhost);
51 
52 #ifdef USB_FS_LOW_PWR_ENABLE
53 static void usb_hwp_suspend(usb_core_driver *pudev);
54 static void usb_hwp_resume(usb_core_driver *pudev);
55 #endif
56 
57 usbh_int_cb usbh_int_op =
58 {
59     usbh_connect,
60     usbh_disconnect,
61     usbh_port_enabled,
62     usbh_port_disabled,
63     usbh_sof
64 };
65 
66 usbh_int_cb *usbh_int_fop = &usbh_int_op;
67 
68 /*!
69     \brief      USB host stack initializations
70     \param[in]  puhost: pointer to USB host
71     \param[in]  user_cb: pointer to user callback
72     \param[out] none
73     \retval     none
74 */
usbh_init(usbh_host * puhost,usbh_user_cb * user_cb)75 void usbh_init (usbh_host *puhost, usbh_user_cb *user_cb)
76 {
77     /* host de-initializations */
78     usbh_deinit(puhost);
79 
80     puhost->usr_cb = user_cb;
81 
82     usbh_core.host.connect_status = 0U;
83 
84     for (uint8_t i = 0U; i < USBFS_MAX_TX_FIFOS; i++) {
85         usbh_core.host.pipe[i].err_count = 0U;
86         usbh_core.host.pipe[i].pp_status = PIPE_IDLE;
87         usbh_core.host.backup_xfercount[i] = 0U;
88     }
89 
90     usbh_core.host.pipe[0].ep.mps = 8U;
91 
92 #ifdef USE_USB_FS
93     usb_basic_init (&usbh_core.bp, &usbh_core.regs, USB_CORE_ENUM_FS);
94 #endif /* USE_USB_FS */
95 
96 #ifndef DUAL_ROLE_MODE_ENABLED
97     usb_globalint_disable(&usbh_core.regs);
98 
99     usb_core_init (usbh_core.bp, &usbh_core.regs);
100 
101 #ifndef USE_OTG_MODE
102     usb_curmode_set (&usbh_core.regs, HOST_MODE);
103 #endif /* USE_OTG_MODE */
104 
105     usb_host_init (&usbh_core);
106 
107     usb_globalint_enable(&usbh_core.regs);
108 #endif /* DUAL_ROLE_MODE_ENABLED */
109 
110     /* link driver to the stack */
111     usbh_core.host.data = (void *)puhost;
112     puhost->data = (void *)&usbh_core;
113 
114     /* upon init call usr call back */
115     puhost->usr_cb->dev_init();
116 }
117 
118 /*!
119     \brief      USB host register device class
120     \param[in]  puhost: pointer to usb host instance
121     \param[in]  puclass: pointer to USB device class
122     \param[out] none
123     \retval     operation status
124 */
usbh_class_register(usbh_host * puhost,usbh_class * puclass)125 usbh_status usbh_class_register (usbh_host *puhost, usbh_class *puclass)
126 {
127     usbh_status status = USBH_OK;
128 
129     if (NULL != puclass) {
130         if (puhost->class_num < USBH_MAX_SUPPORTED_CLASS) {
131             puhost->uclass[puhost->class_num++] = puclass;
132         } else {
133             status = USBH_FAIL;
134         }
135     } else {
136         status = USBH_FAIL;
137     }
138 
139     return status;
140 }
141 
142 /*!
143     \brief      de-initialize USB host
144     \param[in]  puhost: pointer to USB host
145     \param[out] none
146     \retval     operation status
147 */
usbh_deinit(usbh_host * puhost)148 usbh_status usbh_deinit(usbh_host *puhost)
149 {
150     /* software init */
151     puhost->cur_state = HOST_DEFAULT;
152     puhost->backup_state = HOST_DEFAULT;
153     puhost->enum_state = ENUM_DEFAULT;
154 
155     puhost->control.ctl_state = CTL_IDLE;
156     puhost->control.max_len = USB_FS_EP0_MAX_LEN;
157 
158     puhost->dev_prop.addr = USBH_DEV_ADDR_DEFAULT;
159     puhost->dev_prop.speed = PORT_SPEED_FULL;
160     puhost->dev_prop.cur_itf = 0xFFU;
161 
162     usbh_pipe_free(&usbh_core, puhost->control.pipe_in_num);
163     usbh_pipe_free(&usbh_core, puhost->control.pipe_out_num);
164 
165     return USBH_OK;
166 }
167 
168 /*!
169     \brief      USB host core main state machine process
170     \param[in]  puhost: pointer to USB host
171     \param[out] none
172     \retval     none
173 */
usbh_core_task(usbh_host * puhost)174 void usbh_core_task (usbh_host *puhost)
175 {
176     volatile usbh_status status = USBH_FAIL;
177 
178     /* check for host port events */
179     if (((0U == usbh_core.host.connect_status) || (0U == usbh_core.host.port_enabled)) && (HOST_DEFAULT != puhost->cur_state)) {
180         if (puhost->cur_state != HOST_DEV_DETACHED) {
181             puhost->cur_state = HOST_DEV_DETACHED;
182         }
183     }
184 
185     switch (puhost->cur_state) {
186     case HOST_DEFAULT:
187         if (usbh_core.host.connect_status) {
188             puhost->cur_state = HOST_DETECT_DEV_SPEED;
189 
190             usb_mdelay (100U);
191 
192             usb_port_reset (&usbh_core);
193 
194             puhost->usr_cb->dev_reset();
195         }
196         break;
197 
198     case HOST_DETECT_DEV_SPEED:
199         if (usbh_core.host.port_enabled) {
200             puhost->cur_state = HOST_DEV_ATTACHED;
201 
202             puhost->dev_prop.speed = usb_curspeed_get (&usbh_core);
203 
204             puhost->usr_cb->dev_speed_detected(puhost->dev_prop.speed);
205 
206             usb_mdelay (50U);
207         }
208         break;
209 
210     case HOST_DEV_ATTACHED:
211         puhost->usr_cb->dev_attach();
212         puhost->control.pipe_out_num = usbh_pipe_allocate(&usbh_core, 0x00U);
213         puhost->control.pipe_in_num = usbh_pipe_allocate(&usbh_core, 0x80U);
214 
215         /* open IN control pipe */
216         usbh_pipe_create (&usbh_core,
217                           &puhost->dev_prop,
218                           puhost->control.pipe_in_num,
219                           USB_EPTYPE_CTRL,
220                           (uint16_t)puhost->control.max_len);
221 
222         /* open OUT control pipe */
223         usbh_pipe_create (&usbh_core,
224                           &puhost->dev_prop,
225                           puhost->control.pipe_out_num,
226                           USB_EPTYPE_CTRL,
227                           (uint16_t)puhost->control.max_len);
228 
229         puhost->cur_state = HOST_ENUM;
230         break;
231 
232     case HOST_ENUM:
233         /* check for enumeration status */
234         if (USBH_OK == usbh_enum_task (puhost)) {
235             /* the function shall return USBH_OK when full enumeration is complete */
236 
237             /* user callback for end of device basic enumeration */
238             puhost->usr_cb->dev_enumerated();
239 
240 #ifdef USB_FS_LOW_PWR_ENABLE
241             puhost->cur_state = HOST_SUSPENDED;
242 #else
243             puhost->cur_state = HOST_SET_WAKEUP_FEATURE;
244 #endif
245         }
246         break;
247 
248     case HOST_SET_WAKEUP_FEATURE:
249         if ((puhost->dev_prop.cfg_desc_set.cfg_desc.bmAttributes) & (1U << 5)) {
250             if (usbh_setdevfeature(puhost, FEATURE_SELECTOR_REMOTEWAKEUP, 0U) == USBH_OK) {
251                 puhost->cur_state = HOST_CHECK_CLASS;
252             }
253         } else {
254             puhost->cur_state = HOST_CHECK_CLASS;
255         }
256         break;
257 
258     case HOST_CHECK_CLASS:
259         if (puhost->class_num == 0U) {
260             puhost->cur_state = HOST_ERROR;
261         } else {
262             puhost->active_class = NULL;
263 
264             uint8_t itf_class = puhost->dev_prop.cfg_desc_set.itf_desc_set[0][0].itf_desc.bInterfaceClass;
265 
266             for (uint8_t index = 0U; index < puhost->class_num; index++) {
267                 if ((puhost->uclass[index]->class_code == itf_class) || (0xFFU == itf_class)) {
268                     puhost->active_class = puhost->uclass[index];
269                 }
270             }
271 
272             if (puhost->active_class != NULL) {
273                 puhost->cur_state = HOST_USER_INPUT;
274             } else {
275                 puhost->cur_state = HOST_ERROR;
276             }
277         }
278         break;
279 
280     case HOST_USER_INPUT:
281         /* the function should return user response true to move to class state */
282         if (USBH_USER_RESP_OK == puhost->usr_cb->dev_user_input()) {
283             if ((USBH_OK == puhost->active_class->class_init(puhost))) {
284                 puhost->cur_state = HOST_CLASS_ENUM;
285             }
286         }
287         break;
288 
289 #ifdef USB_FS_LOW_PWR_ENABLE
290     case HOST_SUSPENDED:
291         if (USBH_OK == usbh_setdevfeature(puhost, FEATURE_SELECTOR_DEV, 0U)) {
292             puhost->suspend_flag = 1;
293             usb_hwp_suspend(puhost->data);
294             puhost->usr_cb->dev_user_input();
295             pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, WFI_CMD);
296             puhost->cur_state = HOST_WAKEUP;
297         }
298         break;
299 
300     case HOST_WAKEUP:
301         if (USBH_OK == usbh_clrdevfeature(puhost, FEATURE_SELECTOR_DEV, 0U)) {
302             /* user callback for initalization */
303             puhost->usr_cb->dev_init();
304 
305             puhost->cur_state = HOST_CHECK_CLASS;
306         }
307         break;
308 #endif
309 
310     case HOST_CLASS_ENUM:
311         /* process class standard contol requests state machine */
312         status = puhost->active_class->class_requests(puhost);
313 
314         if (USBH_OK == status) {
315             puhost->cur_state = HOST_CLASS_HANDLER;
316         } else {
317             usbh_error_handler (puhost, status);
318         }
319         break;
320 
321     case HOST_CLASS_HANDLER:
322         /* process class state machine */
323         status = puhost->active_class->class_machine(puhost);
324 
325         usbh_error_handler (puhost, status);
326         break;
327 
328     case HOST_ERROR:
329         /* re-initilaize host for new enumeration */
330         usbh_deinit (puhost);
331         puhost->usr_cb->dev_deinit();
332         puhost->active_class->class_deinit(puhost);
333         break;
334 
335     case HOST_DEV_DETACHED:
336         /* manage user disconnect operations*/
337         puhost->usr_cb->dev_detach();
338 
339         /* re-initilaize host for new enumeration */
340         usbh_deinit(puhost);
341         puhost->usr_cb->dev_deinit();
342         puhost->active_class->class_deinit(puhost);
343         usbh_pipe_delete(&usbh_core);
344         puhost->cur_state = HOST_DEFAULT;
345         break;
346 
347     default:
348         break;
349     }
350 }
351 
352 /*!
353     \brief      handle the error on USB host side
354     \param[in]  puhost: pointer to USB host
355     \param[in]  err_type: type of error or busy/OK state
356     \param[out] none
357     \retval     none
358 */
usbh_error_handler(usbh_host * puhost,usbh_status err_type)359 void usbh_error_handler (usbh_host *puhost, usbh_status err_type)
360 {
361     /* error unrecovered or not supported device speed */
362     if ((USBH_SPEED_UNKNOWN_ERROR == err_type) || (USBH_UNRECOVERED_ERROR == err_type)) {
363         puhost->usr_cb->dev_error();
364 
365         puhost->cur_state = HOST_ERROR;
366     } else if (USBH_APPLY_DEINIT == err_type) {
367         puhost->cur_state = HOST_ERROR;
368 
369         /* user callback for initalization */
370         puhost->usr_cb->dev_init();
371     } else {
372         /* no operation */
373     }
374 }
375 
376 /*!
377     \brief      USB SOF callback function from the interrupt
378     \param[in]  puhost: pointer to usb host
379     \param[out] none
380     \retval     operation status
381 */
usbh_sof(usbh_host * puhost)382 static uint8_t usbh_sof (usbh_host *puhost)
383 {
384     /* this callback could be used to implement a scheduler process */
385     puhost->control.timer = (uint16_t)usb_curframe_get(&usbh_core);
386 
387     if (puhost->active_class != NULL) {
388         if (puhost->active_class->class_sof != NULL) {
389             puhost->active_class->class_sof(puhost);
390         }
391     }
392 
393     return 0U;
394 }
395 
396 /*!
397     \brief      USB connect callback function from the interrupt
398     \param[in]  puhost: pointer to usb host
399     \param[out] none
400     \retval     operation status
401 */
usbh_connect(usbh_host * puhost)402 static uint8_t usbh_connect (usbh_host *puhost)
403 {
404     usbh_core.host.connect_status = 1U;
405 
406     return 0U;
407 }
408 
409 /*!
410     \brief      USB disconnect callback function from the interrupt
411     \param[in]  puhost: pointer to usb host
412     \param[out] none
413     \retval     operation status
414 */
usbh_disconnect(usbh_host * puhost)415 static uint8_t usbh_disconnect (usbh_host *puhost)
416 {
417     usbh_core.host.connect_status = 0U;
418 
419     return 0U;
420 }
421 
422 /*!
423     \brief      USB port enable callback function from the interrupt
424     \param[in]  puhost: pointer to usb host
425     \param[out] none
426     \retval     operation status
427 */
usbh_port_enabled(usbh_host * puhost)428 static uint8_t usbh_port_enabled (usbh_host *puhost)
429 {
430     usbh_core.host.port_enabled = 1U;
431 
432     return 0U;
433 }
434 
435 /*!
436     \brief      USB port disabled callback function from the interrupt
437     \param[in]  puhost: pointer to usb host
438     \param[out] none
439     \retval     operation status
440 */
usbh_port_disabled(usbh_host * puhost)441 static uint8_t usbh_port_disabled (usbh_host *puhost)
442 {
443     usbh_core.host.port_enabled = 0U;
444 
445     return 0U;
446 }
447 
448 /*!
449     \brief      handle the USB enumeration task
450     \param[in]  puhost: pointer to host
451     \param[out] none
452     \retval     none
453 */
usbh_enum_task(usbh_host * puhost)454 static usbh_status usbh_enum_task (usbh_host *puhost)
455 {
456     uint8_t str_buf[64];
457 
458     usbh_status status = USBH_BUSY;
459 
460     static uint8_t index_mfc_str = 0U, index_prod_str = 0U, index_serial_str = 0U;
461 
462     switch (puhost->enum_state) {
463     case ENUM_DEFAULT:
464         /* get device descriptor for only 1st 8 bytes : to get ep0 maxpacketsize */
465         if (USBH_OK == usbh_devdesc_get (puhost, 8U)) {
466             puhost->control.max_len = puhost->dev_prop.dev_desc.bMaxPacketSize0;
467 
468             /* modify control channels configuration for maximum packet size */
469             usbh_pipe_update (&usbh_core,
470                               puhost->control.pipe_out_num,
471                               0U, 0U,
472                               (uint16_t)puhost->control.max_len);
473 
474             usbh_pipe_update (&usbh_core,
475                               puhost->control.pipe_in_num,
476                               0U, 0U,
477                               (uint16_t)puhost->control.max_len);
478 
479             puhost->enum_state = ENUM_GET_DEV_DESC;
480         }
481         break;
482 
483     case ENUM_GET_DEV_DESC:
484         /* get full device descriptor */
485         if (USBH_OK == usbh_devdesc_get (puhost, USB_DEV_DESC_LEN)) {
486             puhost->usr_cb->dev_devdesc_assigned(&puhost->dev_prop.dev_desc);
487 
488             index_mfc_str = puhost->dev_prop.dev_desc.iManufacturer;
489             index_prod_str = puhost->dev_prop.dev_desc.iProduct;
490             index_serial_str = puhost->dev_prop.dev_desc.iSerialNumber;
491 
492             puhost->enum_state = ENUM_SET_ADDR;
493         }
494         break;
495 
496     case ENUM_SET_ADDR:
497         /* set address */
498         if (USBH_OK == usbh_setaddress (puhost, USBH_DEV_ADDR)) {
499             usb_mdelay (2U);
500 
501             puhost->dev_prop.addr = USBH_DEV_ADDR;
502 
503             /* user callback for device address assigned */
504             puhost->usr_cb->dev_address_set();
505 
506             /* modify control channels to update device address */
507             usbh_pipe_update (&usbh_core,
508                               puhost->control.pipe_in_num,
509                               puhost->dev_prop.addr,
510                               0U, 0U);
511 
512             usbh_pipe_update (&usbh_core,
513                               puhost->control.pipe_out_num,
514                               puhost->dev_prop.addr,
515                               0U, 0U);
516 
517             puhost->enum_state = ENUM_GET_CFG_DESC;
518         }
519         break;
520 
521     case ENUM_GET_CFG_DESC:
522         /* get standard configuration descriptor */
523         if (USBH_OK == usbh_cfgdesc_get (puhost, USB_CFG_DESC_LEN)) {
524             puhost->enum_state = ENUM_GET_CFG_DESC_SET;
525         }
526         break;
527 
528     case ENUM_GET_CFG_DESC_SET:
529         /* get full config descriptor (config, interface, endpoints) */
530         if (USBH_OK == usbh_cfgdesc_get (puhost, puhost->dev_prop.cfg_desc_set.cfg_desc.wTotalLength)) {
531             /* user callback for configuration descriptors available */
532             puhost->usr_cb->dev_cfgdesc_assigned (&puhost->dev_prop.cfg_desc_set.cfg_desc,
533                                                   &puhost->dev_prop.cfg_desc_set.itf_desc_set[0][0].itf_desc,
534                                                   &puhost->dev_prop.cfg_desc_set.itf_desc_set[0][0].ep_desc[0]);
535 
536             puhost->enum_state = ENUM_GET_STR_DESC;
537         }
538         break;
539 
540     case ENUM_GET_STR_DESC:
541         if (index_mfc_str) {
542             if (USBH_OK == usbh_strdesc_get (puhost,
543                                              puhost->dev_prop.dev_desc.iManufacturer,
544                                              str_buf,
545                                              0xFFU)) {
546                 /* user callback for manufacturing string */
547                 puhost->usr_cb->dev_mfc_str(str_buf);
548 
549                 index_mfc_str = 0U;
550             }
551         } else {
552             if (index_prod_str) {
553                 /* check that product string is available */
554                 if (USBH_OK == usbh_strdesc_get (puhost,
555                                                  puhost->dev_prop.dev_desc.iProduct,
556                                                  str_buf,
557                                                  0xFFU)) {
558                     puhost->usr_cb->dev_prod_str(str_buf);
559 
560                     index_prod_str = 0U;
561                 }
562             } else {
563                 if (index_serial_str) {
564                     if (USBH_OK == usbh_strdesc_get (puhost,
565                                                      puhost->dev_prop.dev_desc.iSerialNumber,
566                                                      str_buf,
567                                                      0xFFU)) {
568                         puhost->usr_cb->dev_seral_str(str_buf);
569                         puhost->enum_state = ENUM_SET_CONFIGURATION;
570                         index_serial_str = 0U;
571                     }
572                 } else {
573                     puhost->enum_state = ENUM_SET_CONFIGURATION;
574                 }
575             }
576         }
577         break;
578 
579     case ENUM_SET_CONFIGURATION:
580         if (USBH_OK == usbh_setcfg (puhost, (uint16_t)puhost->dev_prop.cfg_desc_set.cfg_desc.bConfigurationValue)) {
581             puhost->enum_state = ENUM_DEV_CONFIGURED;
582         }
583         break;
584 
585     case ENUM_DEV_CONFIGURED:
586         status = USBH_OK;
587         break;
588 
589     default:
590         break;
591     }
592 
593     return status;
594 }
595 
596 
597 #ifdef USB_FS_LOW_PWR_ENABLE
598 
599 /*!
600     \brief      handles the USB resume from suspend mode
601     \param[in]  pudev: pointer to selected USB device
602     \param[out] none
603     \retval     none
604 */
usb_hwp_resume(usb_core_driver * pudev)605 static void usb_hwp_resume(usb_core_driver *pudev)
606 {
607     __IO uint32_t hprt = 0U;
608 
609     /*  switch-on the clocks */
610     *pudev->regs.PWRCLKCTL &= ~PWRCLKCTL_SUCLK;
611 
612     *pudev->regs.PWRCLKCTL &= ~PWRCLKCTL_SHCLK;
613 
614     hprt = usb_port_read(pudev);
615 
616     hprt &= ~HPCS_PSP;
617     hprt |= HPCS_PREM;
618 
619     *pudev->regs.HPCS = hprt;
620 
621     usb_mdelay (20U);
622 
623     hprt &= ~HPCS_PREM;
624 
625     *pudev->regs.HPCS = hprt;
626 }
627 
628 /*!
629     \brief      handles the USB enter to suspend mode
630     \param[in]  pudev: pointer to selected USB device
631     \param[out] none
632     \retval     none
633 */
usb_hwp_suspend(usb_core_driver * pudev)634 static void usb_hwp_suspend(usb_core_driver *pudev)
635 {
636     __IO uint32_t hprt = 0U;
637 
638     hprt = usb_port_read(pudev);
639 
640     hprt |= HPCS_PSP;
641 
642     *pudev->regs.HPCS = hprt;
643 
644     /*  switch-off the clocks */
645     *pudev->regs.PWRCLKCTL |= PWRCLKCTL_SUCLK;
646 
647     *pudev->regs.PWRCLKCTL |= PWRCLKCTL_SHCLK;
648 }
649 
650 #endif
651