1 /* *********************************************************************
2 * Broadcom Common Firmware Environment (CFE)
4 * USB Hub and device discovery code File: usbhub.c
6 * This module deals with hubs and device discovery.
8 * Author: Mitch Lichtenberg (mpl@broadcom.com)
10 *********************************************************************
12 * Copyright 2000,2001,2002,2003
13 * Broadcom Corporation. All rights reserved.
15 * This software is furnished under license and may be used and
16 * copied only in accordance with the following terms and
17 * conditions. Subject to these conditions, you may download,
18 * copy, install, use, modify and distribute modified or unmodified
19 * copies of this software in source and/or binary form. No title
20 * or ownership is transferred hereby.
22 * 1) Any source code used, modified or distributed must reproduce
23 * and retain this copyright notice and list of conditions
24 * as they appear in the source file.
26 * 2) No right is granted to use any trade name, trademark, or
27 * logo of Broadcom Corporation. The "Broadcom Corporation"
28 * name may not be used to endorse or promote products derived
29 * from this software without the prior written permission of
30 * Broadcom Corporation.
32 * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
33 * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
34 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
35 * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
36 * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
37 * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
38 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
40 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
42 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
43 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
44 * THE POSSIBILITY OF SUCH DAMAGE.
45 ********************************************************************* */
55 #include "lib_types.h"
56 #include "lib_string.h"
57 #include "lib_printf.h"
58 #include "cfe_console.h"
61 #include "lib_malloc.h"
62 #include "lib_queue.h"
66 /* *********************************************************************
67 * Macros for common hub requests
68 ********************************************************************* */
70 #define usbhub_set_port_feature(dev,port,feature) \
71 usb_simple_request(dev,0x23,USB_HUBREQ_SET_FEATURE,feature,port)
73 #define usbhub_set_hub_feature(dev,feature) \
74 usb_simple_request(dev,0x20,USB_HUBREQ_SET_FEATURE,feature,0)
76 #define usbhub_clear_port_feature(dev,port,feature) \
77 usb_simple_request(dev,0x23,USB_HUBREQ_CLEAR_FEATURE,feature,port)
79 #define usbhub_clear_hub_feature(dev,feature) \
80 usb_simple_request(dev,0x20,USB_HUBREQ_CLEAR_FEATURE,feature,0)
83 /* *********************************************************************
85 ********************************************************************* */
89 /* *********************************************************************
90 * Forward declarations
91 ********************************************************************* */
93 static int usbhub_attach(usbdev_t
*dev
,usb_driver_t
*drv
);
94 static int usbhub_detach(usbdev_t
*dev
);
96 /* *********************************************************************
97 * Hub-specific data structures
98 ********************************************************************* */
100 #define UHUB_MAX_DEVICES 8
102 #define UHUB_FLG_NEEDSCAN 1
104 typedef struct usbhub_softc_s
{
105 usb_hub_descr_t uhub_descr
;
106 usb_hub_status_t uhub_status
;
110 unsigned int uhub_flags
;
111 uint8_t uhub_imsg
[8];
112 usbdev_t
*uhub_devices
[UHUB_MAX_DEVICES
];
115 usb_driver_t usbhub_driver
= {
122 /* *********************************************************************
123 * usbhub_ireq_callback(ur)
125 * this routine is called when the transfer we queued to the
126 * interrupt endpoint on the hub completes. It means that
127 * *some* port on the hub needs attention. The data indicates
128 * which port, but for our purposes we don't really care - if
129 * we get this callback, we'll set a flag and re-probe the bus.
132 * ur - usbreq that completed
136 ********************************************************************* */
138 static int usbhub_ireq_callback(usbreq_t
*ur
)
141 usbhub_softc_t
*uhub
= (ur
->ur_dev
->ud_private
);
144 * Check to see if the request was cancelled by someone
145 * deleting our endpoint.
148 if (ur
->ur_status
== 0xFF) {
149 usb_free_request(ur
);
154 * Check to see if any of our ports need attention
157 for (idx
= 1; idx
<= uhub
->uhub_nports
; idx
++) {
158 if (ur
->ur_buffer
[0] & (1<<idx
)) {
161 * Mark the hub as needing a scan, and mark the bus as well
162 * so the top-level polling will notice.
165 uhub
->uhub_flags
|= UHUB_FLG_NEEDSCAN
;
166 ur
->ur_dev
->ud_bus
->ub_flags
|= UB_FLG_NEEDSCAN
;
172 * Do NOT requeue the request here. We will do this
173 * during exploration.
176 usb_free_request(ur
);
182 /* *********************************************************************
183 * usbhub_get_hub_descriptor(dev,dscr,idx,maxlen)
185 * Obtain the hub descriptor (special for hubs) from the
190 * dscr - place to put hub descriptor
191 * idx - which hub descriptor to get (usually zero)
192 * maxlen - max # of bytes to return
195 * # of bytes returned
196 ********************************************************************* */
198 static int usbhub_get_hub_descriptor(usbdev_t
*dev
,usb_hub_descr_t
*dscr
,int idx
,int maxlen
)
200 return usb_std_request(dev
,0xA0,
201 USB_HUBREQ_GET_DESCRIPTOR
,
208 /* *********************************************************************
209 * usbhub_get_hub_status(dev,status)
211 * Obtain the hub status (special for hubs) from the
216 * status - where to put hub status structure
219 * # of bytes returned
220 ********************************************************************* */
224 /* *********************************************************************
225 * usbhub_get_port_status(dev,port,status)
227 * Obtain the port status for a particular port from
232 * port - 1-based port number
233 * status - where to put port status structure
236 * # of bytes returned
237 ********************************************************************* */
239 static int usbhub_get_port_status(usbdev_t
*dev
,int port
,usb_port_status_t
*status
)
241 return usb_std_request(dev
,
247 sizeof(usb_port_status_t
));
251 /* *********************************************************************
252 * usbhub_queue_intreq(dev,softc)
254 * Queue the transfer to the interrupt pipe that will catch
255 * the hub's port status changes
259 * softc - hub-specific data
263 ********************************************************************* */
265 static void usbhub_queue_intreq(usbdev_t
*dev
,usbhub_softc_t
*softc
)
269 ur
= usb_make_request(dev
,
271 softc
->uhub_imsg
,softc
->uhub_ipipemps
,
272 UR_FLAG_IN
| UR_FLAG_SHORTOK
);
274 ur
->ur_callback
= usbhub_ireq_callback
;
276 usb_queue_request(ur
);
280 /* *********************************************************************
281 * usbhub_attach(dev,drv)
283 * This routine is called when the hub attaches to the system.
284 * We complete initialization for the hub and set things up so
285 * that an explore will happen soon.
289 * drv - driver structure
293 ********************************************************************* */
295 static int usbhub_attach(usbdev_t
*dev
,usb_driver_t
*drv
)
297 usb_device_status_t devstatus
;
298 usb_config_descr_t
*cfgdscr
;
299 usb_interface_descr_t
*ifdscr
;
300 usb_endpoint_descr_t
*epdscr
;
301 usbhub_softc_t
*softc
;
304 * Remember the driver dispatch.
309 softc
= KMALLOC(sizeof(usbhub_softc_t
),0);
310 memset(softc
,0,sizeof(usbhub_softc_t
));
311 dev
->ud_private
= softc
;
314 * Dig out the data from the configuration descriptor
315 * (we got this from the device before attach time)
318 cfgdscr
= dev
->ud_cfgdescr
;
319 epdscr
= usb_find_cfg_descr(dev
,USB_ENDPOINT_DESCRIPTOR_TYPE
,0);
320 ifdscr
= usb_find_cfg_descr(dev
,USB_INTERFACE_DESCRIPTOR_TYPE
,0);
323 * Get device status (is this really necessary?)
326 usb_get_device_status(dev
,&devstatus
);
329 * Set us to configuration index 0
332 usb_set_configuration(dev
,cfgdscr
->bConfigurationValue
);
335 * Get the hub descriptor. Get the first 8 bytes first, then get the rest
339 if (usbhub_get_hub_descriptor(dev
,&(softc
->uhub_descr
),0,USB_HUB_DESCR_SIZE
) > USB_HUB_DESCR_SIZE
) {
340 usbhub_get_hub_descriptor(dev
,&(softc
->uhub_descr
),0,softc
->uhub_descr
.bDescriptorLength
);
344 * remember stuff from the hub descriptor
347 softc
->uhub_nports
= softc
->uhub_descr
.bNumberOfPorts
;
350 * Open the interrupt pipe
353 softc
->uhub_ipipe
= usb_open_pipe(dev
,epdscr
);
354 softc
->uhub_ipipemps
= GETUSBFIELD(epdscr
,wMaxPacketSize
);
357 * Mark the bus and the hub as needing service.
360 softc
->uhub_flags
|= UHUB_FLG_NEEDSCAN
;
361 dev
->ud_bus
->ub_flags
|= UB_FLG_NEEDSCAN
;
364 * Okay, that's it. The top-level USB daemon will notice
365 * that the bus needs service and will invoke the exploration code.
366 * This may in turn require additional explores until
367 * everything settles down.
374 /* *********************************************************************
377 * Called when a hub is removed from the system - we remove
378 * all subordinate devicees.
381 * dev - device (hub) that was removed
385 ********************************************************************* */
387 static int usbhub_detach(usbdev_t
*dev
)
393 if (!IS_HUB(dev
)) return 0; /* should not happen */
395 hub
= dev
->ud_private
;
396 for (idx
= 0; idx
< UHUB_MAX_DEVICES
; idx
++) {
397 deldev
= hub
->uhub_devices
[idx
];
399 console_log("USB: Removing device attached to bus %d hub %d port %d",
401 dev
->ud_address
,idx
+1);
402 if (deldev
->ud_drv
) {
403 (*(deldev
->ud_drv
->udrv_detach
))(deldev
);
407 console_log("USB: Detached device on bus %d hub %d port %d "
410 dev
->ud_address
,idx
+1);
413 if (deldev
->ud_cfgdescr
) KFREE(deldev
->ud_cfgdescr
);
414 usb_destroy_device(deldev
);
418 KFREE(hub
); /* remove softc */
425 /* *********************************************************************
426 * usbhub_map_tree1(dev,level,func,arg)
428 * This routine is used in recursive device tree exploration.
429 * We call 'func' for each device at this tree, and descend
430 * when we run into hubs
433 * dev - current device pointer
434 * level - current nesting level
435 * func - function to call
436 * arg - argument to pass to function
440 ********************************************************************* */
442 static void usbhub_map_tree1(usbdev_t
*dev
,int level
,
443 int (*func
)(usbdev_t
*dev
,void *arg
),void *arg
)
451 hub
= dev
->ud_private
;
452 for (idx
= 0; idx
< UHUB_MAX_DEVICES
; idx
++) {
453 if (hub
->uhub_devices
[idx
]) {
454 usbhub_map_tree1(hub
->uhub_devices
[idx
],level
+1,func
,arg
);
460 /* *********************************************************************
461 * usbhub_map_tree(bus,func,arg)
463 * Call a function for each device in the tree
467 * func - function to call
468 * arg - argument to pass to function
472 ********************************************************************* */
474 void usbhub_map_tree(usbbus_t
*bus
,int (*func
)(usbdev_t
*dev
,void *arg
),void *arg
)
476 usbhub_map_tree1(bus
->ub_roothub
,0,func
,arg
);
480 /* *********************************************************************
481 * usbhub_dumpbus1(dev,arg)
483 * map function to dump devices in the device tree
486 * dev - device we're working on
487 * arg - argument from map_tree call
491 ********************************************************************* */
493 static int usbhub_dumpbus1(usbdev_t
*dev
,void *arg
)
495 uint32_t *verbose
= (uint32_t *) arg
;
497 if ((*verbose
& 0x00FF) && (dev
->ud_address
!= (*verbose
& 0x00FF))) return 0;
499 if (*verbose
& 0x100) {
500 printf("============================================================================\n");
503 printf("Bus %d Device %d Class %d Vendor %04X Product %04X ",
506 dev
->ud_devdescr
.bDeviceClass
,
507 GETUSBFIELD(&(dev
->ud_devdescr
),idVendor
),
508 GETUSBFIELD(&(dev
->ud_devdescr
),idProduct
));
515 printf("[DEVICE]\n");
518 if (*verbose
& 0x100) {
519 usb_dbg_dumpdescriptors(dev
,(uint8_t *) &(dev
->ud_devdescr
),dev
->ud_devdescr
.bLength
);
520 usb_dbg_dumpcfgdescr(dev
);
527 /* *********************************************************************
528 * usbhub_dumpbus(bus,verbose)
530 * Dump information about devices on the USB bus.
534 * verbose - nonzero to display more info, like descriptors
538 ********************************************************************* */
540 void usbhub_dumpbus(usbbus_t
*bus
,uint32_t verbose
)
542 usbhub_map_tree(bus
,usbhub_dumpbus1
,&verbose
);
547 /* *********************************************************************
548 * usbhub_reset_devicee(dev,port,status)
550 * Reset a device on a hub port. This routine does a
551 * USB_PORT_FEATURE_RESET on the specified port, waits for the
552 * bit to clear, and returns. It is used to get a device into the
553 * DEFAULT state according to the spec.
557 * port - port number(1-based)
558 * status - place to return port_status structure after
563 ********************************************************************* */
565 static void usbhub_reset_device(usbdev_t
*dev
,int port
,usb_port_status_t
*portstatus
)
567 console_log("USB: Resetting device on bus %d port %d",dev
->ud_bus
->ub_num
,port
);
572 usbhub_set_port_feature(dev
,port
,USB_PORT_FEATURE_RESET
);
574 usbhub_get_port_status(dev
,port
,portstatus
);
577 usbhub_get_port_status(dev
,port
,portstatus
);
578 if ((GETUSBFIELD((portstatus
),wPortStatus
) & USB_PORT_STATUS_RESET
) == 0) break;
579 usb_delay_ms(dev
->ud_bus
,250);
581 usb_delay_ms(dev
->ud_bus
,250);
583 usbhub_clear_port_feature(dev
,port
,USB_PORT_FEATURE_C_PORT_RESET
);
588 /* *********************************************************************
589 * usbhub_scan_ports(dev,arg)
591 * Scan the ports on this hub for new or removed devices.
595 * arg - passed from bus scan main routines
599 ********************************************************************* */
601 static void usbhub_scan_ports(usbdev_t
*dev
,void *arg
)
605 usbhub_softc_t
*softc
;
611 usb_driver_t
*newdrv
;
613 usb_port_status_t portstatus
;
614 usb_config_descr_t cfgdescr
;
615 unsigned int powerondelay
;
617 if (!IS_HUB(dev
)) return; /* should not happen. */
620 * We know this is a hub. Get the softc back.
623 softc
= (usbhub_softc_t
*) dev
->ud_private
;
625 powerondelay
= ((unsigned int) softc
->uhub_descr
.bPowerOnToPowerGood
)*2 + 20;
628 * Turn on the power to the ports whose power is not yet on.
631 for (idx
= 0; idx
< softc
->uhub_nports
; idx
++) {
633 usbhub_get_port_status(dev
,idx
+1,&portstatus
);
635 current
= GETUSBFIELD((&portstatus
),wPortStatus
);
636 changed
= GETUSBFIELD((&portstatus
),wPortChange
);
638 printf("BeforePowerup: port %d status %04X changed %04X\n",idx
+1,current
,changed
);
641 if (!(current
& USB_PORT_STATUS_POWER
)) {
642 if (usb_noisy
> 1) console_log("USB: Powering up bus %d port %d",
643 dev
->ud_bus
->ub_num
,idx
+1);
644 usbhub_set_port_feature(dev
,idx
+1,USB_PORT_FEATURE_POWER
);
645 usb_delay_ms(dev
->ud_bus
,powerondelay
);
650 * Begin exploration at this level.
653 for (idx
= 0; idx
< softc
->uhub_nports
; idx
++) {
655 usbhub_get_port_status(dev
,idx
+1,&portstatus
);
657 current
= GETUSBFIELD((&portstatus
),wPortStatus
);
658 changed
= GETUSBFIELD((&portstatus
),wPortChange
);
661 printf("USB: Explore: Bus %d Hub %d port %d status %04X changed %04X\n",
663 dev
->ud_address
,idx
+1,current
,changed
);
664 usb_dbg_dumpportstatus(idx
+1,&portstatus
,1);
668 // if (changed & USB_PORT_STATUS_RESET) {
669 // usbhub_clear_port_feature(dev,idx+1,USB_PORT_FEATURE_C_PORT_RESET);
672 if (changed
& USB_PORT_STATUS_ENABLED
) {
673 usbhub_clear_port_feature(dev
,idx
+1,USB_PORT_FEATURE_C_PORT_ENABLE
);
676 if (changed
& USB_PORT_STATUS_CONNECT
) {
678 * A device was either connected or disconnected.
679 * Clear the status change first.
682 usbhub_clear_port_feature(dev
,idx
+1,USB_PORT_FEATURE_C_PORT_CONNECTION
);
684 if (current
& USB_PORT_STATUS_CONNECT
) {
687 * The device has been CONNECTED.
690 console_log("USB: New device connected to bus %d hub %d port %d",
692 dev
->ud_address
,idx
+1);
695 * Reset the device. Reuse our old port status structure
696 * so we get the latest status. Some devices do not report
697 * lowspeed until they are reset.
700 usbhub_reset_device(dev
,idx
+1,&portstatus
);
701 current
= GETUSBFIELD((&portstatus
),wPortStatus
);
702 changed
= GETUSBFIELD((&portstatus
),wPortChange
);
705 * Create a device for this port.
708 newdev
= usb_create_device(dev
->ud_bus
,(current
& USB_PORT_STATUS_LOWSPD
) ? 1 : 0);
711 * Get the device descriptor.
714 res
= usb_get_device_descriptor(newdev
,&(newdev
->ud_devdescr
),TRUE
);
716 if (usb_noisy
> 0) usb_dbg_dumpdescriptors(newdev
,(uint8_t *) &(newdev
->ud_devdescr
),8);
719 * Set up the max packet size for the control endpoint,
720 * then get the rest of the descriptor.
723 usb_set_ep0mps(newdev
,newdev
->ud_devdescr
.bMaxPacketSize0
);
724 res
= usb_get_device_descriptor(newdev
,&(newdev
->ud_devdescr
),FALSE
);
727 * Obtain a new address and set the address of the
728 * root hub to this address.
731 addr
= usb_new_address(newdev
->ud_bus
);
732 res
= usb_set_address(newdev
,addr
);
735 * Get the configuration descriptor and all the
736 * associated interface and endpoint descriptors.
739 res
= usb_get_config_descriptor(newdev
,&cfgdescr
,0,
740 sizeof(usb_config_descr_t
));
741 if (res
!= sizeof(usb_config_descr_t
)) {
742 printf("[a]usb_get_config_descriptor returns %d\n",res
);
745 len
= GETUSBFIELD(&cfgdescr
,wTotalLength
);
746 buf
= KMALLOC(len
,0);
748 res
= usb_get_config_descriptor(newdev
,(usb_config_descr_t
*)buf
,0,len
);
750 printf("[b]usb_get_config_descriptor returns %d\n",res
);
753 newdev
->ud_cfgdescr
= (usb_config_descr_t
*) buf
;
755 if (usb_noisy
> 0) usb_dbg_dumpdescriptors(newdev
,buf
,len
);
758 * Point the hub at the devices it owns
761 softc
->uhub_devices
[idx
] = newdev
;
764 * Find the driver for this. It had better be the hub
768 newdrv
= usb_find_driver(newdev
);
771 * Call the attach method.
775 dev
->ud_drv
= newdrv
; /* remember driver dispatch in device */
776 (*(newdrv
->udrv_attach
))(newdev
,newdrv
);
783 * The device has been DISCONNECTED.
786 console_log("USB: Device disconnected from bus %d hub %d port %d",
788 dev
->ud_address
,idx
+1);
791 * Recover pointer to device below hub and clear
795 newdev
= softc
->uhub_devices
[idx
]; /* Get device pointer */
796 softc
->uhub_devices
[idx
] = NULL
; /* remove device from hub */
799 * Deassign the USB device's address and then
800 * call detach method to free resources. Devices that
801 * do not have drivers will not have any methods.
805 if (newdev
->ud_drv
) {
806 (*(newdev
->ud_drv
->udrv_detach
))(newdev
);
810 console_log("USB: Detached device on bus %d hub %d port %d "
813 dev
->ud_address
,idx
+1);
817 if (newdev
->ud_cfgdescr
) KFREE(newdev
->ud_cfgdescr
);
819 usb_destroy_device(newdev
);
829 * Queue up a request for the interrupt pipe. This will catch further
830 * changes at this port.
833 usbhub_queue_intreq(dev
,softc
);
837 /* *********************************************************************
838 * usbhub_scan1(dev,arg)
840 * Scan one device at this level, or descend if we run into a hub
841 * This is part of the device discovery code.
844 * dev - current device, maybe a hub
845 * arg - passed from main scan routine
849 ********************************************************************* */
852 static int usbhub_scan1(usbdev_t
*dev
,void *arg
)
857 * If the device is not a hub, we've reached the leaves of the
861 if (!IS_HUB(dev
)) return 0;
864 * Otherwise, scan the ports on this hub.
867 hub
= dev
->ud_private
;
869 if (hub
->uhub_flags
& UHUB_FLG_NEEDSCAN
) {
870 hub
->uhub_flags
&= ~UHUB_FLG_NEEDSCAN
;
871 usbhub_scan_ports(dev
,arg
);
877 /* *********************************************************************
880 * Scan the bus looking for new or removed devices
887 ********************************************************************* */
889 void usb_scan(usbbus_t
*bus
)
892 * Call our tree walker with the scan function.
895 usbhub_map_tree(bus
,usbhub_scan1
,NULL
);