xref: /openbsd-src/sys/dev/usb/uhub.c (revision 1fc27e414118cd8922c6b93fbaeb7a5246bfd593)
1 /*	$OpenBSD: uhub.c,v 1.6 1999/11/07 21:30:19 fgsch Exp $	*/
2 /*	$NetBSD: uhub.c,v 1.32 1999/10/13 08:10:56 augustss Exp $	*/
3 
4 /*
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Lennart Augustsson (augustss@carlstedt.se) at
10  * Carlstedt Research & Technology.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *        This product includes software developed by the NetBSD
23  *        Foundation, Inc. and its contributors.
24  * 4. Neither the name of The NetBSD Foundation nor the names of its
25  *    contributors may be used to endorse or promote products derived
26  *    from this software without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  */
40 
41 /*
42  * USB spec: http://www.usb.org/cgi-usb/mailmerge.cgi/home/usb/docs/developers/cgiform.tpl
43  */
44 
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/malloc.h>
49 #if defined(__NetBSD__) || defined(__OpenBSD__)
50 #include <sys/device.h>
51 #elif defined(__FreeBSD__)
52 #include <sys/module.h>
53 #include <sys/bus.h>
54 #include "bus_if.h"
55 #endif
56 #include <sys/proc.h>
57 
58 #include <machine/bus.h>
59 
60 #include <dev/usb/usb.h>
61 #include <dev/usb/usbdi.h>
62 #include <dev/usb/usbdi_util.h>
63 #include <dev/usb/usbdivar.h>
64 
65 #ifdef UHUB_DEBUG
66 #define DPRINTF(x)	if (usbdebug) logprintf x
67 #define DPRINTFN(n,x)	if (usbdebug>(n)) logprintf x
68 extern int	usbdebug;
69 #else
70 #define DPRINTF(x)
71 #define DPRINTFN(n,x)
72 #endif
73 
74 struct uhub_softc {
75 	USBBASEDEVICE		sc_dev;		/* base device */
76 	usbd_device_handle	sc_hub;		/* USB device */
77 	usbd_pipe_handle	sc_ipipe;	/* interrupt pipe */
78 	u_int8_t		sc_status[1];	/* XXX more ports */
79 	u_char			sc_running;
80 };
81 
82 usbd_status uhub_init_port __P((struct usbd_port *));
83 usbd_status uhub_explore __P((usbd_device_handle hub));
84 void uhub_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
85 
86 #if defined(__FreeBSD__)
87 static bus_child_detached_t uhub_child_detached;
88 #endif
89 
90 USB_DECLARE_DRIVER_INIT(uhub,
91 			DEVMETHOD(bus_child_detached, uhub_child_detached));
92 
93 /*
94  * We need two attachment points:
95  * hub to usb and hub to hub
96  * Every other driver only connects to hubs
97  */
98 
99 #if defined(__NetBSD__) || defined(__OpenBSD__)
100 /* Create the driver instance for the hub connected to hub case */
101 struct cfattach uhub_uhub_ca = {
102 	sizeof(struct uhub_softc), uhub_match, uhub_attach,
103 	uhub_detach, uhub_activate
104 };
105 #elif defined(__FreeBSD__)
106 /* Create the driver instance for the hub connected to usb case. */
107 devclass_t uhubroot_devclass;
108 
109 static device_method_t uhubroot_methods[] = {
110 	DEVMETHOD(device_probe, uhub_match),
111 	DEVMETHOD(device_attach, uhub_attach),
112 
113 	/* detach is not allowed for a root hub */
114 	{0,0}
115 };
116 
117 static	driver_t uhubroot_driver = {
118 	"uhub",
119 	uhubroot_methods,
120 	sizeof(struct uhub_softc)
121 };
122 #endif
123 
124 USB_MATCH(uhub)
125 {
126 	USB_MATCH_START(uhub, uaa);
127 	usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device);
128 
129 	DPRINTFN(5,("uhub_match, dd=%p\n", dd));
130 	/*
131 	 * The subclass for hubs seems to be 0 for some and 1 for others,
132 	 * so we just ignore the subclass.
133 	 */
134 	if (uaa->iface == 0 && dd->bDeviceClass == UCLASS_HUB)
135 		return (UMATCH_DEVCLASS_DEVSUBCLASS);
136 	return (UMATCH_NONE);
137 }
138 
139 USB_ATTACH(uhub)
140 {
141 	USB_ATTACH_START(uhub, sc, uaa);
142 	usbd_device_handle dev = uaa->device;
143 	char devinfo[1024];
144 	usbd_status r;
145 	struct usbd_hub *hub;
146 	usb_device_request_t req;
147 	usb_hub_descriptor_t hubdesc;
148 	int p, port, nports, nremov;
149 	usbd_interface_handle iface;
150 	usb_endpoint_descriptor_t *ed;
151 
152 	DPRINTFN(1,("uhub_attach\n"));
153 	sc->sc_hub = dev;
154 	usbd_devinfo(dev, 1, devinfo);
155 	USB_ATTACH_SETUP;
156 	printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
157 
158 	r = usbd_set_config_index(dev, 0, 1);
159 	if (r != USBD_NORMAL_COMPLETION) {
160 		DPRINTF(("%s: configuration failed, error=%s\n",
161 			 USBDEVNAME(sc->sc_dev), usbd_errstr(r)));
162 		USB_ATTACH_ERROR_RETURN;
163 	}
164 
165 	if (dev->depth > USB_HUB_MAX_DEPTH) {
166 		printf("%s: hub depth (%d) exceeded, hub ignored\n",
167 		       USBDEVNAME(sc->sc_dev), USB_HUB_MAX_DEPTH);
168 		USB_ATTACH_ERROR_RETURN;
169 	}
170 
171 	/* Get hub descriptor. */
172 	req.bmRequestType = UT_READ_CLASS_DEVICE;
173 	req.bRequest = UR_GET_DESCRIPTOR;
174 	USETW(req.wValue, 0);
175 	USETW(req.wIndex, 0);
176 	USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
177 	DPRINTFN(1,("usb_init_hub: getting hub descriptor\n"));
178 	r = usbd_do_request(dev, &req, &hubdesc);
179 	nports = hubdesc.bNbrPorts;
180 	if (r == USBD_NORMAL_COMPLETION && nports > 7) {
181 		USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE + (nports+1) / 8);
182 		r = usbd_do_request(dev, &req, &hubdesc);
183 	}
184 	if (r != USBD_NORMAL_COMPLETION) {
185 		DPRINTF(("%s: getting hub descriptor failed, error=%s\n",
186 			 USBDEVNAME(sc->sc_dev), usbd_errstr(r)));
187 		USB_ATTACH_ERROR_RETURN;
188 	}
189 
190 	for (nremov = 0, port = 1; port <= nports; port++)
191 		if (!UHD_NOT_REMOV(&hubdesc, port))
192 			nremov++;
193 	printf("%s: %d port%s with %d removable, %s powered\n",
194 	       USBDEVNAME(sc->sc_dev), nports, nports != 1 ? "s" : "",
195 	       nremov, dev->self_powered ? "self" : "bus");
196 
197 	hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port),
198 		     M_USBDEV, M_NOWAIT);
199 	if (hub == 0)
200 		USB_ATTACH_ERROR_RETURN;
201 	dev->hub = hub;
202 	dev->hub->hubsoftc = sc;
203 	hub->explore = uhub_explore;
204 	hub->hubdesc = hubdesc;
205 
206 	DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, "
207 		    "parent->selfpowered=%d\n",
208 		 dev->self_powered, dev->powersrc->parent,
209 		 dev->powersrc->parent ?
210 		 dev->powersrc->parent->self_powered : 0));
211 
212 	if (!dev->self_powered && dev->powersrc->parent &&
213 	    !dev->powersrc->parent->self_powered) {
214 		printf("%s: bus powered hub connected to bus powered hub, "
215 		       "ignored\n", USBDEVNAME(sc->sc_dev));
216 		goto bad;
217 	}
218 
219 	/* Set up interrupt pipe. */
220 	r = usbd_device2interface_handle(dev, 0, &iface);
221 	if (r != USBD_NORMAL_COMPLETION) {
222 		printf("%s: no interface handle\n", USBDEVNAME(sc->sc_dev));
223 		goto bad;
224 	}
225 	ed = usbd_interface2endpoint_descriptor(iface, 0);
226 	if (ed == 0) {
227 		printf("%s: no endpoint descriptor\n", USBDEVNAME(sc->sc_dev));
228 		goto bad;
229 	}
230 	if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
231 		printf("%s: bad interrupt endpoint\n", USBDEVNAME(sc->sc_dev));
232 		goto bad;
233 	}
234 
235 	r = usbd_open_pipe_intr(iface, ed->bEndpointAddress,USBD_SHORT_XFER_OK,
236 				&sc->sc_ipipe, sc, sc->sc_status,
237 				sizeof(sc->sc_status),
238 				uhub_intr);
239 	if (r != USBD_NORMAL_COMPLETION) {
240 		printf("%s: cannot open interrupt pipe\n",
241 		       USBDEVNAME(sc->sc_dev));
242 		goto bad;
243 	}
244 
245 	/* Wait with power off for a while. */
246 	usbd_delay_ms(dev, USB_POWER_DOWN_TIME);
247 
248 	for (p = 0; p < nports; p++) {
249 		struct usbd_port *up = &hub->ports[p];
250 		up->device = 0;
251 		up->parent = dev;
252 		up->portno = p+1;
253 		r = uhub_init_port(up);
254 		if (r != USBD_NORMAL_COMPLETION)
255 			printf("%s: init of port %d failed\n",
256 			       USBDEVNAME(sc->sc_dev), up->portno);
257 	}
258 	sc->sc_running = 1;
259 
260 	USB_ATTACH_SUCCESS_RETURN;
261 
262  bad:
263 	free(hub, M_USBDEV);
264 	dev->hub = 0;
265 	USB_ATTACH_ERROR_RETURN;
266 }
267 
268 usbd_status
269 uhub_init_port(up)
270 	struct usbd_port *up;
271 {
272 	int port = up->portno;
273 	usbd_device_handle dev = up->parent;
274 	usbd_status r;
275 	u_int16_t pstatus;
276 
277 	r = usbd_get_port_status(dev, port, &up->status);
278 	if (r != USBD_NORMAL_COMPLETION)
279 		return (r);
280 	pstatus = UGETW(up->status.wPortStatus);
281 	DPRINTF(("usbd_init_port: adding hub port=%d status=0x%04x "
282 		 "change=0x%04x\n",
283 		 port, pstatus, UGETW(up->status.wPortChange)));
284 	if ((pstatus & UPS_PORT_POWER) == 0) {
285 		/* Port lacks power, turn it on */
286 
287 		/* First let the device go through a good power cycle, */
288 		usbd_delay_ms(dev, USB_PORT_POWER_DOWN_TIME);
289 
290 #if 0
291 usbd_clear_hub_feature(dev, UHF_C_HUB_OVER_CURRENT);
292 usbd_clear_port_feature(dev, port, UHF_C_PORT_OVER_CURRENT);
293 #endif
294 
295 		/* then turn the power on. */
296 		r = usbd_set_port_feature(dev, port, UHF_PORT_POWER);
297 		if (r != USBD_NORMAL_COMPLETION)
298 			return (r);
299 		DPRINTF(("usb_init_port: turn on port %d power status=0x%04x "
300 			 "change=0x%04x\n",
301 			 port, UGETW(up->status.wPortStatus),
302 			 UGETW(up->status.wPortChange)));
303 		/* Wait for stable power. */
304 		usbd_delay_ms(dev, dev->hub->hubdesc.bPwrOn2PwrGood *
305 			           UHD_PWRON_FACTOR);
306 		/* Get the port status again. */
307 		r = usbd_get_port_status(dev, port, &up->status);
308 		if (r != USBD_NORMAL_COMPLETION)
309 			return (r);
310 		DPRINTF(("usb_init_port: after power on status=0x%04x "
311 			 "change=0x%04x\n",
312 			 UGETW(up->status.wPortStatus),
313 			 UGETW(up->status.wPortChange)));
314 
315 #if 0
316 usbd_clear_hub_feature(dev, UHF_C_HUB_OVER_CURRENT);
317 usbd_clear_port_feature(dev, port, UHF_C_PORT_OVER_CURRENT);
318 usbd_get_port_status(dev, port, &up->status);
319 #endif
320 
321 		pstatus = UGETW(up->status.wPortStatus);
322 		if ((pstatus & UPS_PORT_POWER) == 0)
323 			printf("%s: port %d did not power up\n",
324  USBDEVNAME(((struct uhub_softc *)dev->hub->hubsoftc)->sc_dev), port);
325 
326 	}
327 	if (dev->self_powered)
328 		/* Self powered hub, give ports maximum current. */
329 		up->power = USB_MAX_POWER;
330 	else
331 		up->power = USB_MIN_POWER;
332 	return (USBD_NORMAL_COMPLETION);
333 }
334 
335 usbd_status
336 uhub_explore(dev)
337 	usbd_device_handle dev;
338 {
339 	usb_hub_descriptor_t *hd = &dev->hub->hubdesc;
340 	struct uhub_softc *sc = dev->hub->hubsoftc;
341 	struct usbd_port *up;
342 	usbd_status r;
343 	int port;
344 	int change, status;
345 
346 	DPRINTFN(10, ("uhub_explore dev=%p addr=%d\n", dev, dev->address));
347 
348 	if (!sc->sc_running)
349 		return (USBD_NOT_STARTED);
350 
351 	/* Ignore hubs that are too deep. */
352 	if (dev->depth > USB_HUB_MAX_DEPTH)
353 		return (USBD_TOO_DEEP);
354 
355 	for(port = 1; port <= hd->bNbrPorts; port++) {
356 		up = &dev->hub->ports[port-1];
357 		r = usbd_get_port_status(dev, port, &up->status);
358 		if (r != USBD_NORMAL_COMPLETION) {
359 			DPRINTF(("uhub_explore: get port status failed, "
360 				 "error=%s\n",
361 				 usbd_errstr(r)));
362 			continue;
363 		}
364 		status = UGETW(up->status.wPortStatus);
365 		change = UGETW(up->status.wPortChange);
366 		DPRINTFN(5, ("uhub_explore: port %d status 0x%04x 0x%04x\n",
367 			     port, status, change));
368 		if (change & UPS_C_PORT_ENABLED) {
369 			usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
370 			if (status & UPS_PORT_ENABLED) {
371 				printf("%s: illegal enable change, port %d\n",
372 				       USBDEVNAME(sc->sc_dev), port);
373 			} else {
374 				/* Port error condition. */
375 				if (up->restartcnt++ < USBD_RESTART_MAX) {
376 					printf("%s: port error, restarting "
377 					       "port %d\n",
378 					       USBDEVNAME(sc->sc_dev), port);
379 					goto disco;
380 				} else {
381 					printf("%s: port error, giving up "
382 					       "port %d\n",
383 					       USBDEVNAME(sc->sc_dev), port);
384 				}
385 			}
386 		}
387 		if (!(change & UPS_C_CONNECT_STATUS)) {
388 			/* No status change, just do recursive explore. */
389 			if (up->device && up->device->hub)
390 				up->device->hub->explore(up->device);
391 			continue;
392 		}
393 		DPRINTF(("uhub_explore: status change hub=%d port=%d\n",
394 			 dev->address, port));
395 		usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION);
396 		usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
397 		/*
398 		 * If there is already a device on the port the change status
399 		 * must mean that is has disconnected.  Looking at the
400 		 * current connect status is not enough to figure this out
401 		 * since a new unit may have been connected before we handle
402 		 * the disconnect.
403 		 */
404 	disco:
405 		if (up->device) {
406 			/* Disconnected */
407 			DPRINTF(("uhub_explore: device %d disappeared "
408 				 "on port %d\n", up->device->address, port));
409 			usb_disconnect_port(up, USBDEV(sc->sc_dev));
410 			usbd_clear_port_feature(dev, port,
411 						UHF_C_PORT_CONNECTION);
412 		}
413 		if (!(status & UPS_CURRENT_CONNECT_STATUS))
414 			continue;
415 
416 		/* Connected */
417 		up->restartcnt = 0;
418 
419 		/* Wait for maximum device power up time. */
420 		usbd_delay_ms(dev, USB_PORT_POWERUP_DELAY);
421 
422 		/* Reset port, which implies enabling it. */
423 		if (usbd_reset_port(dev, port, &up->status) !=
424 		    USBD_NORMAL_COMPLETION)
425 			continue;
426 
427 		/* Get device info and set its address. */
428 		r = usbd_new_device(USBDEV(sc->sc_dev), dev->bus,
429 				    dev->depth + 1, status & UPS_LOW_SPEED,
430 				    port, up);
431 		/* XXX retry a few times? */
432 		if (r != USBD_NORMAL_COMPLETION) {
433 			DPRINTFN(-1,("uhub_explore: usb_new_device failed, "
434 				     "error=%s\n", usbd_errstr(r)));
435 			/* Avoid addressing problems by disabling. */
436 			/* usbd_reset_port(dev, port, &up->status); */
437 
438 			/*
439 			 * The unit refused to accept a new address, or had
440 			 * some other serious problem.  Since we cannot leave
441 			 * at 0 we have to disable the port instead.
442 			 */
443 			printf("%s: device problem, disabling port %d\n",
444 			       USBDEVNAME(sc->sc_dev), port);
445 			usbd_clear_port_feature(dev, port, UHF_PORT_ENABLE);
446 			/* Make sure we don't try to restart it infinitely. */
447 			up->restartcnt = USBD_RESTART_MAX;
448 		} else {
449 			if (up->device->hub)
450 				up->device->hub->explore(up->device);
451 		}
452 	}
453 	return (USBD_NORMAL_COMPLETION);
454 }
455 
456 #if defined(__NetBSD__) || defined(__OpenBSD__)
457 int
458 uhub_activate(self, act)
459 	device_ptr_t self;
460 	enum devact act;
461 {
462 	struct uhub_softc *sc = (struct uhub_softc *)self;
463 	usbd_device_handle devhub = sc->sc_hub;
464 	int nports, p, i;
465 
466 	switch (act) {
467 	case DVACT_ACTIVATE:
468 		return (EOPNOTSUPP);
469 		break;
470 
471 	case DVACT_DEACTIVATE:
472 		nports = devhub->hub->hubdesc.bNbrPorts;
473 		for(p = 0; p < nports; p++) {
474 			usbd_device_handle dev = devhub->hub->ports[p].device;
475 			if (dev) {
476 				for (i = 0; dev->subdevs[i]; i++)
477 					config_deactivate(dev->subdevs[i]);
478 			}
479 		}
480 		break;
481 	}
482 	return (0);
483 }
484 #endif
485 
486 /*
487  * Called from process context when the hub is gone.
488  * Detach all devices on active ports.
489  */
490 USB_DETACH(uhub)
491 {
492 	USB_DETACH_START(uhub, sc);
493 	usbd_device_handle dev = sc->sc_hub;
494 	struct usbd_port *rup;
495 	int port, nports;
496 
497 #if defined(__NetBSD__) || defined(__OpenBSD__)
498 	DPRINTF(("uhub_detach: sc=%p flags=%d\n", sc, flags));
499 #elif defined(__FreeBSD__)
500 	DPRINTF(("uhub_detach: sc=%port\n", sc));
501 #endif
502 
503 	if (!dev->hub)		/* Must be partially working */
504 		return (0);
505 
506 	usbd_abort_pipe(sc->sc_ipipe);
507 	usbd_close_pipe(sc->sc_ipipe);
508 
509 	nports = dev->hub->hubdesc.bNbrPorts;
510 	for(port = 0; port < nports; port++) {
511 		rup = &dev->hub->ports[port];
512 		if (rup->device)
513 			usb_disconnect_port(rup, self);
514 	}
515 
516 	free(dev->hub, M_USBDEV);
517 	dev->hub = 0;
518 
519 	return (0);
520 }
521 
522 /*
523  * Hub interrupt.
524  * This an indication that some port has changed status.
525  * Notify the bus event handler thread that we need
526  * to be explored again.
527  */
528 void
529 uhub_intr(reqh, addr, status)
530 	usbd_request_handle reqh;
531 	usbd_private_handle addr;
532 	usbd_status status;
533 {
534 	struct uhub_softc *sc = addr;
535 
536 	DPRINTFN(5,("uhub_intr: sc=%p\n", sc));
537 	if (status != USBD_NORMAL_COMPLETION)
538 		usbd_clear_endpoint_stall_async(sc->sc_ipipe);
539 
540 	usb_needs_explore(sc->sc_hub->bus);
541 }
542 
543 #if defined(__FreeBSD__)
544 DRIVER_MODULE(uhub, usb, uhubroot_driver, uhubroot_devclass, 0, 0);
545 DRIVER_MODULE(uhub, uhub, uhub_driver, uhub_devclass, usbd_driver_load, 0);
546 #endif
547