M480 BSP V3.05.006
The Board Support Package for M480 Series
hub.c
Go to the documentation of this file.
1/**************************************************************************/
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include "NuMicro.h"
15
16#include "usb.h"
17#include "usbh_lib.h"
18#include "hub.h"
19
20
22
23
24#define HUB_DBGMSG printf
25//#define HUB_DBGMSG(...)
26
27static HUB_DEV_T g_hub_dev[MAX_HUB_DEVICE];
28
29static int do_port_reset(HUB_DEV_T *hub, int port);
30
31static HUB_DEV_T *alloc_hub_device(void)
32{
33 int i;
34 for (i = 0; i < MAX_HUB_DEVICE; i++)
35 {
36 if (g_hub_dev[i].iface == NULL)
37 {
38 memset((char *)&g_hub_dev[i], 0, sizeof(HUB_DEV_T));
39 g_hub_dev[i].port_reset = do_port_reset;
40 return &g_hub_dev[i];
41 }
42 }
43 return NULL;
44}
45
46static void free_hub_device(HUB_DEV_T *hub_dev)
47{
48 int i;
49 for (i = 0; i < MAX_HUB_DEVICE; i++)
50 {
51 if (g_hub_dev[i].iface == hub_dev->iface)
52 {
53 memset((char *)&g_hub_dev[i], 0, sizeof(HUB_DEV_T));
54 }
55 }
56}
57
58static HUB_DEV_T * find_hub_device(IFACE_T *iface)
59{
60 int i;
61 for (i = 0; i < MAX_HUB_DEVICE; i++)
62 {
63 if (g_hub_dev[i].iface == iface)
64 {
65 return &g_hub_dev[i];
66 }
67 }
68 return NULL;
69}
70
71#if 0
72/*
73 * Hub Class-specific Request - "Set Hub Feature"
74 */
75static int set_hub_feature(HUB_DEV_T *hub, int feature_selector, int port)
76{
77 UDEV_T *udev = hub->iface->udev;
78 uint32_t read_len;
79
80 return usbh_ctrl_xfer(udev, REQ_TYPE_OUT | REQ_TYPE_CLASS_DEV | REQ_TYPE_TO_DEV,
81 USB_REQ_SET_FEATURE, feature_selector, 0, 0,
82 NULL, &read_len, 200);
83}
84#endif
85
86/*
87 * Hub Class-specific Request - "Clear Hub Feature"
88 */
89static int clear_hub_feature(HUB_DEV_T *hub, int feature_selector)
90{
91 UDEV_T *udev = hub->iface->udev;
92 uint32_t read_len;
93
94 return usbh_ctrl_xfer(udev, REQ_TYPE_OUT | REQ_TYPE_CLASS_DEV | REQ_TYPE_TO_DEV,
95 USB_REQ_CLEAR_FEATURE, feature_selector, 0, 0,
96 NULL, &read_len, 200);
97}
98
99/*
100 * Hub Class-specific Request - "Get Hub Status"
101 */
102static int get_hub_status(HUB_DEV_T *hub, uint16_t *wHubStatus, uint16_t *wHubChange)
103{
104 UDEV_T *udev = hub->iface->udev;
105 uint8_t buff[4];
106 uint32_t read_len;
107 int ret;
108
109 ret = usbh_ctrl_xfer(udev, REQ_TYPE_IN | REQ_TYPE_CLASS_DEV | REQ_TYPE_TO_DEV,
110 USB_REQ_GET_STATUS, 0, 0, 4,
111 buff, &read_len, 200);
112 if (ret < 0)
113 return ret;
114
115 if (read_len != 4)
117
118 *wHubStatus = (buff[1] << 8) | buff[0];
119 *wHubChange = (buff[3] << 8) | buff[2];
120 return 0;
121}
122
123/*
124 * Hub Class-specific Request - "Set Port Feature"
125 */
126static int set_port_feature(HUB_DEV_T *hub, int feature_selector, int port)
127{
128 UDEV_T *udev = hub->iface->udev;
129 uint32_t read_len;
130
131 return usbh_ctrl_xfer(udev, REQ_TYPE_OUT | REQ_TYPE_CLASS_DEV | REQ_TYPE_TO_OTHER,
132 USB_REQ_SET_FEATURE, feature_selector, port, 0,
133 NULL, &read_len, 200);
134}
135
136/*
137 * Hub Class-specific Request - "Clear Port Feature"
138 */
139static int clear_port_feature(HUB_DEV_T *hub, int feature_selector, int port)
140{
141 UDEV_T *udev = hub->iface->udev;
142 uint32_t read_len;
143
144 return usbh_ctrl_xfer(udev, REQ_TYPE_OUT | REQ_TYPE_CLASS_DEV | REQ_TYPE_TO_OTHER,
145 USB_REQ_CLEAR_FEATURE, feature_selector, port, 0,
146 NULL, &read_len, 200);
147}
148
149/*
150 * Hub Class-specific Request - "Get Port Status"
151 */
152static int get_port_status(HUB_DEV_T *hub, int port, uint16_t *wPortStatus, uint16_t *wPortChange)
153{
154 UDEV_T *udev = hub->iface->udev;
155 uint8_t buff[4];
156 uint32_t read_len;
157 int ret;
158
159 ret = usbh_ctrl_xfer(udev, REQ_TYPE_IN | REQ_TYPE_CLASS_DEV | REQ_TYPE_TO_OTHER,
160 USB_REQ_GET_STATUS, 0, port, 4,
161 buff, &read_len, 200);
162 if (ret < 0)
163 return ret;
164
165 if (read_len != 4)
166 {
167 USB_error("HUB [%s] get_port_status read_len!=4. (%d).\n", hub->pos_id, read_len);
169 }
170
171 *wPortStatus = (buff[1] << 8) | buff[0];
172 *wPortChange = (buff[3] << 8) | buff[2];
173 return 0;
174}
175
176static void hub_status_irq(UTR_T *utr)
177{
178 HUB_DEV_T *hub;
179 int i;
180
181 // HUB_DBGMSG("hub_read_irq - %d\n", utr->xfer_len);
182
183 hub = (HUB_DEV_T *)utr->context;
184
185 if (utr->status != 0)
186 {
187 USB_error("hub_status_irq - has error: 0x%x\n", utr->status);
188 return;
189 }
190
191 if (utr->xfer_len)
192 {
193 for (i = 0; i < utr->xfer_len; i++)
194 {
195 hub->sc_bitmap |= (utr->buff[i] << (i * 8));
196 }
197 // HUB_DBGMSG("hub_status_irq - status bitmap: 0x%x\n", hub->sc_bitmap);
198 }
199}
200
201int hub_probe(IFACE_T *iface)
202{
203 UDEV_T *udev = iface->udev;
204 ALT_IFACE_T *aif = iface->aif;
205 EP_INFO_T *ep;
206 HUB_DEV_T *hub;
207 UTR_T *utr;
208 uint32_t read_len;
209 int i, ret;
210 DESC_HUB_T desc_hub;
211 char str[2] = "0";
212
213 /* Is this interface HID class? */
214 if (aif->ifd->bInterfaceClass != USB_CLASS_HUB)
216
217 /*
218 * Try to find an interrupt endpoint
219 */
220 for (i = 0; i < aif->ifd->bNumEndpoints; i++)
221 {
222 if (((aif->ep[i].bmAttributes & EP_ATTR_TT_MASK) == EP_ATTR_TT_INT) &&
223 ((aif->ep[i].bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_IN))
224 {
225 ep = &aif->ep[i];
226 break;
227 }
228 }
229
230 if (ep == NULL)
231 return USBH_ERR_NOT_MATCHED; /* no INT-in endpoints, Ignore this interface */
232
233 hub = alloc_hub_device(); /* allocate hub device */
234 if (hub == NULL)
235 return USBH_ERR_MEMORY_OUT; /* out of memory */
236
237 hub->iface = iface; /* assign interface device pointer */
238 iface->context = (void *)hub;
239
240 str[0] += udev->port_num;
241 if (udev->parent == NULL) /* is connected under the root hub? */
242 strcpy(hub->pos_id, str); /* create hub position identifier string */
243 else
244 {
245 strcpy(hub->pos_id, udev->parent->pos_id);
246 strcat(hub->pos_id, str);
247 }
248
249 HUB_DBGMSG("hub found is:[%s] - device (vid=0x%x, pid=0x%x), interface %d.\n", hub->pos_id,
250 udev->descriptor.idVendor, udev->descriptor.idProduct, iface->if_num);
251
252 /*------------------------------------------------------------------------------------*/
253 /* Get Hub descriptor and parse to get information */
254 /*------------------------------------------------------------------------------------*/
255 ret = usbh_ctrl_xfer(udev, REQ_TYPE_IN | REQ_TYPE_CLASS_DEV | REQ_TYPE_TO_DEV,
256 USB_REQ_GET_DESCRIPTOR,
257 ((USB_DT_CLASS | 0x9) << 8), /* Hub descriptor type: 29H */
258 0, sizeof(desc_hub),
259 (uint8_t *)&desc_hub, &read_len, 200);
260 if (ret < 0)
261 {
262 USB_error("Failed to get hub descriptor!\n");
263 }
264 else
265 {
266 hub->bNbrPorts = desc_hub.bNbrPorts;
267 hub->bPwrOn2PwrGood = desc_hub.bPwrOn2PwrGood * 2;
268 HUB_DBGMSG("Hub has %d ports, power-to-power-good time is %d ms.\n", hub->bNbrPorts, hub->bPwrOn2PwrGood);
269 }
270
271 /*------------------------------------------------------------------------------------*/
272 /* Enable all hub port power */
273 /*------------------------------------------------------------------------------------*/
274 for (i = 1; i <= hub->bNbrPorts; i++)
275 {
276 ret = set_port_feature(hub, FS_PORT_POWER, i);
277 if (ret == 0)
278 HUB_DBGMSG("Hub [%s] port %d power enabled.\n", hub->pos_id, i);
279 else
280 HUB_DBGMSG("Hub [%s] port %d power enabling failed!\n", hub->pos_id, i);
281 }
282
283 delay_us(hub->bPwrOn2PwrGood * 1000 + 100000); /* delay to wait hub power ready */
284
285 utr = alloc_utr(udev); /* allocate an UTR for INT-in transfer */
286 if (utr == NULL)
287 {
288 free_hub_device(hub);
289 return USBH_ERR_MEMORY_OUT; /* out of memory */
290 }
291 hub->utr = utr;
292 utr->context = hub; /* hook backward link to hub device */
293 utr->ep = ep; /* the INT-in endpoint found earlier */
294 utr->buff = hub->buff; /* INT-in data receiving buffer */
295 utr->data_len = HUB_STATUS_MAX_BYTE; /* maximum length of data of INT-in status */
296 utr->xfer_len = 0;
297 utr->func = hub_status_irq; /* interrupt in transfer done callback */
298
299 ret = usbh_int_xfer(utr); /* submit the INT-in transfer */
300 if (ret < 0)
301 {
302 HUB_DBGMSG("Error - failed to submit interrupt read request (%d)", ret);
303 free_utr(utr);
304 free_hub_device(hub);
305 return USBH_ERR_TRANSFER;
306 }
307 HUB_DBGMSG("hub_probe OK.\n");
308 return 0;
309}
310
311void hub_disconnect(IFACE_T *iface)
312{
313 HUB_DEV_T *hub;
314 UDEV_T *udev;
315 int port;
316
317 hub = find_hub_device(iface); /* find the hub device by inface device */
318 if (hub == NULL)
319 {
320 HUB_DBGMSG("hub_disconnect - hub not found!\n");
321 return;
322 }
323
324 /*
325 * disconnect all device under this hub
326 */
327 for (port = 1; port <= hub->bNbrPorts; port++)
328 {
329 udev = usbh_find_device(hub->pos_id, port);
330 if (udev != NULL)
331 {
332 HUB_DBGMSG("Disconnect HUB [%s] port %d device 0x%x:0x%x\n", hub->pos_id, port, udev->descriptor.idVendor, udev->descriptor.idProduct);
333 disconnect_device(udev);
334 }
335 }
336
337 if (hub->utr)
338 {
339 usbh_quit_utr(hub->utr);
340 free_utr(hub->utr);
341 }
342
343 HUB_DBGMSG("Disconnect HUB [%s].\n", hub->pos_id);
344 free_hub_device(hub);
345}
346
347
348UDEV_DRV_T hub_driver =
349{
350 hub_probe,
351 hub_disconnect,
352 NULL,
353 NULL
354};
355
356
357static int hub_status_change(HUB_DEV_T *hub)
358{
359 uint16_t wHubStatus, wHubChange;
360 int ret;
361
362 HUB_DBGMSG("Hub [%s] hub status change 0x%x.\n", hub->pos_id, hub->sc_bitmap);
363
364 ret = get_hub_status(hub, &wHubStatus, &wHubChange);
365 if (ret < 0)
366 {
367 USB_error("Failed to get Hub [%s] status! (%d)\n", hub->pos_id, ret);
368 return ret;
369 }
370
371 printf("Hub [%s] status: 0x%x, change: 0x%x\n", hub->pos_id, wHubStatus, wHubChange);
372
373 if (wHubChange & HUB_C_LOCAL_POWER) /* has local power change? */
374 {
375 ret = clear_hub_feature(hub, FS_C_HUB_LOCAL_POWER); /* clear local power change */
376 if (ret < 0)
377 return ret; /* class command failed */
378 }
379
380 if (wHubChange & HUB_C_OVERCURRENT) /* has over-current change? */
381 {
382 ret = clear_hub_feature(hub, FS_C_HUB_OVER_CURRENT); /* clear change */
383 if (ret < 0)
384 return ret; /* class command failed */
385 }
386
387 return 0;
388}
389
390static int do_port_reset(HUB_DEV_T *hub, int port)
391{
392 int retry;
393 int reset_time;
394 uint32_t t0;
395 uint16_t wPortStatus, wPortChange;
396 int ret;
397
398 reset_time = PORT_RESET_TIME_MS; /* initial reset time */
399
400 for (retry = 0; retry < PORT_RESET_RETRY; retry++)
401 {
402 ret = set_port_feature(hub, FS_PORT_RESET, port); /* submit a port reset */
403 if (ret < 0)
404 return ret; /* class command failed */
405
406 t0 = get_ticks(); /* get start time */
407 while (get_ticks() - t0 < (reset_time/10)+1) /* time-out? */
408 {
409 delay_us(5000); /* wait 5 ms */
410
411 ret = get_port_status(hub, port, &wPortStatus, &wPortChange);
412 if (ret < 0)
413 {
414 USB_error("Failed to get Hub [%s] port %d status! (%d)\n", hub->pos_id, port, ret);
415 return ret;
416 }
417
418 /*
419 * If device is disconnected or port enabled, we can stop port reset.
420 */
421 if (((wPortStatus & PORT_S_CONNECTION) == 0) ||
422 ((wPortStatus & (PORT_S_CONNECTION | PORT_S_ENABLE)) == (PORT_S_CONNECTION | PORT_S_ENABLE)))
423 {
424 clear_port_feature(hub, FS_C_PORT_ENABLE, port); /* clear port enable change */
425 return USBH_OK;
426 }
427 }
428 reset_time += PORT_RESET_RETRY_INC_MS; /* increase reset time */
429 }
430 USB_debug("HUB [%s] port %d - port reset failed!\n", hub->pos_id, port);
431 return USBH_ERR_PORT_RESET;
432}
433
434static int port_connect_change(HUB_DEV_T *hub, int port, uint16_t wPortStatus)
435{
436 UDEV_T *udev;
437 uint16_t wPortChange;
438 int ret;
439
440 if (wPortStatus & PORT_S_CONNECTION)
441 {
442 /*--------------------------------------------------------------------------------*/
443 /* First of all, check if there's any previously connected device. */
444 /*--------------------------------------------------------------------------------*/
445 udev = usbh_find_device(hub->pos_id, port);
446 if (udev != NULL)
447 {
448 disconnect_device(udev);
449 }
450
451 /*
452 * New device connected. Do a port reset first.
453 */
454 ret = do_port_reset(hub, port);
455 if (ret < 0)
456 return ret;
457
458 ret = get_port_status(hub, port, &wPortStatus, &wPortChange);
459 if (ret < 0)
460 {
461 USB_error("Failed to get Hub [%s] port %d status! (%d)\n", hub->pos_id, port, ret);
462 return ret;
463 }
464 printf("Hub [%s] port %d, status: 0x%x, change: 0x%x\n", hub->pos_id, port, wPortStatus, wPortChange);
465
466 /*
467 * Port reset success. Create and enumerate this device.
468 */
469 udev = alloc_device();
470 if (udev == NULL)
471 return USBH_ERR_MEMORY_OUT; /* unlikely, out of memory */
472
473 udev->parent = hub;
474 udev->port_num = port;
475
476 if (wPortStatus & PORT_S_HIGH_SPEED)
477 udev->speed = SPEED_HIGH;
478 else if (wPortStatus & PORT_S_LOW_SPEED)
479 udev->speed = SPEED_LOW;
480 else
481 udev->speed = SPEED_FULL;
482
483 udev->hc_driver = hub->iface->udev->hc_driver;
484
485 ret = connect_device(udev);
486 if (ret < 0)
487 {
488 USB_error("connect_device error! [%d]\n", ret);
489 free_device(udev);
490 }
491 }
492 else
493 {
494 /*
495 * Device disconnected
496 */
497 udev = usbh_find_device(hub->pos_id, port);
498 if (udev != NULL)
499 {
500 disconnect_device(udev);
501 }
502 }
503 return 0;
504}
505
506static int port_status_change(HUB_DEV_T *hub, int port)
507{
508 uint16_t wPortStatus, wPortChange;
509 int ret;
510
511 ret = get_port_status(hub, port, &wPortStatus, &wPortChange);
512 if (ret < 0)
513 {
514 USB_error("Failed to get Hub [%s] port %d status! (%d)\n", hub->pos_id, port, ret);
515 return ret;
516 }
517 printf("Hub [%s] port %d, status: 0x%x, change: 0x%x\n", hub->pos_id, port, wPortStatus, wPortChange);
518
519 if (wPortChange & PORT_C_CONNECTION) /* have port connection change? */
520 {
521 ret = clear_port_feature(hub, FS_C_PORT_CONNECTION, port); /* clear port change */
522 if (ret < 0)
523 return ret; /* class command failed */
524
525 port_connect_change(hub, port, wPortStatus);
526 }
527
528 if (wPortChange & PORT_C_ENABLE) /* have port enable change? */
529 {
530 ret = clear_port_feature(hub, FS_C_PORT_ENABLE, port); /* clear port change */
531 if (ret < 0)
532 return ret; /* class command failed */
533 }
534
535 if (wPortChange & PORT_C_SUSPEND) /* have port suspend change? */
536 {
537 ret = clear_port_feature(hub, FS_C_PORT_SUSPEND, port); /* clear port change */
538 if (ret < 0)
539 return ret; /* class command failed */
540 }
541
542 if (wPortChange & PORT_C_OVERCURRENT) /* have port over-current change? */
543 {
544 ret = clear_port_feature(hub, FS_C_PORT_OVER_CURRENT, port); /* clear port change */
545 if (ret < 0)
546 return ret; /* class command failed */
547 }
548
549 if (wPortChange & PORT_C_RESET) /* have port reset change? */
550 {
551 ret = clear_port_feature(hub, FS_C_PORT_RESET, port); /* clear port change */
552 if (ret < 0)
553 return ret; /* class command failed */
554 }
555 return 0;
556}
557
558static volatile uint8_t _hub_polling_mutex = 0;
559
560static int hub_polling(void)
561{
562 HUB_DEV_T *hub;
563 UTR_T *utr;
564 int i, ret, port, change = 0;
565
566 if (_hub_polling_mutex) /* do nothing */
567 return 0;
568
569 _hub_polling_mutex = 1;
570
571 for (i = 0; i < MAX_HUB_DEVICE; i++)
572 {
573 if ((g_hub_dev[i].iface != NULL) && (g_hub_dev[i].sc_bitmap))
574 {
575 /*
576 * This hub device has status change
577 */
578 hub = &g_hub_dev[i];
579 change = 1;
580
581 // HUB_DBGMSG("HUB [%s] hub status change 0x%x.\n", hub->pos_id, hub->sc_bitmap);
582
583 if (hub->sc_bitmap & 0x1)
584 hub_status_change(hub);
585
586 for (port = 1; port <= hub->bNbrPorts; port++)
587 {
588 if (hub->sc_bitmap & (1 << port))
589 {
590 ret = port_status_change(hub, port);
591 if (ret < 0)
592 break;
593 }
594 }
595 hub->sc_bitmap = 0;
596 /* re-submit interrupt-in transfer */
597 if (ret == 0)
598 {
599 utr = hub->utr;
600 utr->xfer_len = 0;
601 ret = usbh_int_xfer(utr);
602 if (ret)
603 {
604 USB_error("Failed to re-submit HUB [%s] interrupt-in request (%d)", hub->pos_id, ret);
605 }
606 }
607 }
608 }
609 _hub_polling_mutex = 0;
610 return change;
611}
612
613
618void usbh_hub_init(void)
619{
620 memset((char *)&g_hub_dev[0], 0, sizeof(g_hub_dev));
621 usbh_register_driver(&hub_driver);
622}
623
624
626
638{
639 int ret, change = 0;
640
641#ifdef ENABLE_EHCI
642 if ((SYS->CSERVER & SYS_CSERVER_VERSION_Msk) == 0x0) /* Only M480MD has EHCI. */
643 {
644 _ehci->UPSCR[1] = HSUSBH_UPSCR_PP_Msk | HSUSBH_UPSCR_PO_Msk; /* set port 2 owner to OHCI */
645 do
646 {
647 ret = ehci_driver.rthub_polling();
648 if (ret)
649 change = 1;
650 }
651 while (ret == 1);
652 }
653#endif
654
655#ifdef ENABLE_OHCI
656 do
657 {
658 ret = ohci_driver.rthub_polling();
659 if (ret)
660 change = 1;
661 }
662 while (ret == 1);
663#endif
664
665
666 do
667 {
668 ret = hub_polling();
669 if (ret)
670 change = 1;
671 }
672 while (ret == 1);
673
674 return change;
675}
676
677
685UDEV_T * usbh_find_device(char *hub_id, int port)
686{
687 int i;
688 HUB_DEV_T *hub = NULL;
689 UDEV_T *udev;
690
691 for (i = 0; i < MAX_HUB_DEVICE; i++)
692 {
693 if ((g_hub_dev[i].iface != NULL) && (strcmp(g_hub_dev[i].pos_id, hub_id) == 0))
694 {
695 hub = &g_hub_dev[i];
696 break;
697 }
698 }
699 if (hub == NULL)
700 return NULL;
701
702 udev = g_udev_list;
703 while (udev != NULL)
704 {
705 if ((udev->parent == hub) && (udev->port_num == port))
706 return udev;
707 udev = udev->next;
708 }
709 return NULL;
710}
711
712
713/*** (C) COPYRIGHT 2017 Nuvoton Technology Corp. ***/
NuMicro peripheral access layer header file.
#define NULL
NULL pointer.
Definition: M480.h:605
#define SYS
Definition: M480.h:367
#define SYS_CSERVER_VERSION_Msk
Definition: sys_reg.h:6034
#define HSUSBH_UPSCR_PP_Msk
Definition: hsusbh_reg.h:1140
#define HSUSBH_UPSCR_PO_Msk
Definition: hsusbh_reg.h:1143
#define USBH_ERR_DATA_UNDERRUN
Definition: usbh_lib.h:65
#define USBH_ERR_TRANSFER
Definition: usbh_lib.h:45
#define USBH_OK
Definition: usbh_lib.h:31
#define USBH_ERR_NOT_MATCHED
Definition: usbh_lib.h:36
#define USBH_ERR_PORT_RESET
Definition: usbh_lib.h:48
#define USBH_ERR_MEMORY_OUT
Definition: usbh_lib.h:32
UDEV_T * usbh_find_device(char *hub_id, int port)
Find the device under the specified hub port.
Definition: hub.c:685
HIDDEN_SYMBOLS int usbh_pooling_hubs(void)
Let USB stack polls all root hubs and downstream hubs. If there's any hub port change found,...
Definition: hub.c:637
uint32_t get_ticks(void)
A function return current tick count.
USB Host hub class driver header file.
USB Host library header file.
USB Host library exported header file.