grml...
[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_pipe *pipe, struct usb_hub_descriptor *desc)
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(pipe, &req, desc);
22 }
23
24 static int
25 set_port_feature(struct usbhub_s *hub, int port, int feature)
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 + 1;
32     req.wLength = 0;
33     mutex_lock(&hub->lock);
34     int ret = send_default_control(hub->pipe, &req, NULL);
35     mutex_unlock(&hub->lock);
36     return ret;
37 }
38
39 static int
40 clear_port_feature(struct usbhub_s *hub, int port, int feature)
41 {
42     struct usb_ctrlrequest req;
43     req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
44     req.bRequest = USB_REQ_CLEAR_FEATURE;
45     req.wValue = feature;
46     req.wIndex = port + 1;
47     req.wLength = 0;
48     mutex_lock(&hub->lock);
49     int ret = send_default_control(hub->pipe, &req, NULL);
50     mutex_unlock(&hub->lock);
51     return ret;
52 }
53
54 static int
55 get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts)
56 {
57     struct usb_ctrlrequest req;
58     req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER;
59     req.bRequest = USB_REQ_GET_STATUS;
60     req.wValue = 0;
61     req.wIndex = port + 1;
62     req.wLength = sizeof(*sts);
63     mutex_lock(&hub->lock);
64     int ret = send_default_control(hub->pipe, &req, sts);
65     mutex_unlock(&hub->lock);
66     return ret;
67 }
68
69 // Check if device attached to port
70 static int
71 usb_hub_detect(struct usbhub_s *hub, u32 port)
72 {
73     // Turn on power to port.
74     int ret = set_port_feature(hub, port, USB_PORT_FEAT_POWER);
75     if (ret)
76         goto fail;
77
78     // Wait for port power to stabilize.
79     msleep(hub->powerwait);
80
81     // Check periodically for a device connect.
82     struct usb_port_status sts;
83     u64 end = calc_future_tsc(USB_TIME_SIGATT);
84     for (;;) {
85         ret = get_port_status(hub, port, &sts);
86         if (ret)
87             goto fail;
88         if (sts.wPortStatus & USB_PORT_STAT_CONNECTION)
89             // Device connected.
90             break;
91         if (check_tsc(end))
92             // No device found.
93             return -1;
94         msleep(5);
95     }
96
97     // XXX - wait USB_TIME_ATTDB time?
98
99     return 0;
100
101 fail:
102     dprintf(1, "Failure on hub port %d detect\n", port);
103     return -1;
104 }
105
106 // Disable port
107 static void
108 usb_hub_disconnect(struct usbhub_s *hub, u32 port)
109 {
110     int ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE);
111     if (ret)
112         dprintf(1, "Failure on hub port %d disconnect\n", port);
113 }
114
115 // Reset device on port
116 static int
117 usb_hub_reset(struct usbhub_s *hub, u32 port)
118 {
119     int ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET);
120     if (ret)
121         goto fail;
122
123     // Wait for reset to complete.
124     struct usb_port_status sts;
125     u64 end = calc_future_tsc(USB_TIME_DRST * 2);
126     for (;;) {
127         ret = get_port_status(hub, port, &sts);
128         if (ret)
129             goto fail;
130         if (!(sts.wPortStatus & USB_PORT_STAT_RESET))
131             break;
132         if (check_tsc(end)) {
133             warn_timeout();
134             goto fail;
135         }
136         msleep(5);
137     }
138
139     // Reset complete.
140     if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
141         // Device no longer present
142         return -1;
143
144     return ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK)
145             >> USB_PORT_STAT_SPEED_SHIFT);
146
147 fail:
148     dprintf(1, "Failure on hub port %d reset\n", port);
149     usb_hub_disconnect(hub, port);
150     return -1;
151 }
152
153 static struct usbhub_op_s HubOp = {
154     .detect = usb_hub_detect,
155     .reset = usb_hub_reset,
156     .disconnect = usb_hub_disconnect,
157 };
158
159 // Configure a usb hub and then find devices connected to it.
160 int
161 usb_hub_init(struct usb_pipe *pipe)
162 {
163     ASSERT32FLAT();
164     if (!CONFIG_USB_HUB)
165         return -1;
166
167     struct usb_hub_descriptor desc;
168     int ret = get_hub_desc(pipe, &desc);
169     if (ret)
170         return ret;
171
172     struct usbhub_s hub;
173     memset(&hub, 0, sizeof(hub));
174     hub.pipe = pipe;
175     hub.cntl = pipe->cntl;
176     hub.powerwait = desc.bPwrOn2PwrGood * 2;
177     hub.portcount = desc.bNbrPorts;
178     hub.op = &HubOp;
179     usb_enumerate(&hub);
180
181     dprintf(1, "Initialized USB HUB (%d ports used)\n", hub.devcount);
182     if (hub.devcount)
183         return 0;
184     return -1;
185 }