uhub.c revision 1.6 1 /* $NetBSD: uhub.c,v 1.6 1998/11/25 22:32:05 augustss 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 #else
60 #define DPRINTF(x)
61 #define DPRINTFN(n,x)
62 #endif
63
64 struct uhub_softc {
65 struct device sc_dev; /* base device */
66 usbd_device_handle sc_hub; /* USB device */
67 usbd_pipe_handle sc_ipipe; /* interrupt pipe */
68 u_int8_t sc_status[1]; /* XXX more ports */
69 u_char sc_running;
70 };
71
72 int uhub_match __P((struct device *, struct cfdata *, void *));
73 void uhub_attach __P((struct device *, struct device *, void *));
74
75 usbd_status uhub_init_port __P((int, struct usbd_port *, usbd_device_handle));
76 void uhub_disconnect __P((struct uhub_softc *sc, struct device *parent,
77 struct usbd_port *up, int portno));
78 usbd_status uhub_explore __P((struct device *parent, usbd_device_handle hub));
79 void uhub_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
80
81 /*void uhub_disco __P((void *));*/
82
83 extern struct cfdriver uhub_cd;
84
85 struct cfattach uhub_ca = {
86 sizeof(struct uhub_softc), uhub_match, uhub_attach
87 };
88
89 struct cfattach uhub_uhub_ca = {
90 sizeof(struct uhub_softc), uhub_match, uhub_attach
91 };
92
93 int
94 uhub_match(parent, match, aux)
95 struct device *parent;
96 struct cfdata *match;
97 void *aux;
98 {
99 struct usb_attach_arg *uaa = aux;
100 usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device);
101
102 DPRINTFN(1,("uhub_match, dd=%p\n", dd));
103 /*
104 * The subclass for hubs seems to be 0 for some and 1 for others,
105 * so we just ignore the subclass.
106 */
107 if (uaa->iface == 0 && dd->bDeviceClass == UCLASS_HUB)
108 return (UMATCH_DEVCLASS_DEVSUBCLASS);
109 return (UMATCH_NONE);
110 }
111
112 void
113 uhub_attach(parent, self, aux)
114 struct device *parent;
115 struct device *self;
116 void *aux;
117 {
118 struct uhub_softc *sc = (struct uhub_softc *)self;
119 struct usb_attach_arg *uaa = aux;
120 usbd_device_handle dev = uaa->device;
121 char devinfo[1024];
122 usbd_status r;
123 struct usbd_hub *hub;
124 usb_device_request_t req;
125 usb_hub_descriptor_t hubdesc;
126 int port, nports;
127 usbd_interface_handle iface;
128 usb_endpoint_descriptor_t *ed;
129
130 DPRINTFN(1,("uhub_attach\n"));
131 sc->sc_hub = dev;
132 usbd_devinfo(dev, 1, devinfo);
133 printf(": %s\n", devinfo);
134
135 r = usbd_set_config_no(dev, 0, 1);
136 if (r != USBD_NORMAL_COMPLETION) {
137 printf("%s: configuration failed, error=%d\n",
138 sc->sc_dev.dv_xname, r);
139 return;
140 }
141
142 if (dev->depth > USB_HUB_MAX_DEPTH) {
143 printf("%s: hub depth (%d) exceeded, hub ignored\n",
144 sc->sc_dev.dv_xname, USB_HUB_MAX_DEPTH);
145 return;
146 }
147
148 /* Get hub descriptor. */
149 req.bmRequestType = UT_READ_CLASS_DEVICE;
150 req.bRequest = UR_GET_DESCRIPTOR;
151 USETW(req.wValue, 0);
152 USETW(req.wIndex, 0);
153 USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
154 DPRINTFN(1,("usb_init_hub: getting hub descriptor\n"));
155 /* XXX not correct for hubs with >7 ports */
156 r = usbd_do_request(dev, &req, &hubdesc);
157 if (r != USBD_NORMAL_COMPLETION) {
158 printf("%s: getting hub descriptor failed, error=%d\n",
159 sc->sc_dev.dv_xname, r);
160 return;
161 }
162
163 nports = hubdesc.bNbrPorts;
164 hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port),
165 M_USB, M_NOWAIT);
166 if (hub == 0)
167 return;
168 dev->hub = hub;
169 dev->hub->hubdata = sc;
170 hub->explore = uhub_explore;
171 hub->hubdesc = hubdesc;
172
173 DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, parent->selfpowered=%d\n",
174 dev->self_powered, dev->powersrc->parent,
175 dev->powersrc->parent ?
176 dev->powersrc->parent->self_powered : 0));
177 if (!dev->self_powered && dev->powersrc->parent &&
178 !dev->powersrc->parent->self_powered) {
179 printf("%s: bus powered hub connected to bus powered hub, ignored\n",
180 sc->sc_dev.dv_xname);
181 return;
182 }
183
184 /* Set up interrupt pipe. */
185 r = usbd_device2interface_handle(dev, 0, &iface);
186 if (r != USBD_NORMAL_COMPLETION) {
187 printf("%s: no interface handle\n", sc->sc_dev.dv_xname);
188 return;
189 }
190 ed = usbd_interface2endpoint_descriptor(iface, 0);
191 if (ed == 0) {
192 printf("%s: no endpoint descriptor\n", sc->sc_dev.dv_xname);
193 return;
194 }
195 if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
196 printf("%s: bad interrupt endpoint\n", sc->sc_dev.dv_xname);
197 return;
198 }
199
200 r = usbd_open_pipe_intr(iface, ed->bEndpointAddress,USBD_SHORT_XFER_OK,
201 &sc->sc_ipipe, sc, sc->sc_status,
202 sizeof(sc->sc_status),
203 uhub_intr);
204 if (r != USBD_NORMAL_COMPLETION) {
205 printf("%s: cannot open interrupt pipe\n",sc->sc_dev.dv_xname);
206 return;
207 }
208
209 for (port = 1; port <= nports; port++) {
210 r = uhub_init_port(port, &hub->ports[port-1], dev);
211 if (r != USBD_NORMAL_COMPLETION)
212 printf("%s: init of port %d failed\n",
213 sc->sc_dev.dv_xname, port);
214 }
215 sc->sc_running = 1;
216 }
217
218 usbd_status
219 uhub_init_port(port, uport, dev)
220 int port;
221 struct usbd_port *uport;
222 usbd_device_handle dev;
223 {
224 usbd_status r;
225 u_int16_t pstatus;
226
227 uport->device = 0;
228 uport->parent = dev;
229 r = usbd_get_port_status(dev, port, &uport->status);
230 if (r != USBD_NORMAL_COMPLETION)
231 return r;
232 pstatus = UGETW(uport->status.wPortStatus);
233 DPRINTF(("usbd_init_port: adding hub port=%d status=0x%04x change=0x%04x\n",
234 port, pstatus, UGETW(uport->status.wPortChange)));
235 if ((pstatus & UPS_PORT_POWER) == 0) {
236 /* Port lacks power, turn it on */
237 r = usbd_set_port_feature(dev, port, UHF_PORT_POWER);
238 if (r != USBD_NORMAL_COMPLETION)
239 return (r);
240 r = usbd_get_port_status(dev, port, &uport->status);
241 if (r != USBD_NORMAL_COMPLETION)
242 return (r);
243 DPRINTF(("usb_init_port: turn on port %d power status=0x%04x change=0x%04x\n",
244 port, UGETW(uport->status.wPortStatus),
245 UGETW(uport->status.wPortChange)));
246 /* Wait for stable power. */
247 usbd_delay_ms(dev->bus, dev->hub->hubdesc.bPwrOn2PwrGood *
248 UHD_PWRON_FACTOR);
249 }
250 if (dev->self_powered)
251 /* Self powered hub, give ports maximum current. */
252 uport->power = USB_MAX_POWER;
253 else
254 uport->power = USB_MIN_POWER;
255 return (USBD_NORMAL_COMPLETION);
256 }
257
258 usbd_status
259 uhub_explore(parent, dev)
260 struct device *parent;
261 usbd_device_handle dev;
262 {
263 usb_hub_descriptor_t *hd = &dev->hub->hubdesc;
264 struct uhub_softc *sc = dev->hub->hubdata;
265 struct usbd_port *up;
266 usbd_status r;
267 int port;
268 int change, status;
269
270 DPRINTF(("uhub_explore dev=%p addr=%d\n", dev, dev->address));
271
272 if (!sc->sc_running)
273 return (USBD_NOT_STARTED);
274
275 /* Ignore hubs that are too deep. */
276 if (dev->depth > USB_HUB_MAX_DEPTH)
277 return (USBD_TOO_DEEP);
278
279 for(port = 1; port <= hd->bNbrPorts; port++) {
280 up = &dev->hub->ports[port-1];
281 r = usbd_get_port_status(dev, port, &up->status);
282 if (r != USBD_NORMAL_COMPLETION) {
283 DPRINTF(("uhub_explore: get port status failed, error=%d\n",
284 r));
285 continue;
286 }
287 status = UGETW(up->status.wPortStatus);
288 change = UGETW(up->status.wPortChange);
289 DPRINTFN(5, ("uhub_explore: port %d status 0x%04x 0x%04x\n",
290 port, status, change));
291 if (!(change & UPS_CURRENT_CONNECT_STATUS)) {
292 /* No status change, just do recursive explore. */
293 if (up->device && up->device->hub)
294 up->device->hub->explore(parent, up->device);
295 continue;
296 }
297 DPRINTF(("uhub_explore: status change hub=%d port=%d\n",
298 dev->address, port));
299 usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION);
300 usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
301 /*
302 * If there is already a device on the port the change status
303 * must mean that is has disconnected. Looking at the
304 * current connect status is not enough to figure this out
305 * since a new unit may have been connected before we handle
306 * the disconnect.
307 */
308 if (up->device) {
309 /* Disconnected */
310 DPRINTF(("uhub_explore: device %d disappeared on port %d\n",
311 up->device->address, port));
312 uhub_disconnect(sc, parent, up, port);
313 usbd_clear_port_feature(dev, port,
314 UHF_C_PORT_CONNECTION);
315 }
316 if (!(status & UPS_CURRENT_CONNECT_STATUS))
317 continue;
318
319 /* Connected */
320 /* Wait for maximum device power up time. */
321 usbd_delay_ms(dev->bus, USB_PORT_POWERUP_DELAY);
322 /* Reset port, which implies enabling it. */
323 if (usbd_reset_port(dev, port, &up->status) !=
324 USBD_NORMAL_COMPLETION)
325 continue;
326
327 /* Wait for power to settle in device. */
328 usbd_delay_ms(dev->bus, USB_POWER_SETTLE);
329
330 /* Get device info and set its address. */
331 r = usbd_new_device((struct device *)sc, dev->bus,
332 dev->depth + 1, status & UPS_LOW_SPEED,
333 port, up);
334 /* XXX retry a few times? */
335 if (r != USBD_NORMAL_COMPLETION) {
336 DPRINTFN(-1,("uhub_explore: usb_new_device failed, error=%d\n", r));
337 /* Avoid addressing problems by disabling. */
338 /* usbd_reset_port(dev, port, &up->status); */
339 /* XXX
340 * What should we do. The device may or may not be at its
341 * assigned address. In any case we'd like to ignore it.
342 * Maybe the port should be disabled until the device is
343 * disconnected.
344 */
345 if (r == USBD_SET_ADDR_FAILED || 1) {/* XXX */
346 /* The unit refused to accept a new
347 * address, and since we cannot leave
348 * at 0 we have to disable the port
349 * instead. */
350 printf("%s: device problem, disable port %d\n",
351 parent->dv_xname, port);
352 usbd_clear_port_feature(dev, port,
353 UHF_PORT_ENABLE);
354 }
355 } else {
356 if (up->device->hub)
357 up->device->hub->explore(parent, up->device);
358 }
359 }
360 return (USBD_NORMAL_COMPLETION);
361 }
362
363 void
364 uhub_disconnect(sc, parent, up, portno)
365 struct uhub_softc *sc;
366 struct device *parent;
367 struct usbd_port *up;
368 int portno;
369 {
370 usbd_device_handle dev = up->device;
371 usbd_pipe_handle p, n;
372 usb_hub_descriptor_t *hd;
373 struct usbd_port *spi;
374 int i, port;
375
376 DPRINTFN(3,("uhub_disconnect: up=%p dev=%p port=%d\n",
377 up, dev, portno));
378
379 printf("%s: device addr %d%s on hub addr %d, port %d disconnected\n",
380 parent->dv_xname, dev->address, dev->hub ? " (hub)" : "",
381 up->parent->address, portno);
382
383 for (i = 0; i < dev->cdesc->bNumInterface; i++) {
384 for (p = LIST_FIRST(&dev->ifaces[i].pipes); p; p = n) {
385 n = LIST_NEXT(p, next);
386 if (p->disco)
387 p->disco(p->discoarg);
388 usbd_abort_pipe(p);
389 usbd_close_pipe(p);
390 }
391 }
392
393 /* XXX Free all data structures and disable further I/O. */
394
395
396 if (dev->hub) {
397 DPRINTFN(3,("usb_disconnect: hub, recursing\n"));
398 hd = &dev->hub->hubdesc;
399 for(port = 1; port <= hd->bNbrPorts; port++) {
400 spi = &dev->hub->ports[port-1];
401 if (spi->device)
402 uhub_disconnect(sc, parent, spi, port);
403 }
404 }
405
406 dev->bus->devices[dev->address] = 0;
407 up->device = 0;
408 /* XXX free */
409 }
410
411 void
412 uhub_intr(reqh, addr, status)
413 usbd_request_handle reqh;
414 usbd_private_handle addr;
415 usbd_status status;
416 {
417 struct uhub_softc *sc = addr;
418
419 DPRINTFN(1,("uhub_intr: sc=%p\n", sc));
420 #if 0
421 if (status != USBD_NORMAL_COMPLETION)
422 usbd_clear_endpoint_stall(sc->sc_ipipe);
423 else
424 #endif
425 usb_needs_explore(sc->sc_hub->bus);
426 }
427