M480 BSP  V3.05.001
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 
27 static HUB_DEV_T g_hub_dev[MAX_HUB_DEVICE];
28 
29 static int do_port_reset(HUB_DEV_T *hub, int port);
30 
31 static 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 
46 static 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 
58 static 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  */
75 static 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  */
89 static 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  */
102 static 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)
116  return USBH_ERR_DATA_UNDERRUN;
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  */
126 static 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  */
139 static 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  */
152 static 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);
168  return USBH_ERR_DATA_UNDERRUN;
169  }
170 
171  *wPortStatus = (buff[1] << 8) | buff[0];
172  *wPortChange = (buff[3] << 8) | buff[2];
173  return 0;
174 }
175 
176 static 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 
201 int 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)
215  return USBH_ERR_NOT_MATCHED;
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 
311 void 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 
348 UDEV_DRV_T hub_driver =
349 {
350  hub_probe,
351  hub_disconnect,
352  NULL,
353  NULL
354 };
355 
356 
357 static 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 
390 static 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, PORT_C_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 
434 static 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 
506 static 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 & FS_C_PORT_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 
558 static volatile uint8_t _hub_polling_mutex = 0;
559 
560 static 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 
618 void 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 
685 UDEV_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. ***/
#define HSUSBH_UPSCR_PO_Msk
Definition: hsusbh_reg.h:1143
#define USBH_ERR_TRANSFER
Definition: usbh_lib.h:45
NuMicro peripheral access layer header file.
#define SYS
Definition: M480.h:367
USB Host hub class driver header file.
#define SYS_CSERVER_VERSION_Msk
Definition: sys_reg.h:6034
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
USB Host library exported header file.
#define USBH_ERR_PORT_RESET
Definition: usbh_lib.h:48
uint32_t get_ticks(void)
A function return current tick count.
#define USBH_ERR_DATA_UNDERRUN
Definition: usbh_lib.h:65
#define USBH_ERR_MEMORY_OUT
Definition: usbh_lib.h:32
UDEV_T * udev
Definition: usbh_uac.h:111
#define HSUSBH_UPSCR_PP_Msk
Definition: hsusbh_reg.h:1140
USB Host library header file.
UDEV_T * usbh_find_device(char *hub_id, int port)
Find the device under the specified hub port.
Definition: hub.c:685
#define NULL
NULL pointer.
Definition: M480.h:604
#define USBH_ERR_NOT_MATCHED
Definition: usbh_lib.h:36
#define USBH_OK
Definition: usbh_lib.h:31