blob: 9effbc3328f8859fdbc416f37c1a4df94baf4cc1 [file] [log] [blame]
Kevin O'Connor54671c12010-02-15 18:58:12 -05001// 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
12static int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050013get_hub_desc(struct usb_pipe *pipe, struct usb_hub_descriptor *desc)
Kevin O'Connor54671c12010-02-15 18:58:12 -050014{
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);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050021 return send_default_control(pipe, &req, desc);
Kevin O'Connor54671c12010-02-15 18:58:12 -050022}
23
24static int
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050025set_port_feature(struct usbhub_s *hub, int port, int feature)
Kevin O'Connor54671c12010-02-15 18:58:12 -050026{
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;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050033 mutex_lock(&hub->lock);
34 int ret = send_default_control(hub->pipe, &req, NULL);
35 mutex_unlock(&hub->lock);
36 return ret;
Kevin O'Connor54671c12010-02-15 18:58:12 -050037}
38
39static int
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050040clear_port_feature(struct usbhub_s *hub, int port, int feature)
Kevin O'Connor54671c12010-02-15 18:58:12 -050041{
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;
47 req.wLength = 0;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050048 mutex_lock(&hub->lock);
49 int ret = send_default_control(hub->pipe, &req, NULL);
50 mutex_unlock(&hub->lock);
51 return ret;
Kevin O'Connor54671c12010-02-15 18:58:12 -050052}
53
54static int
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050055get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts)
Kevin O'Connor54671c12010-02-15 18:58:12 -050056{
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;
62 req.wLength = sizeof(*sts);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050063 mutex_lock(&hub->lock);
64 int ret = send_default_control(hub->pipe, &req, sts);
65 mutex_unlock(&hub->lock);
66 return ret;
67}
68
69static void
70init_hub_port(void *data)
71{
72 struct usbhub_s *hub = data;
73 u32 port = hub->port; // XXX - find better way to pass port
74
75 // Turn on power to port.
76 int ret = set_port_feature(hub, port, USB_PORT_FEAT_POWER);
77 if (ret)
78 goto fail;
79
80 // Wait for port power to stabilize.
81 msleep(hub->powerwait);
82
83 // Check periodically for a device connect.
84 struct usb_port_status sts;
85 u64 end = calc_future_tsc(USB_TIME_SIGATT);
86 for (;;) {
87 ret = get_port_status(hub, port, &sts);
88 if (ret)
89 goto fail;
90 if (sts.wPortStatus & USB_PORT_STAT_CONNECTION)
91 // Device connected.
92 break;
93 if (check_time(end))
94 // No device found.
95 goto done;
96 msleep(5);
97 }
98
99 // XXX - wait USB_TIME_ATTDB time?
100
101 // Reset port.
102 mutex_lock(&hub->cntl->resetlock);
103 ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET);
104 if (ret)
105 goto resetfail;
106
107 // Wait for reset to complete.
108 end = calc_future_tsc(USB_TIME_DRST * 2);
109 for (;;) {
110 ret = get_port_status(hub, port, &sts);
111 if (ret)
112 goto resetfail;
113 if (!(sts.wPortStatus & USB_PORT_STAT_RESET))
114 break;
115 if (check_time(end)) {
116 warn_timeout();
117 goto resetfail;
118 }
119 msleep(5);
120 }
121
122 // Reset complete.
123 if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
124 // Device no longer present
125 goto resetfail;
126
127 // Set address of port
128 struct usb_pipe *pipe = usb_set_address(
Kevin O'Connor190cc622010-03-09 19:43:52 -0500129 hub, port, ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK)
130 >> USB_PORT_STAT_SPEED_SHIFT));
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500131 if (!pipe)
132 goto resetfail;
133 mutex_unlock(&hub->cntl->resetlock);
134
135 // Configure the device
136 int count = configure_usb_device(pipe);
137 free_pipe(pipe);
138 if (!count) {
139 ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE);
140 if (ret)
141 goto fail;
142 }
143 hub->devcount += count;
144done:
145 hub->threads--;
146 return;
147
148resetfail:
149 clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE);
150 mutex_unlock(&hub->cntl->resetlock);
151fail:
152 dprintf(1, "Failure on hub port %d setup\n", port);
153 goto done;
Kevin O'Connor54671c12010-02-15 18:58:12 -0500154}
155
156// Configure a usb hub and then find devices connected to it.
157int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500158usb_hub_init(struct usb_pipe *pipe)
Kevin O'Connor54671c12010-02-15 18:58:12 -0500159{
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500160 ASSERT32FLAT();
Kevin O'Connor54671c12010-02-15 18:58:12 -0500161 if (!CONFIG_USB_HUB)
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500162 return -1;
Kevin O'Connor54671c12010-02-15 18:58:12 -0500163
164 struct usb_hub_descriptor desc;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500165 int ret = get_hub_desc(pipe, &desc);
Kevin O'Connor54671c12010-02-15 18:58:12 -0500166 if (ret)
167 return ret;
168
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500169 struct usbhub_s hub;
170 memset(&hub, 0, sizeof(hub));
171 hub.pipe = pipe;
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500172 hub.cntl = pipe->cntl;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500173 hub.powerwait = desc.bPwrOn2PwrGood * 2;
174
175 // Launch a thread for every port.
Kevin O'Connor54671c12010-02-15 18:58:12 -0500176 int i;
177 for (i=1; i<=desc.bNbrPorts; i++) {
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500178 hub.port = i;
179 hub.threads++;
180 run_thread(init_hub_port, &hub);
Kevin O'Connor54671c12010-02-15 18:58:12 -0500181 }
182
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500183 // Wait for threads to complete.
184 while (hub.threads)
185 yield();
Kevin O'Connor54671c12010-02-15 18:58:12 -0500186
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500187 dprintf(1, "Initialized USB HUB (%d ports used)\n", hub.devcount);
188 if (hub.devcount)
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500189 return 0;
190 return -1;
Kevin O'Connor54671c12010-02-15 18:58:12 -0500191}