uhub.c revision 1.10 1 /* $NetBSD: uhub.c,v 1.10 1998/12/09 19:24:28 drochner Exp $ */
2
3 /*
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (augustss (at) carlstedt.se) at
9 * Carlstedt Research & Technology.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
44 #include <sys/malloc.h>
45 #include <sys/device.h>
46 #include <sys/proc.h>
47 #include <sys/device.h>
48
49 #include <dev/usb/usb.h>
50
51 #include <dev/usb/usbdi.h>
52 #include <dev/usb/usbdi_util.h>
53 #include <dev/usb/usbdivar.h>
54
55 #ifdef USB_DEBUG
56 #define DPRINTF(x) if (usbdebug) printf x
57 #define DPRINTFN(n,x) if (usbdebug>(n)) printf x
58 extern int usbdebug;
59 extern char *usbd_error_strs[];
60 #else
61 #define DPRINTF(x)
62 #define DPRINTFN(n,x)
63 #endif
64
65 struct uhub_softc {
66 struct device sc_dev; /* base device */
67 usbd_device_handle sc_hub; /* USB device */
68 usbd_pipe_handle sc_ipipe; /* interrupt pipe */
69 u_int8_t sc_status[1]; /* XXX more ports */
70 u_char sc_running;
71 };
72
73 int uhub_match __P((struct device *, struct cfdata *, void *));
74 void uhub_attach __P((struct device *, struct device *, void *));
75
76 usbd_status uhub_init_port __P((int, struct usbd_port *, usbd_device_handle));
77 void uhub_disconnect __P((struct uhub_softc *sc, struct device *parent,
78 struct usbd_port *up, int portno));
79 usbd_status uhub_explore __P((struct device *parent, usbd_device_handle hub));
80 void uhub_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
81
82 /*void uhub_disco __P((void *));*/
83
84 extern struct cfdriver uhub_cd;
85
86 struct cfattach uhub_ca = {
87 sizeof(struct uhub_softc), uhub_match, uhub_attach
88 };
89
90 struct cfattach uhub_uhub_ca = {
91 sizeof(struct uhub_softc), uhub_match, uhub_attach
92 };
93
94 int
95 uhub_match(parent, match, aux)
96 struct device *parent;
97 struct cfdata *match;
98 void *aux;
99 {
100 struct usb_attach_arg *uaa = aux;
101 usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device);
102
103 DPRINTFN(1,("uhub_match, dd=%p\n", dd));
104 /*
105 * The subclass for hubs seems to be 0 for some and 1 for others,
106 * so we just ignore the subclass.
107 */
108 if (uaa->iface == 0 && dd->bDeviceClass == UCLASS_HUB)
109 return (UMATCH_DEVCLASS_DEVSUBCLASS);
110 return (UMATCH_NONE);
111 }
112
113 void
114 uhub_attach(parent, self, aux)
115 struct device *parent;
116 struct device *self;
117 void *aux;
118 {
119 struct uhub_softc *sc = (struct uhub_softc *)self;
120 struct usb_attach_arg *uaa = aux;
121 usbd_device_handle dev = uaa->device;
122 char devinfo[1024];
123 usbd_status r;
124 struct usbd_hub *hub;
125 usb_device_request_t req;
126 usb_hub_descriptor_t hubdesc;
127 int port, nports;
128 usbd_interface_handle iface;
129 usb_endpoint_descriptor_t *ed;
130
131 DPRINTFN(1,("uhub_attach\n"));
132 sc->sc_hub = dev;
133 usbd_devinfo(dev, 1, devinfo);
134 printf("\n%s: %s\n", sc->sc_dev.dv_xname, devinfo);
135
136 r = usbd_set_config_index(dev, 0, 1);
137 if (r != USBD_NORMAL_COMPLETION) {
138 DPRINTF(("%s: configuration failed, error=%d(%s)\n",
139 sc->sc_dev.dv_xname, r, usbd_error_strs[r]));
140 return;
141 }
142
143 if (dev->depth > USB_HUB_MAX_DEPTH) {
144 printf("%s: hub depth (%d) exceeded, hub ignored\n",
145 sc->sc_dev.dv_xname, USB_HUB_MAX_DEPTH);
146 return;
147 }
148
149 /* Get hub descriptor. */
150 req.bmRequestType = UT_READ_CLASS_DEVICE;
151 req.bRequest = UR_GET_DESCRIPTOR;
152 USETW(req.wValue, 0);
153 USETW(req.wIndex, 0);
154 USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
155 DPRINTFN(1,("usb_init_hub: getting hub descriptor\n"));
156 /* XXX not correct for hubs with >7 ports */
157 r = usbd_do_request(dev, &req, &hubdesc);
158 if (r != USBD_NORMAL_COMPLETION) {
159 DPRINTF(("%s: getting hub descriptor failed, error=%d(%s)\n",
160 sc->sc_dev.dv_xname, r, usbd_error_strs[r]));
161 return;
162 }
163
164 nports = hubdesc.bNbrPorts;
165 hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port),
166 M_USB, M_NOWAIT);
167 if (hub == 0)
168 return;
169 dev->hub = hub;
170 dev->hub->hubdata = sc;
171 hub->explore = uhub_explore;
172 hub->hubdesc = hubdesc;
173
174 DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, "
175 "parent->selfpowered=%d\n",
176 dev->self_powered, dev->powersrc->parent,
177 dev->powersrc->parent ?
178 dev->powersrc->parent->self_powered : 0));
179 if (!dev->self_powered && dev->powersrc->parent &&
180 !dev->powersrc->parent->self_powered) {
181 printf("%s: bus powered hub connected to bus powered hub, "
182 "ignored\n",
183 sc->sc_dev.dv_xname);
184 return;
185 }
186
187 /* Set up interrupt pipe. */
188 r = usbd_device2interface_handle(dev, 0, &iface);
189 if (r != USBD_NORMAL_COMPLETION) {
190 printf("%s: no interface handle\n", sc->sc_dev.dv_xname);
191 return;
192 }
193 ed = usbd_interface2endpoint_descriptor(iface, 0);
194 if (ed == 0) {
195 printf("%s: no endpoint descriptor\n", sc->sc_dev.dv_xname);
196 return;
197 }
198 if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
199 printf("%s: bad interrupt endpoint\n", sc->sc_dev.dv_xname);
200 return;
201 }
202
203 r = usbd_open_pipe_intr(iface, ed->bEndpointAddress,USBD_SHORT_XFER_OK,
204 &sc->sc_ipipe, sc, sc->sc_status,
205 sizeof(sc->sc_status),
206 uhub_intr);
207 if (r != USBD_NORMAL_COMPLETION) {
208 printf("%s: cannot open interrupt pipe\n",sc->sc_dev.dv_xname);
209 return;
210 }
211
212 for (port = 1; port <= nports; port++) {
213 r = uhub_init_port(port, &hub->ports[port-1], dev);
214 if (r != USBD_NORMAL_COMPLETION)
215 printf("%s: init of port %d failed\n",
216 sc->sc_dev.dv_xname, port);
217 }
218 sc->sc_running = 1;
219 }
220
221 usbd_status
222 uhub_init_port(port, uport, dev)
223 int port;
224 struct usbd_port *uport;
225 usbd_device_handle dev;
226 {
227 usbd_status r;
228 u_int16_t pstatus;
229
230 uport->device = 0;
231 uport->parent = dev;
232 r = usbd_get_port_status(dev, port, &uport->status);
233 if (r != USBD_NORMAL_COMPLETION)
234 return r;
235 pstatus = UGETW(uport->status.wPortStatus);
236 DPRINTF(("usbd_init_port: adding hub port=%d status=0x%04x "
237 "change=0x%04x\n",
238 port, pstatus, UGETW(uport->status.wPortChange)));
239 if ((pstatus & UPS_PORT_POWER) == 0) {
240 /* Port lacks power, turn it on */
241 #if 0
242 /* First let the device go through a good power cycle, */
243 usbd_delay_ms(dev->bus, USB_POWER_DOWN_TIME);
244 #endif
245 /* then turn the power on. */
246 r = usbd_set_port_feature(dev, port, UHF_PORT_POWER);
247 if (r != USBD_NORMAL_COMPLETION)
248 return (r);
249 r = usbd_get_port_status(dev, port, &uport->status);
250 if (r != USBD_NORMAL_COMPLETION)
251 return (r);
252 DPRINTF(("usb_init_port: turn on port %d power status=0x%04x "
253 "change=0x%04x\n",
254 port, UGETW(uport->status.wPortStatus),
255 UGETW(uport->status.wPortChange)));
256 /* Wait for stable power. */
257 usbd_delay_ms(dev->bus, dev->hub->hubdesc.bPwrOn2PwrGood *
258 UHD_PWRON_FACTOR);
259 }
260 if (dev->self_powered)
261 /* Self powered hub, give ports maximum current. */
262 uport->power = USB_MAX_POWER;
263 else
264 uport->power = USB_MIN_POWER;
265 return (USBD_NORMAL_COMPLETION);
266 }
267
268 usbd_status
269 uhub_explore(parent, dev)
270 struct device *parent;
271 usbd_device_handle dev;
272 {
273 usb_hub_descriptor_t *hd = &dev->hub->hubdesc;
274 struct uhub_softc *sc = dev->hub->hubdata;
275 struct usbd_port *up;
276 usbd_status r;
277 int port;
278 int change, status;
279
280 DPRINTF(("uhub_explore dev=%p addr=%d\n", dev, dev->address));
281
282 if (!sc->sc_running)
283 return (USBD_NOT_STARTED);
284
285 /* Ignore hubs that are too deep. */
286 if (dev->depth > USB_HUB_MAX_DEPTH)
287 return (USBD_TOO_DEEP);
288
289 for(port = 1; port <= hd->bNbrPorts; port++) {
290 up = &dev->hub->ports[port-1];
291 r = usbd_get_port_status(dev, port, &up->status);
292 if (r != USBD_NORMAL_COMPLETION) {
293 DPRINTF(("uhub_explore: get port status failed, "
294 "error=%d(%s)\n",
295 r, usbd_error_strs[r]));
296 continue;
297 }
298 status = UGETW(up->status.wPortStatus);
299 change = UGETW(up->status.wPortChange);
300 DPRINTFN(5, ("uhub_explore: port %d status 0x%04x 0x%04x\n",
301 port, status, change));
302 if (!(change & UPS_CURRENT_CONNECT_STATUS)) {
303 /* No status change, just do recursive explore. */
304 if (up->device && up->device->hub)
305 up->device->hub->explore(parent, up->device);
306 continue;
307 }
308 DPRINTF(("uhub_explore: status change hub=%d port=%d\n",
309 dev->address, port));
310 usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION);
311 usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
312 /*
313 * If there is already a device on the port the change status
314 * must mean that is has disconnected. Looking at the
315 * current connect status is not enough to figure this out
316 * since a new unit may have been connected before we handle
317 * the disconnect.
318 */
319 if (up->device) {
320 /* Disconnected */
321 DPRINTF(("uhub_explore: device %d disappeared "
322 "on port %d\n",
323 up->device->address, port));
324 uhub_disconnect(sc, parent, up, port);
325 usbd_clear_port_feature(dev, port,
326 UHF_C_PORT_CONNECTION);
327 }
328 if (!(status & UPS_CURRENT_CONNECT_STATUS))
329 continue;
330
331 /* Connected */
332 /* Wait for maximum device power up time. */
333 usbd_delay_ms(dev->bus, USB_PORT_POWERUP_DELAY);
334 /* Reset port, which implies enabling it. */
335 if (usbd_reset_port(dev, port, &up->status) !=
336 USBD_NORMAL_COMPLETION)
337 continue;
338
339 /* Wait for power to settle in device. */
340 usbd_delay_ms(dev->bus, USB_POWER_SETTLE);
341
342 /* Get device info and set its address. */
343 r = usbd_new_device((struct device *)sc, dev->bus,
344 dev->depth + 1, status & UPS_LOW_SPEED,
345 port, up);
346 /* XXX retry a few times? */
347 if (r != USBD_NORMAL_COMPLETION) {
348 DPRINTFN(-1,("uhub_explore: usb_new_device failed, "
349 "error=%d(%s)\n", r, usbd_error_strs[r]));
350 /* Avoid addressing problems by disabling. */
351 /* usbd_reset_port(dev, port, &up->status); */
352 /* XXX
353 * What should we do. The device may or may not be at its
354 * assigned address. In any case we'd like to ignore it.
355 * Maybe the port should be disabled until the device is
356 * disconnected.
357 */
358 if (r == USBD_SET_ADDR_FAILED || 1) {/* XXX */
359 /* The unit refused to accept a new
360 * address, and since we cannot leave
361 * at 0 we have to disable the port
362 * instead. */
363 printf("%s: device problem, disabling "
364 "port %d\n",
365 parent->dv_xname, port);
366 usbd_clear_port_feature(dev, port,
367 UHF_PORT_ENABLE);
368 }
369 } else {
370 if (up->device->hub)
371 up->device->hub->explore(parent, up->device);
372 }
373 }
374 return (USBD_NORMAL_COMPLETION);
375 }
376
377 void
378 uhub_disconnect(sc, parent, up, portno)
379 struct uhub_softc *sc;
380 struct device *parent;
381 struct usbd_port *up;
382 int portno;
383 {
384 usbd_device_handle dev = up->device;
385 usbd_pipe_handle p, n;
386 usb_hub_descriptor_t *hd;
387 struct usbd_port *spi;
388 int i, port;
389
390 DPRINTFN(3,("uhub_disconnect: up=%p dev=%p port=%d\n",
391 up, dev, portno));
392
393 printf("%s: device addr %d%s on hub addr %d, port %d disconnected\n",
394 parent->dv_xname, dev->address, dev->hub ? " (hub)" : "",
395 up->parent->address, portno);
396
397 for (i = 0; i < dev->cdesc->bNumInterface; i++) {
398 for (p = LIST_FIRST(&dev->ifaces[i].pipes); p; p = n) {
399 n = LIST_NEXT(p, next);
400 if (p->disco)
401 p->disco(p->discoarg);
402 usbd_abort_pipe(p);
403 usbd_close_pipe(p);
404 }
405 }
406
407 /* XXX Free all data structures and disable further I/O. */
408
409
410 if (dev->hub) {
411 DPRINTFN(3,("usb_disconnect: hub, recursing\n"));
412 hd = &dev->hub->hubdesc;
413 for(port = 1; port <= hd->bNbrPorts; port++) {
414 spi = &dev->hub->ports[port-1];
415 if (spi->device)
416 uhub_disconnect(sc, parent, spi, port);
417 }
418 }
419
420 dev->bus->devices[dev->address] = 0;
421 up->device = 0;
422 /* XXX free */
423 }
424
425 void
426 uhub_intr(reqh, addr, status)
427 usbd_request_handle reqh;
428 usbd_private_handle addr;
429 usbd_status status;
430 {
431 struct uhub_softc *sc = addr;
432
433 DPRINTFN(1,("uhub_intr: sc=%p\n", sc));
434 if (status != USBD_NORMAL_COMPLETION)
435 usbd_clear_endpoint_stall_async(sc->sc_ipipe);
436 else
437 usb_needs_explore(sc->sc_hub->bus);
438 }
439