Initial support for USB hubs.
[seabios.git] / src / usb-hub.c
1 // Code for handling standard USB hubs.
2 //
3 // Copyright (C) 2010  Kevin O'Connor <kevin@koconnor.net>
4 //
5 // This file may be distributed under the terms of the GNU LGPLv3 license.
6
7 #include "util.h" // dprintf
8 #include "config.h" // CONFIG_USB_HUB
9 #include "usb-hub.h" // struct usb_hub_descriptor
10 #include "usb.h" // struct usb_s
11
12 static int
13 get_hub_desc(struct usb_hub_descriptor *desc, u32 endp)
14 {
15     struct usb_ctrlrequest req;
16     req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE;
17     req.bRequest = USB_REQ_GET_DESCRIPTOR;
18     req.wValue = USB_DT_HUB<<8;
19     req.wIndex = 0;
20     req.wLength = sizeof(*desc);
21     return send_default_control(endp, &req, desc);
22 }
23
24 static int
25 set_port_feature(int port, int feature, u32 endp)
26 {
27     struct usb_ctrlrequest req;
28     req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
29     req.bRequest = USB_REQ_SET_FEATURE;
30     req.wValue = feature;
31     req.wIndex = port;
32     req.wLength = 0;
33     return send_default_control(endp, &req, NULL);
34 }
35
36 static int
37 clear_port_feature(int port, int feature, u32 endp)
38 {
39     struct usb_ctrlrequest req;
40     req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
41     req.bRequest = USB_REQ_CLEAR_FEATURE;
42     req.wValue = feature;
43     req.wIndex = port;
44     req.wLength = 0;
45     return send_default_control(endp, &req, NULL);
46 }
47
48 static int
49 get_port_status(int port, struct usb_port_status *sts, u32 endp)
50 {
51     struct usb_ctrlrequest req;
52     req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER;
53     req.bRequest = USB_REQ_GET_STATUS;
54     req.wValue = 0;
55     req.wIndex = port;
56     req.wLength = sizeof(*sts);
57     return send_default_control(endp, &req, sts);
58 }
59
60 // Configure a usb hub and then find devices connected to it.
61 int
62 usb_hub_init(u32 endp)
63 {
64     if (!CONFIG_USB_HUB)
65         return 0;
66
67     struct usb_hub_descriptor desc;
68     int ret = get_hub_desc(&desc, endp);
69     if (ret)
70         return ret;
71
72     // Turn on power to all ports.
73     int i;
74     for (i=1; i<=desc.bNbrPorts; i++) {
75         ret = set_port_feature(i, USB_PORT_FEAT_POWER, endp);
76         if (ret)
77             goto fail;
78     }
79
80     // Wait for port detection.
81     msleep(desc.bPwrOn2PwrGood * 2 + USB_TIME_SIGATT);
82     // XXX - should poll for ports becoming active sooner and then
83     // possibly wait USB_TIME_ATTDB.
84
85     // Detect down stream devices.
86     struct usb_s *cntl = endp2cntl(endp);
87     int totalcount = 0;
88     for (i=1; i<=desc.bNbrPorts; i++) {
89         struct usb_port_status sts;
90         ret = get_port_status(i, &sts, endp);
91         if (ret)
92             goto fail;
93         if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
94             // XXX - power down port?
95             continue;
96
97         // Reset port.
98         ret = set_port_feature(i, USB_PORT_FEAT_RESET, endp);
99         if (ret)
100             goto fail;
101
102         // Wait for reset to complete.
103         u64 end = calc_future_tsc(USB_TIME_DRST * 2);
104         for (;;) {
105             ret = get_port_status(i, &sts, endp);
106             if (ret)
107                 goto fail;
108             if (!(sts.wPortStatus & USB_PORT_STAT_RESET))
109                 break;
110             if (check_time(end)) {
111                 // Timeout.
112                 warn_timeout();
113                 goto fail;
114             }
115             yield();
116         }
117         if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
118             // Device no longer present.  XXX - power down port?
119             continue;
120
121         // XXX - should try to parallelize configuration.
122         int count = configure_usb_device(
123             cntl, !!(sts.wPortStatus & USB_PORT_STAT_LOW_SPEED));
124         if (! count) {
125             // Shutdown port
126             ret = clear_port_feature(i, USB_PORT_FEAT_ENABLE, endp);
127             if (ret)
128                 goto fail;
129         }
130         totalcount += count;
131     }
132
133     return totalcount;
134
135 fail:
136     dprintf(1, "Failure on hub setup\n");
137     return 0;
138 }