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