Annotation of src/sys/dev/usb/uhub.c, Revision 1.126.2.19
1.126.2.19! skrll 1: /* $NetBSD: uhub.c,v 1.126.2.18 2016/01/02 14:04:41 skrll Exp $ */
1.34 augustss 2: /* $FreeBSD: src/sys/dev/usb/uhub.c,v 1.18 1999/11/17 22:33:43 n_hibma Exp $ */
1.126.2.16 skrll 3: /* $OpenBSD: uhub.c,v 1.86 2015/06/29 18:27:40 mpi Exp $ */
1.1 augustss 4:
5: /*
1.74 mycroft 6: * Copyright (c) 1998, 2004 The NetBSD Foundation, Inc.
1.1 augustss 7: * All rights reserved.
8: *
1.6 augustss 9: * This code is derived from software contributed to The NetBSD Foundation
1.44 augustss 10: * by Lennart Augustsson (lennart@augustsson.net) at
1.6 augustss 11: * Carlstedt Research & Technology.
1.1 augustss 12: *
13: * Redistribution and use in source and binary forms, with or without
14: * modification, are permitted provided that the following conditions
15: * are met:
16: * 1. Redistributions of source code must retain the above copyright
17: * notice, this list of conditions and the following disclaimer.
18: * 2. Redistributions in binary form must reproduce the above copyright
19: * notice, this list of conditions and the following disclaimer in the
20: * documentation and/or other materials provided with the distribution.
21: *
22: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32: * POSSIBILITY OF SUCH DAMAGE.
1.15 augustss 33: */
34:
35: /*
1.64 ichiro 36: * USB spec: http://www.usb.org/developers/docs/usbspec.zip
1.1 augustss 37: */
1.53 lukem 38:
39: #include <sys/cdefs.h>
1.126.2.19! skrll 40: __KERNEL_RCSID(0, "$NetBSD: uhub.c,v 1.126.2.18 2016/01/02 14:04:41 skrll Exp $");
1.1 augustss 41:
42: #include <sys/param.h>
1.126.2.9 skrll 43:
1.126.2.10 skrll 44: #include <sys/bus.h>
1.126.2.9 skrll 45: #include <sys/device.h>
1.1 augustss 46: #include <sys/kernel.h>
1.126.2.3 skrll 47: #include <sys/kmem.h>
1.34 augustss 48: #include <sys/proc.h>
1.126.2.9 skrll 49: #include <sys/sysctl.h>
1.126.2.10 skrll 50: #include <sys/systm.h>
1.28 augustss 51:
1.1 augustss 52:
53: #include <dev/usb/usb.h>
54: #include <dev/usb/usbdi.h>
55: #include <dev/usb/usbdi_util.h>
56: #include <dev/usb/usbdivar.h>
1.126.2.9 skrll 57: #include <dev/usb/usbhist.h>
1.1 augustss 58:
1.126.2.9 skrll 59: #ifdef USB_DEBUG
60: #ifndef UHUB_DEBUG
61: #define uhubdebug 0
1.1 augustss 62: #else
1.126.2.9 skrll 63: static int uhubdebug = 0;
64:
65: SYSCTL_SETUP(sysctl_hw_uhub_setup, "sysctl hw.uhub setup")
66: {
67: int err;
68: const struct sysctlnode *rnode;
69: const struct sysctlnode *cnode;
70:
71: err = sysctl_createv(clog, 0, NULL, &rnode,
72: CTLFLAG_PERMANENT, CTLTYPE_NODE, "uhub",
73: SYSCTL_DESCR("uhub global controls"),
74: NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
75:
76: if (err)
77: goto fail;
78:
79: /* control debugging printfs */
80: err = sysctl_createv(clog, 0, &rnode, &cnode,
81: CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
82: "debug", SYSCTL_DESCR("Enable debugging output"),
83: NULL, 0, &uhubdebug, sizeof(uhubdebug), CTL_CREATE, CTL_EOL);
84: if (err)
85: goto fail;
86:
87: return;
88: fail:
89: aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err);
90: }
91:
92: #endif /* UHUB_DEBUG */
93: #endif /* USB_DEBUG */
94:
95: #define DPRINTF(FMT,A,B,C,D) USBHIST_LOGN(uhubdebug,1,FMT,A,B,C,D)
96: #define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(uhubdebug,N,FMT,A,B,C,D)
97: #define UHUBHIST_FUNC() USBHIST_FUNC()
98: #define UHUBHIST_CALLED(name) USBHIST_CALLED(uhubdebug)
1.1 augustss 99:
100: struct uhub_softc {
1.111 dyoung 101: device_t sc_dev; /* base device */
1.126.2.7 skrll 102: struct usbd_device * sc_hub; /* USB device */
1.86 drochner 103: int sc_proto; /* device protocol */
1.126.2.7 skrll 104: struct usbd_pipe * sc_ipipe; /* interrupt pipe */
1.87 drochner 105:
1.126.2.18 skrll 106: kmutex_t sc_lock;
107:
1.126.2.1 skrll 108: uint8_t *sc_statusbuf;
1.126.2.12 skrll 109: uint8_t *sc_statuspend;
1.126.2.1 skrll 110: uint8_t *sc_status;
1.87 drochner 111: size_t sc_statuslen;
112: int sc_explorepending;
113:
1.11 augustss 114: u_char sc_running;
1.1 augustss 115: };
1.87 drochner 116:
1.126.2.12 skrll 117: #define UHUB_IS_HIGH_SPEED(sc) \
118: ((sc)->sc_proto == UDPROTO_HSHUBSTT || (sc)->sc_proto == UDPROTO_HSHUBMTT)
1.86 drochner 119: #define UHUB_IS_SINGLE_TT(sc) ((sc)->sc_proto == UDPROTO_HSHUBSTT)
1.1 augustss 120:
1.87 drochner 121: #define PORTSTAT_ISSET(sc, port) \
122: ((sc)->sc_status[(port) / 8] & (1 << ((port) % 8)))
123:
1.126.2.7 skrll 124: Static usbd_status uhub_explore(struct usbd_device *);
125: Static void uhub_intr(struct usbd_xfer *, void *, usbd_status);
1.1 augustss 126:
1.32 augustss 127:
1.58 augustss 128: /*
1.32 augustss 129: * We need two attachment points:
130: * hub to usb and hub to hub
131: * Every other driver only connects to hubs
132: */
1.1 augustss 133:
1.98 cube 134: int uhub_match(device_t, cfdata_t, void *);
1.95 dyoung 135: void uhub_attach(device_t, device_t, void *);
1.103 kent 136: int uhub_rescan(device_t, const char *, const int *);
1.95 dyoung 137: void uhub_childdet(device_t, device_t);
138: int uhub_detach(device_t, int);
139: extern struct cfdriver uhub_cd;
1.104 dyoung 140: CFATTACH_DECL3_NEW(uhub, sizeof(struct uhub_softc), uhub_match,
1.108 dyoung 141: uhub_attach, uhub_detach, NULL, uhub_rescan, uhub_childdet,
1.104 dyoung 142: DVF_DETACH_SHUTDOWN);
1.99 drochner 143: CFATTACH_DECL2_NEW(uroothub, sizeof(struct uhub_softc), uhub_match,
1.108 dyoung 144: uhub_attach, uhub_detach, NULL, uhub_rescan, uhub_childdet);
1.1 augustss 145:
1.109 pooka 146: /*
147: * Setting this to 1 makes sure than an uhub attaches even at higher
148: * priority than ugen when ugen_override is set to 1. This allows to
149: * probe the whole USB bus and attach functions with ugen.
150: */
151: int uhub_ubermatch = 0;
152:
1.126.2.12 skrll 153: static usbd_status
154: usbd_get_hub_desc(struct usbd_device *dev, usb_hub_descriptor_t *hd, int speed)
155: {
156: usb_device_request_t req;
157: usbd_status err;
158: int nports;
159:
160: UHUBHIST_FUNC(); UHUBHIST_CALLED();
161:
162: /* don't issue UDESC_HUB to SS hub, or it would stall */
163: if (dev->ud_depth != 0 && USB_IS_SS(dev->ud_speed)) {
164: usb_hub_ss_descriptor_t hssd;
165: int rmvlen;
166:
167: memset(&hssd, 0, sizeof(hssd));
168: req.bmRequestType = UT_READ_CLASS_DEVICE;
169: req.bRequest = UR_GET_DESCRIPTOR;
170: USETW2(req.wValue, UDESC_SS_HUB, 0);
171: USETW(req.wIndex, 0);
172: USETW(req.wLength, USB_HUB_SS_DESCRIPTOR_SIZE);
173: DPRINTFN(1, "getting sshub descriptor", 0, 0, 0, 0);
174: err = usbd_do_request(dev, &req, &hssd);
175: nports = hssd.bNbrPorts;
176: if (dev->ud_depth != 0 && nports > UHD_SS_NPORTS_MAX) {
177: DPRINTF("num of ports %d exceeds maxports %d",
178: nports, UHD_SS_NPORTS_MAX, 0, 0);
179: nports = hd->bNbrPorts = UHD_SS_NPORTS_MAX;
180: }
181: rmvlen = (nports + 7) / 8;
182: hd->bDescLength = USB_HUB_DESCRIPTOR_SIZE +
183: (rmvlen > 1 ? rmvlen : 1) - 1;
184: memcpy(hd->DeviceRemovable, hssd.DeviceRemovable, rmvlen);
185: hd->bDescriptorType = hssd.bDescriptorType;
186: hd->bNbrPorts = hssd.bNbrPorts;
187: hd->wHubCharacteristics[0] = hssd.wHubCharacteristics[0];
188: hd->wHubCharacteristics[1] = hssd.wHubCharacteristics[1];
189: hd->bPwrOn2PwrGood = hssd.bPwrOn2PwrGood;
190: hd->bHubContrCurrent = hssd.bHubContrCurrent;
191: } else {
192: req.bmRequestType = UT_READ_CLASS_DEVICE;
193: req.bRequest = UR_GET_DESCRIPTOR;
194: USETW2(req.wValue, UDESC_HUB, 0);
195: USETW(req.wIndex, 0);
196: USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
197: DPRINTFN(1, "getting hub descriptor", 0, 0, 0, 0);
198: err = usbd_do_request(dev, &req, hd);
199: nports = hd->bNbrPorts;
200: if (!err && nports > 7) {
201: USETW(req.wLength,
202: USB_HUB_DESCRIPTOR_SIZE + (nports+1) / 8);
203: err = usbd_do_request(dev, &req, hd);
204: }
205: }
206:
207: return err;
208: }
209:
210: static usbd_status
211: usbd_set_hub_depth(struct usbd_device *dev, int depth)
212: {
213: usb_device_request_t req;
214:
215: req.bmRequestType = UT_WRITE_CLASS_DEVICE;
216: req.bRequest = UR_SET_HUB_DEPTH;
217: USETW(req.wValue, depth);
218: USETW(req.wIndex, 0);
219: USETW(req.wLength, 0);
220: return usbd_do_request(dev, &req, 0);
221: }
222:
1.105 dyoung 223: int
224: uhub_match(device_t parent, cfdata_t match, void *aux)
1.1 augustss 225: {
1.107 dyoung 226: struct usb_attach_arg *uaa = aux;
1.109 pooka 227: int matchvalue;
228:
1.126.2.9 skrll 229: UHUBHIST_FUNC(); UHUBHIST_CALLED();
230:
1.109 pooka 231: if (uhub_ubermatch)
232: matchvalue = UMATCH_HIGHEST+1;
233: else
234: matchvalue = UMATCH_DEVCLASS_DEVSUBCLASS;
1.58 augustss 235:
1.126.2.9 skrll 236: DPRINTFN(5, "uaa=%p", uaa, 0, 0, 0);
1.58 augustss 237: /*
1.1 augustss 238: * The subclass for hubs seems to be 0 for some and 1 for others,
239: * so we just ignore the subclass.
240: */
1.126.2.8 skrll 241: if (uaa->uaa_class == UDCLASS_HUB)
1.126.2.4 skrll 242: return matchvalue;
243: return UMATCH_NONE;
1.1 augustss 244: }
245:
1.105 dyoung 246: void
247: uhub_attach(device_t parent, device_t self, void *aux)
1.1 augustss 248: {
1.107 dyoung 249: struct uhub_softc *sc = device_private(self);
250: struct usb_attach_arg *uaa = aux;
1.126.2.8 skrll 251: struct usbd_device *dev = uaa->uaa_device;
1.76 augustss 252: char *devinfop;
1.33 augustss 253: usbd_status err;
1.70 augustss 254: struct usbd_hub *hub = NULL;
1.1 augustss 255: usb_hub_descriptor_t hubdesc;
1.42 augustss 256: int p, port, nports, nremov, pwrdly;
1.126.2.7 skrll 257: struct usbd_interface *iface;
1.1 augustss 258: usb_endpoint_descriptor_t *ed;
1.83 drochner 259: #if 0 /* notyet */
1.70 augustss 260: struct usbd_tt *tts = NULL;
1.83 drochner 261: #endif
1.58 augustss 262:
1.126.2.9 skrll 263: UHUBHIST_FUNC(); UHUBHIST_CALLED();
264:
1.98 cube 265: sc->sc_dev = self;
1.1 augustss 266: sc->sc_hub = dev;
1.126.2.8 skrll 267: sc->sc_proto = uaa->uaa_proto;
1.76 augustss 268:
269: devinfop = usbd_devinfo_alloc(dev, 1);
1.96 jmcneill 270: aprint_naive("\n");
271: aprint_normal(": %s\n", devinfop);
1.76 augustss 272: usbd_devinfo_free(devinfop);
1.1 augustss 273:
1.126.2.2 skrll 274: if (dev->ud_depth > 0 && UHUB_IS_HIGH_SPEED(sc)) {
1.98 cube 275: aprint_normal_dev(self, "%s transaction translator%s\n",
1.70 augustss 276: UHUB_IS_SINGLE_TT(sc) ? "single" : "multiple",
277: UHUB_IS_SINGLE_TT(sc) ? "" : "s");
1.69 augustss 278: }
279:
1.33 augustss 280: err = usbd_set_config_index(dev, 0, 1);
281: if (err) {
1.126.2.9 skrll 282: DPRINTF("configuration failed, sc %p error %d", sc, err, 0, 0);
1.107 dyoung 283: return;
1.1 augustss 284: }
285:
1.126.2.2 skrll 286: if (dev->ud_depth > USB_HUB_MAX_DEPTH) {
1.98 cube 287: aprint_error_dev(self,
288: "hub depth (%d) exceeded, hub ignored\n",
289: USB_HUB_MAX_DEPTH);
1.107 dyoung 290: return;
1.1 augustss 291: }
292:
293: /* Get hub descriptor. */
1.126.2.12 skrll 294: memset(&hubdesc, 0, sizeof(hubdesc));
295: err = usbd_get_hub_desc(dev, &hubdesc, dev->ud_speed);
1.11 augustss 296: nports = hubdesc.bNbrPorts;
1.33 augustss 297: if (err) {
1.126.2.17 skrll 298: DPRINTF("getting hub descriptor failed, uhub%d error %d",
1.126.2.9 skrll 299: device_unit(self), err, 0, 0);
1.107 dyoung 300: return;
1.1 augustss 301: }
302:
1.11 augustss 303: for (nremov = 0, port = 1; port <= nports; port++)
304: if (!UHD_NOT_REMOV(&hubdesc, port))
305: nremov++;
1.98 cube 306: aprint_verbose_dev(self, "%d port%s with %d removable, %s powered\n",
307: nports, nports != 1 ? "s" : "", nremov,
1.126.2.2 skrll 308: dev->ud_selfpowered ? "self" : "bus");
1.11 augustss 309:
1.70 augustss 310: if (nports == 0) {
1.98 cube 311: aprint_debug_dev(self, "no ports, hub ignored\n");
1.70 augustss 312: goto bad;
313: }
314:
1.126.2.3 skrll 315: hub = kmem_alloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port),
316: KM_SLEEP);
1.33 augustss 317: if (hub == NULL)
1.107 dyoung 318: return;
1.126.2.2 skrll 319: dev->ud_hub = hub;
320: dev->ud_hub->uh_hubsoftc = sc;
321: hub->uh_explore = uhub_explore;
322: hub->uh_hubdesc = hubdesc;
1.58 augustss 323:
1.126.2.12 skrll 324: if (USB_IS_SS(dev->ud_speed) && dev->ud_depth != 0) {
325: aprint_debug_dev(self, "setting hub depth %u\n",
326: dev->ud_depth-1);
327: err = usbd_set_hub_depth(dev, dev->ud_depth - 1);
328: if (err) {
329: aprint_error_dev(self, "can't set depth\n");
330: goto bad;
331: }
332: }
333:
1.1 augustss 334: /* Set up interrupt pipe. */
1.33 augustss 335: err = usbd_device2interface_handle(dev, 0, &iface);
336: if (err) {
1.98 cube 337: aprint_error_dev(self, "no interface handle\n");
1.18 augustss 338: goto bad;
1.1 augustss 339: }
1.83 drochner 340:
341: if (UHUB_IS_HIGH_SPEED(sc) && !UHUB_IS_SINGLE_TT(sc)) {
342: err = usbd_set_interface(iface, 1);
343: if (err)
1.98 cube 344: aprint_error_dev(self, "can't enable multiple TTs\n");
1.83 drochner 345: }
346:
1.1 augustss 347: ed = usbd_interface2endpoint_descriptor(iface, 0);
1.33 augustss 348: if (ed == NULL) {
1.98 cube 349: aprint_error_dev(self, "no endpoint descriptor\n");
1.18 augustss 350: goto bad;
1.1 augustss 351: }
352: if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
1.98 cube 353: aprint_error_dev(self, "bad interrupt endpoint\n");
1.18 augustss 354: goto bad;
1.1 augustss 355: }
356:
1.87 drochner 357: sc->sc_statuslen = (nports + 1 + 7) / 8;
1.126.2.3 skrll 358: sc->sc_statusbuf = kmem_alloc(sc->sc_statuslen, KM_SLEEP);
1.87 drochner 359: if (!sc->sc_statusbuf)
360: goto bad;
1.126.2.12 skrll 361: sc->sc_statuspend = kmem_zalloc(sc->sc_statuslen, KM_SLEEP);
362: if (!sc->sc_statuspend)
363: goto bad;
1.126.2.3 skrll 364: sc->sc_status = kmem_alloc(sc->sc_statuslen, KM_SLEEP);
1.84 drochner 365: if (!sc->sc_status)
366: goto bad;
1.126.2.18 skrll 367:
368: mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB);
369: memset(sc->sc_statuspend, 0, sc->sc_statuslen);
1.84 drochner 370:
1.87 drochner 371: /* force initial scan */
372: memset(sc->sc_status, 0xff, sc->sc_statuslen);
373: sc->sc_explorepending = 1;
374:
1.33 augustss 375: err = usbd_open_pipe_intr(iface, ed->bEndpointAddress,
1.122 jmcneill 376: USBD_SHORT_XFER_OK|USBD_MPSAFE, &sc->sc_ipipe, sc,
377: sc->sc_statusbuf, sc->sc_statuslen,
378: uhub_intr, USBD_DEFAULT_INTERVAL);
1.33 augustss 379: if (err) {
1.98 cube 380: aprint_error_dev(self, "cannot open interrupt pipe\n");
1.18 augustss 381: goto bad;
1.1 augustss 382: }
383:
1.11 augustss 384: /* Wait with power off for a while. */
1.13 augustss 385: usbd_delay_ms(dev, USB_POWER_DOWN_TIME);
1.11 augustss 386:
1.105 dyoung 387: usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, dev, sc->sc_dev);
1.37 augustss 388:
1.42 augustss 389: /*
390: * To have the best chance of success we do things in the exact same
1.123 jakllsch 391: * order as Windows 98. This should not be necessary, but some
1.42 augustss 392: * devices do not follow the USB specs to the letter.
393: *
394: * These are the events on the bus when a hub is attached:
395: * Get device and config descriptors (see attach code)
396: * Get hub descriptor (see above)
397: * For all ports
398: * turn on power
399: * wait for power to become stable
400: * (all below happens in explore code)
401: * For all ports
402: * clear C_PORT_CONNECTION
403: * For all ports
404: * get port status
405: * if device connected
1.57 augustss 406: * wait 100 ms
1.42 augustss 407: * turn on reset
408: * wait
409: * clear C_PORT_RESET
410: * get port status
411: * proceed with device attachment
412: */
413:
1.83 drochner 414: #if 0
1.78 christos 415: if (UHUB_IS_HIGH_SPEED(sc) && nports > 0) {
1.126.2.3 skrll 416: tts = kmem_alloc((UHUB_IS_SINGLE_TT(sc) ? 1 : nports) *
1.126.2.15 skrll 417: sizeof(struct usbd_tt), KM_SLEEP);
1.70 augustss 418: if (!tts)
419: goto bad;
420: }
1.83 drochner 421: #endif
1.42 augustss 422: /* Set up data structures */
1.11 augustss 423: for (p = 0; p < nports; p++) {
1.126.2.2 skrll 424: struct usbd_port *up = &hub->uh_ports[p];
425: up->up_dev = NULL;
426: up->up_parent = dev;
427: up->up_portno = p+1;
428: if (dev->ud_selfpowered)
1.42 augustss 429: /* Self powered hub, give ports maximum current. */
1.126.2.2 skrll 430: up->up_power = USB_MAX_POWER;
1.42 augustss 431: else
1.126.2.2 skrll 432: up->up_power = USB_MIN_POWER;
433: up->up_restartcnt = 0;
434: up->up_reattach = 0;
1.83 drochner 435: #if 0
1.70 augustss 436: if (UHUB_IS_HIGH_SPEED(sc)) {
437: up->tt = &tts[UHUB_IS_SINGLE_TT(sc) ? 0 : p];
438: up->tt->hub = hub;
439: } else {
440: up->tt = NULL;
441: }
1.83 drochner 442: #endif
1.42 augustss 443: }
444:
445: /* XXX should check for none, individual, or ganged power? */
446:
1.126.2.2 skrll 447: pwrdly = dev->ud_hub->uh_hubdesc.bPwrOn2PwrGood * UHD_PWRON_FACTOR
1.42 augustss 448: + USB_EXTRA_POWER_UP_TIME;
449: for (port = 1; port <= nports; port++) {
450: /* Turn the power on. */
451: err = usbd_set_port_feature(dev, port, UHF_PORT_POWER);
1.33 augustss 452: if (err)
1.98 cube 453: aprint_error_dev(self, "port %d power on failed, %s\n",
454: port, usbd_errstr(err));
1.126.2.17 skrll 455: DPRINTF("uhub%d turn on port %d power", device_unit(self),
1.126.2.9 skrll 456: port, 0, 0);
1.94 jmcneill 457: }
458:
459: /* Wait for stable power if we are not a root hub */
1.126.2.2 skrll 460: if (dev->ud_powersrc->up_parent != NULL)
1.42 augustss 461: usbd_delay_ms(dev, pwrdly);
462:
463: /* The usual exploration will finish the setup. */
464:
1.1 augustss 465: sc->sc_running = 1;
1.11 augustss 466:
1.92 jmcneill 467: if (!pmf_device_register(self, NULL, NULL))
468: aprint_error_dev(self, "couldn't establish power handler\n");
469:
1.107 dyoung 470: return;
1.11 augustss 471:
1.18 augustss 472: bad:
1.84 drochner 473: if (sc->sc_status)
1.126.2.6 skrll 474: kmem_free(sc->sc_status, sc->sc_statuslen);
1.126.2.12 skrll 475: if (sc->sc_statuspend)
476: kmem_free(sc->sc_statuspend, sc->sc_statuslen);
1.101 drochner 477: if (sc->sc_statusbuf)
1.126.2.3 skrll 478: kmem_free(sc->sc_statusbuf, sc->sc_statuslen);
1.70 augustss 479: if (hub)
1.126.2.3 skrll 480: kmem_free(hub,
481: sizeof(*hub) + (nports-1) * sizeof(struct usbd_port));
1.126.2.2 skrll 482: dev->ud_hub = NULL;
1.107 dyoung 483: return;
1.11 augustss 484: }
485:
1.1 augustss 486: usbd_status
1.126.2.7 skrll 487: uhub_explore(struct usbd_device *dev)
1.1 augustss 488: {
1.126.2.2 skrll 489: usb_hub_descriptor_t *hd = &dev->ud_hub->uh_hubdesc;
490: struct uhub_softc *sc = dev->ud_hub->uh_hubsoftc;
1.1 augustss 491: struct usbd_port *up;
1.33 augustss 492: usbd_status err;
1.56 augustss 493: int speed;
1.1 augustss 494: int port;
1.72 joff 495: int change, status, reconnect;
1.1 augustss 496:
1.126.2.9 skrll 497: UHUBHIST_FUNC(); UHUBHIST_CALLED();
498:
1.126.2.17 skrll 499: DPRINTFN(10, "uhub%d dev=%p addr=%d speed=%u",
1.126.2.9 skrll 500: device_unit(sc->sc_dev), dev, dev->ud_addr, dev->ud_speed);
1.1 augustss 501:
502: if (!sc->sc_running)
1.126.2.4 skrll 503: return USBD_NOT_STARTED;
1.1 augustss 504:
505: /* Ignore hubs that are too deep. */
1.126.2.2 skrll 506: if (dev->ud_depth > USB_HUB_MAX_DEPTH)
1.126.2.4 skrll 507: return USBD_TOO_DEEP;
1.1 augustss 508:
1.101 drochner 509: if (PORTSTAT_ISSET(sc, 0)) { /* hub status change */
510: usb_hub_status_t hs;
511:
512: err = usbd_get_hub_status(dev, &hs);
513: if (err) {
1.126.2.17 skrll 514: DPRINTF("uhub%d get hub status failed, err %d",
1.126.2.9 skrll 515: device_unit(sc->sc_dev), err, 0, 0);
1.101 drochner 516: } else {
517: /* just acknowledge */
518: status = UGETW(hs.wHubStatus);
519: change = UGETW(hs.wHubChange);
1.126.2.17 skrll 520: DPRINTF("uhub%d s/c=%x/%x", device_unit(sc->sc_dev),
1.126.2.9 skrll 521: status, change, 0);
522:
1.101 drochner 523: if (change & UHS_LOCAL_POWER)
524: usbd_clear_hub_feature(dev,
525: UHF_C_HUB_LOCAL_POWER);
526: if (change & UHS_OVER_CURRENT)
527: usbd_clear_hub_feature(dev,
528: UHF_C_HUB_OVER_CURRENT);
529: }
530: }
531:
1.84 drochner 532: for (port = 1; port <= hd->bNbrPorts; port++) {
1.126.2.2 skrll 533: up = &dev->ud_hub->uh_ports[port-1];
1.87 drochner 534:
535: /* reattach is needed after firmware upload */
1.126.2.2 skrll 536: reconnect = up->up_reattach;
537: up->up_reattach = 0;
1.87 drochner 538:
539: status = change = 0;
540:
541: /* don't check if no change summary notification */
542: if (PORTSTAT_ISSET(sc, port) || reconnect) {
1.126.2.2 skrll 543: err = usbd_get_port_status(dev, port, &up->up_status);
1.87 drochner 544: if (err) {
1.126.2.17 skrll 545: DPRINTF("uhub%d get port stat failed, err %d",
1.126.2.9 skrll 546: device_unit(sc->sc_dev), err, 0, 0);
1.87 drochner 547: continue;
548: }
1.126.2.2 skrll 549: status = UGETW(up->up_status.wPortStatus);
550: change = UGETW(up->up_status.wPortChange);
1.126.2.9 skrll 551:
1.126.2.17 skrll 552: DPRINTF("uhub%d port %d: s/c=%x/%x",
1.126.2.9 skrll 553: device_unit(sc->sc_dev), port, status, change);
1.87 drochner 554: }
555: if (!change && !reconnect) {
556: /* No status change, just do recursive explore. */
1.126.2.2 skrll 557: if (up->up_dev != NULL && up->up_dev->ud_hub != NULL)
558: up->up_dev->ud_hub->uh_explore(up->up_dev);
1.1 augustss 559: continue;
560: }
1.87 drochner 561:
1.11 augustss 562: if (change & UPS_C_PORT_ENABLED) {
1.126.2.17 skrll 563: DPRINTF("uhub%d port %d C_PORT_ENABLED",
1.126.2.9 skrll 564: device_unit(sc->sc_dev), port, 0, 0);
1.11 augustss 565: usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
1.68 mycroft 566: if (change & UPS_C_CONNECT_STATUS) {
567: /* Ignore the port error if the device
568: vanished. */
569: } else if (status & UPS_PORT_ENABLED) {
1.98 cube 570: aprint_error_dev(sc->sc_dev,
571: "illegal enable change, port %d\n", port);
1.11 augustss 572: } else {
573: /* Port error condition. */
1.126.2.2 skrll 574: if (up->up_restartcnt) /* no message first time */
1.98 cube 575: aprint_error_dev(sc->sc_dev,
576: "port error, restarting port %d\n",
577: port);
1.47 augustss 578:
1.126.2.2 skrll 579: if (up->up_restartcnt++ < USBD_RESTART_MAX)
1.11 augustss 580: goto disco;
1.47 augustss 581: else
1.98 cube 582: aprint_error_dev(sc->sc_dev,
583: "port error, giving up port %d\n",
584: port);
1.11 augustss 585: }
586: }
1.126.2.13 skrll 587: if (change & UPS_C_PORT_RESET) {
1.126.2.14 skrll 588: /*
589: * some xHCs set PortResetChange instead of CSC
590: * when port is reset.
591: */
592: if ((status & UPS_CURRENT_CONNECT_STATUS) != 0) {
593: change |= UPS_C_CONNECT_STATUS;
594: }
1.126.2.12 skrll 595: usbd_clear_port_feature(dev, port, UHF_C_PORT_RESET);
1.126.2.13 skrll 596: }
1.126.2.12 skrll 597: if (change & UPS_C_BH_PORT_RESET) {
1.126.2.13 skrll 598: /*
599: * some xHCs set WarmResetChange instead of CSC
600: * when port is reset.
601: */
602: if ((status & UPS_CURRENT_CONNECT_STATUS) != 0) {
603: change |= UPS_C_CONNECT_STATUS;
604: }
1.126.2.12 skrll 605: usbd_clear_port_feature(dev, port,
606: UHF_C_BH_PORT_RESET);
607: }
608: if (change & UPS_C_PORT_LINK_STATE)
609: usbd_clear_port_feature(dev, port,
610: UHF_C_PORT_LINK_STATE);
611: if (change & UPS_C_PORT_CONFIG_ERROR)
612: usbd_clear_port_feature(dev, port,
613: UHF_C_PORT_CONFIG_ERROR);
1.87 drochner 614:
615: /* XXX handle overcurrent and resume events! */
616:
1.126.2.13 skrll 617: if (!reconnect && !(change & UPS_C_CONNECT_STATUS)) {
1.116 jakllsch 618: /* No status change, just do recursive explore. */
1.126.2.2 skrll 619: if (up->up_dev != NULL && up->up_dev->ud_hub != NULL)
620: up->up_dev->ud_hub->uh_explore(up->up_dev);
1.1 augustss 621: continue;
1.116 jakllsch 622: }
1.42 augustss 623:
624: /* We have a connect status change, handle it. */
625:
1.126.2.17 skrll 626: DPRINTF("uhub%d status change port %d", device_unit(sc->sc_dev),
627: port, 0, 0);
1.1 augustss 628: usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION);
629: /*
630: * If there is already a device on the port the change status
631: * must mean that is has disconnected. Looking at the
632: * current connect status is not enough to figure this out
633: * since a new unit may have been connected before we handle
634: * the disconnect.
635: */
1.11 augustss 636: disco:
1.126.2.2 skrll 637: if (up->up_dev != NULL) {
1.1 augustss 638: /* Disconnected */
1.126.2.17 skrll 639: DPRINTF("uhub%d device addr=%d disappeared on port %d",
1.126.2.9 skrll 640: device_unit(sc->sc_dev), up->up_dev->ud_addr, port,
641: 0);
642:
1.108 dyoung 643: usb_disconnect_port(up, sc->sc_dev, DETACH_FORCE);
1.58 augustss 644: usbd_clear_port_feature(dev, port,
1.1 augustss 645: UHF_C_PORT_CONNECTION);
1.126.2.12 skrll 646: continue;
1.1 augustss 647: }
1.35 augustss 648: if (!(status & UPS_CURRENT_CONNECT_STATUS)) {
1.42 augustss 649: /* Nothing connected, just ignore it. */
1.126.2.17 skrll 650: DPRINTFN(3, "uhub%d port %d !CURRENT_CONNECT_STATUS",
1.126.2.9 skrll 651: device_unit(sc->sc_dev), port, 0, 0);
1.126.2.12 skrll 652: usb_disconnect_port(up, sc->sc_dev, DETACH_FORCE);
653: usbd_clear_port_feature(dev, port,
654: UHF_C_PORT_CONNECTION);
1.1 augustss 655: continue;
1.35 augustss 656: }
1.1 augustss 657:
658: /* Connected */
1.126.2.9 skrll 659: DPRINTF("unit %d dev->speed=%u dev->depth=%u",
660: device_unit(sc->sc_dev), dev->ud_speed, dev->ud_depth, 0);
1.42 augustss 661:
1.1 augustss 662: /* Wait for maximum device power up time. */
1.13 augustss 663: usbd_delay_ms(dev, USB_PORT_POWERUP_DELAY);
1.11 augustss 664:
1.1 augustss 665: /* Reset port, which implies enabling it. */
1.126.2.2 skrll 666: if (usbd_reset_port(dev, port, &up->up_status)) {
1.98 cube 667: aprint_error_dev(sc->sc_dev,
668: "port %d reset failed\n", port);
1.54 augustss 669: continue;
670: }
671: /* Get port status again, it might have changed during reset */
1.126.2.2 skrll 672: err = usbd_get_port_status(dev, port, &up->up_status);
1.54 augustss 673: if (err) {
1.126.2.17 skrll 674: DPRINTF("uhub%d port %d get port status failed, "
1.126.2.9 skrll 675: "err %d", device_unit(sc->sc_dev), port, err, 0);
1.54 augustss 676: continue;
677: }
1.126.2.2 skrll 678: status = UGETW(up->up_status.wPortStatus);
679: change = UGETW(up->up_status.wPortChange);
1.126.2.11 skrll 680: DPRINTF("hub %d port %d after reset: s/c=%x/%x",
681: device_unit(sc->sc_dev), port, status, change);
682:
1.54 augustss 683: if (!(status & UPS_CURRENT_CONNECT_STATUS)) {
684: /* Nothing connected, just ignore it. */
685: #ifdef DIAGNOSTIC
1.98 cube 686: aprint_debug_dev(sc->sc_dev,
687: "port %d, device disappeared after reset\n", port);
1.54 augustss 688: #endif
1.1 augustss 689: continue;
1.35 augustss 690: }
1.110 kiyohara 691: if (!(status & UPS_PORT_ENABLED)) {
692: /* Not allowed send/receive packet. */
693: #ifdef DIAGNOSTIC
1.115 sborrill 694: printf("%s: port %d, device not enabled\n",
1.112 dyoung 695: device_xname(sc->sc_dev), port);
1.110 kiyohara 696: #endif
697: continue;
698: }
1.126.2.12 skrll 699: /* port reset may cause Warm Reset Change, drop it. */
700: if (change & UPS_C_BH_PORT_RESET)
701: usbd_clear_port_feature(dev, port,
702: UHF_C_BH_PORT_RESET);
1.1 augustss 703:
1.126.2.16 skrll 704: /*
705: * Figure out device speed from power bit of port status.
706: * USB 2.0 ch 11.24.2.7.1
707: * USB 3.1 ch 10.16.2.6.1
708: */
709: int sts = status;
710: if ((sts & UPS_PORT_POWER) == 0)
711: sts &= ~UPS_PORT_POWER_SS;
712:
713: if (sts & UPS_HIGH_SPEED)
1.56 augustss 714: speed = USB_SPEED_HIGH;
1.126.2.16 skrll 715: else if (sts & UPS_LOW_SPEED)
1.56 augustss 716: speed = USB_SPEED_LOW;
1.126.2.16 skrll 717: else {
718: /*
719: * If there is no power bit set, it is certainly
720: * a Super Speed device, so use the speed of its
721: * parent hub.
722: */
723: if (sts & UPS_PORT_POWER)
724: speed = USB_SPEED_FULL;
725: else
726: speed = dev->ud_speed;
727: }
728:
729: /*
730: * Reduce the speed, otherwise we won't setup the proper
731: * transfer methods.
732: */
733: if (speed > dev->ud_speed)
734: speed = dev->ud_speed;
1.126.2.9 skrll 735:
1.126.2.17 skrll 736: DPRINTF("uhub%d speed %u", device_unit(sc->sc_dev), speed, 0,
1.126.2.9 skrll 737: 0);
738:
1.126.2.16 skrll 739: /*
740: * To check whether port has power,
741: * check UPS_PORT_POWER_SS bit if port speed is SS, and
742: * check UPS_PORT_POWER bit if port speed is HS/FS/LS.
743: */
744: if (USB_IS_SS(speed)) {
745: /* SS hub port */
746: if (!(status & UPS_PORT_POWER_SS))
747: aprint_normal_dev(sc->sc_dev,
748: "strange, connected port %d has no power\n",
749: port);
750: } else {
751: /* HS/FS/LS hub port */
752: if (!(status & UPS_PORT_POWER))
753: aprint_normal_dev(sc->sc_dev,
754: "strange, connected port %d has no power\n",
755: port);
756: }
757:
1.1 augustss 758: /* Get device info and set its address. */
1.126.2.2 skrll 759: err = usbd_new_device(sc->sc_dev, dev->ud_bus,
760: dev->ud_depth + 1, speed, port, up);
1.1 augustss 761: /* XXX retry a few times? */
1.33 augustss 762: if (err) {
1.126.2.9 skrll 763: DPRINTF("usbd_new_device failed, error %d", err, 0, 0,
764: 0);
1.1 augustss 765: /* Avoid addressing problems by disabling. */
766: /* usbd_reset_port(dev, port, &up->status); */
1.30 augustss 767:
1.58 augustss 768: /*
1.30 augustss 769: * The unit refused to accept a new address, or had
770: * some other serious problem. Since we cannot leave
771: * at 0 we have to disable the port instead.
772: */
1.98 cube 773: aprint_error_dev(sc->sc_dev,
774: "device problem, disabling port %d\n", port);
1.30 augustss 775: usbd_clear_port_feature(dev, port, UHF_PORT_ENABLE);
1.1 augustss 776: } else {
1.47 augustss 777: /* The port set up succeeded, reset error count. */
1.126.2.2 skrll 778: up->up_restartcnt = 0;
1.47 augustss 779:
1.126.2.2 skrll 780: if (up->up_dev->ud_hub)
781: up->up_dev->ud_hub->uh_explore(up->up_dev);
1.1 augustss 782: }
783: }
1.126.2.18 skrll 784: mutex_enter(&sc->sc_lock);
1.87 drochner 785: sc->sc_explorepending = 0;
1.126.2.12 skrll 786: for (int i = 0; i < sc->sc_statuslen; i++) {
787: if (sc->sc_statuspend[i] != 0) {
788: memcpy(sc->sc_status, sc->sc_statuspend,
789: sc->sc_statuslen);
790: memset(sc->sc_statuspend, 0, sc->sc_statuslen);
791: usb_needs_explore(sc->sc_hub);
792: break;
793: }
794: }
1.126.2.18 skrll 795: mutex_exit(&sc->sc_lock);
796:
1.126.2.4 skrll 797: return USBD_NORMAL_COMPLETION;
1.1 augustss 798: }
799:
1.18 augustss 800: /*
801: * Called from process context when the hub is gone.
802: * Detach all devices on active ports.
803: */
1.105 dyoung 804: int
805: uhub_detach(device_t self, int flags)
1.18 augustss 806: {
1.107 dyoung 807: struct uhub_softc *sc = device_private(self);
1.126.2.2 skrll 808: struct usbd_hub *hub = sc->sc_hub->ud_hub;
1.18 augustss 809: struct usbd_port *rup;
1.108 dyoung 810: int nports, port, rc;
1.18 augustss 811:
1.126.2.9 skrll 812: UHUBHIST_FUNC(); UHUBHIST_CALLED();
813:
1.126.2.17 skrll 814: DPRINTF("uhub%d flags=%d", device_unit(self), flags, 0, 0);
1.18 augustss 815:
1.39 augustss 816: if (hub == NULL) /* Must be partially working */
1.126.2.4 skrll 817: return 0;
1.18 augustss 818:
1.117 mrg 819: /* XXXSMP usb */
820: KERNEL_LOCK(1, curlwp);
821:
1.126.2.2 skrll 822: nports = hub->uh_hubdesc.bNbrPorts;
1.32 augustss 823: for(port = 0; port < nports; port++) {
1.126.2.2 skrll 824: rup = &hub->uh_ports[port];
825: if (rup->up_dev == NULL)
1.108 dyoung 826: continue;
1.117 mrg 827: if ((rc = usb_disconnect_port(rup, self, flags)) != 0) {
828: /* XXXSMP usb */
829: KERNEL_UNLOCK_ONE(curlwp);
830:
1.108 dyoung 831: return rc;
1.117 mrg 832: }
1.18 augustss 833: }
1.58 augustss 834:
1.108 dyoung 835: pmf_device_deregister(self);
836: usbd_abort_pipe(sc->sc_ipipe);
837: usbd_close_pipe(sc->sc_ipipe);
838:
1.105 dyoung 839: usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_hub, sc->sc_dev);
1.37 augustss 840:
1.83 drochner 841: #if 0
1.70 augustss 842: if (hub->ports[0].tt)
1.126.2.3 skrll 843: kmem_free(hub->ports[0].tt,
844: (UHUB_IS_SINGLE_TT(sc) ? 1 : nports) *
1.126.2.15 skrll 845: sizeof(struct usbd_tt));
1.83 drochner 846: #endif
1.126.2.3 skrll 847: kmem_free(hub,
848: sizeof(*hub) + (nports-1) * sizeof(struct usbd_port));
1.126.2.2 skrll 849: sc->sc_hub->ud_hub = NULL;
1.84 drochner 850: if (sc->sc_status)
1.126.2.6 skrll 851: kmem_free(sc->sc_status, sc->sc_statuslen);
1.126.2.12 skrll 852: if (sc->sc_statuspend)
853: kmem_free(sc->sc_statuspend, sc->sc_statuslen);
1.101 drochner 854: if (sc->sc_statusbuf)
1.126.2.3 skrll 855: kmem_free(sc->sc_statusbuf, sc->sc_statuslen);
1.18 augustss 856:
1.117 mrg 857: /* XXXSMP usb */
858: KERNEL_UNLOCK_ONE(curlwp);
859:
1.126.2.4 skrll 860: return 0;
1.18 augustss 861: }
1.34 augustss 862:
1.103 kent 863: int
864: uhub_rescan(device_t self, const char *ifattr, const int *locators)
865: {
866: struct uhub_softc *sc = device_private(self);
1.126.2.2 skrll 867: struct usbd_hub *hub = sc->sc_hub->ud_hub;
1.126.2.7 skrll 868: struct usbd_device *dev;
1.124 martin 869: int port;
1.103 kent 870:
1.126.2.2 skrll 871: for (port = 0; port < hub->uh_hubdesc.bNbrPorts; port++) {
872: dev = hub->uh_ports[port].up_dev;
1.103 kent 873: if (dev == NULL)
874: continue;
1.124 martin 875: usbd_reattach_device(sc->sc_dev, dev, port, locators);
1.103 kent 876: }
877: return 0;
878: }
879:
1.34 augustss 880: /* Called when a device has been detached from it */
1.95 dyoung 881: void
882: uhub_childdet(device_t self, device_t child)
1.34 augustss 883: {
1.95 dyoung 884: struct uhub_softc *sc = device_private(self);
1.126.2.7 skrll 885: struct usbd_device *devhub = sc->sc_hub;
886: struct usbd_device *dev;
1.95 dyoung 887: int nports;
888: int port;
889: int i;
890:
1.126.2.2 skrll 891: if (!devhub->ud_hub)
1.95 dyoung 892: /* should never happen; children are only created after init */
893: panic("hub not fully initialised, but child deleted?");
894:
1.126.2.2 skrll 895: nports = devhub->ud_hub->uh_hubdesc.bNbrPorts;
1.95 dyoung 896: for (port = 0; port < nports; port++) {
1.126.2.2 skrll 897: dev = devhub->ud_hub->uh_ports[port].up_dev;
898: if (!dev || dev->ud_subdevlen == 0)
1.95 dyoung 899: continue;
1.126.2.2 skrll 900: for (i = 0; i < dev->ud_subdevlen; i++) {
901: if (dev->ud_subdevs[i] == child) {
902: dev->ud_subdevs[i] = NULL;
903: dev->ud_nifaces_claimed--;
1.95 dyoung 904: }
905: }
1.126.2.2 skrll 906: if (dev->ud_nifaces_claimed == 0) {
1.126.2.3 skrll 907: kmem_free(dev->ud_subdevs,
908: dev->ud_subdevlen * sizeof(device_t));
1.126.2.2 skrll 909: dev->ud_subdevs = NULL;
910: dev->ud_subdevlen = 0;
1.113 jmcneill 911: }
1.95 dyoung 912: }
1.34 augustss 913: }
914:
1.18 augustss 915:
916: /*
917: * Hub interrupt.
918: * This an indication that some port has changed status.
919: * Notify the bus event handler thread that we need
920: * to be explored again.
921: */
1.1 augustss 922: void
1.126.2.7 skrll 923: uhub_intr(struct usbd_xfer *xfer, void *addr, usbd_status status)
1.1 augustss 924: {
925: struct uhub_softc *sc = addr;
926:
1.126.2.9 skrll 927: UHUBHIST_FUNC(); UHUBHIST_CALLED();
928:
1.126.2.17 skrll 929: DPRINTFN(5, "uhub%d", device_unit(sc->sc_dev), 0, 0, 0);
1.87 drochner 930:
1.50 augustss 931: if (status == USBD_STALLED)
1.9 augustss 932: usbd_clear_endpoint_stall_async(sc->sc_ipipe);
1.126.2.12 skrll 933: else if (status == USBD_NORMAL_COMPLETION) {
1.126.2.18 skrll 934:
935: mutex_enter(&sc->sc_lock);
936:
937: DPRINTFN(5, "uhub%d: explore pending %d",
938: device_unit(sc->sc_dev), sc->sc_explorepending, 0, 0);
1.126.2.12 skrll 939:
940: /* merge port bitmap into pending interrupts list */
1.126.2.19! skrll 941: for (size_t i = 0; i < sc->sc_statuslen; i++) {
1.126.2.12 skrll 942: sc->sc_statuspend[i] |= sc->sc_statusbuf[i];
943:
1.126.2.18 skrll 944: DPRINTFN(5, "uhub%d: pending/new ports "
945: "[%d] %#x/%#x", device_unit(sc->sc_dev),
946: i, sc->sc_statuspend[i], sc->sc_statusbuf[i]);
947: }
948:
1.126.2.12 skrll 949: if (!sc->sc_explorepending) {
950: sc->sc_explorepending = 1;
1.126.2.18 skrll 951:
1.126.2.12 skrll 952: memcpy(sc->sc_status, sc->sc_statuspend,
953: sc->sc_statuslen);
954: memset(sc->sc_statuspend, 0, sc->sc_statuslen);
1.126.2.18 skrll 955:
956: for (size_t i = 0; i < sc->sc_statuslen; i++) {
957: DPRINTFN(5, "uhub%d: exploring ports "
958: "[%d] %#x", device_unit(sc->sc_dev),
959: i, sc->sc_status[i], 0);
960: }
961:
1.126.2.12 skrll 962: usb_needs_explore(sc->sc_hub);
963: }
1.126.2.18 skrll 964: mutex_exit(&sc->sc_lock);
1.87 drochner 965: }
1.1 augustss 966: }
CVSweb <webmaster@jp.NetBSD.org>