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