1 | #include <machine/rtems-bsd-kernel-space.h> |
---|
2 | |
---|
3 | /* $FreeBSD$ */ |
---|
4 | /*- |
---|
5 | * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. |
---|
6 | * Copyright (c) 1998 Lennart Augustsson. All rights reserved. |
---|
7 | * Copyright (c) 2008-2010 Hans Petter Selasky. All rights reserved. |
---|
8 | * |
---|
9 | * Redistribution and use in source and binary forms, with or without |
---|
10 | * modification, are permitted provided that the following conditions |
---|
11 | * are met: |
---|
12 | * 1. Redistributions of source code must retain the above copyright |
---|
13 | * notice, this list of conditions and the following disclaimer. |
---|
14 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
15 | * notice, this list of conditions and the following disclaimer in the |
---|
16 | * documentation and/or other materials provided with the distribution. |
---|
17 | * |
---|
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
---|
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
---|
22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
28 | * SUCH DAMAGE. |
---|
29 | */ |
---|
30 | |
---|
31 | /* |
---|
32 | * USB spec: http://www.usb.org/developers/docs/usbspec.zip |
---|
33 | */ |
---|
34 | |
---|
35 | #include <sys/stdint.h> |
---|
36 | #include <sys/stddef.h> |
---|
37 | #include <rtems/bsd/sys/param.h> |
---|
38 | #include <sys/queue.h> |
---|
39 | #include <rtems/bsd/sys/types.h> |
---|
40 | #include <sys/systm.h> |
---|
41 | #include <sys/kernel.h> |
---|
42 | #include <sys/bus.h> |
---|
43 | #include <sys/module.h> |
---|
44 | #include <rtems/bsd/sys/lock.h> |
---|
45 | #include <sys/mutex.h> |
---|
46 | #include <sys/condvar.h> |
---|
47 | #include <sys/sysctl.h> |
---|
48 | #include <sys/sx.h> |
---|
49 | #include <rtems/bsd/sys/unistd.h> |
---|
50 | #include <sys/callout.h> |
---|
51 | #include <sys/malloc.h> |
---|
52 | #include <sys/priv.h> |
---|
53 | |
---|
54 | #include <dev/usb/usb.h> |
---|
55 | #include <dev/usb/usbdi.h> |
---|
56 | #include <dev/usb/usbdi_util.h> |
---|
57 | |
---|
58 | #define USB_DEBUG_VAR uhub_debug |
---|
59 | |
---|
60 | #include <dev/usb/usb_core.h> |
---|
61 | #include <dev/usb/usb_process.h> |
---|
62 | #include <dev/usb/usb_device.h> |
---|
63 | #include <dev/usb/usb_request.h> |
---|
64 | #include <dev/usb/usb_debug.h> |
---|
65 | #include <dev/usb/usb_hub.h> |
---|
66 | #include <dev/usb/usb_util.h> |
---|
67 | #include <dev/usb/usb_busdma.h> |
---|
68 | #include <dev/usb/usb_transfer.h> |
---|
69 | #include <dev/usb/usb_dynamic.h> |
---|
70 | |
---|
71 | #include <dev/usb/usb_controller.h> |
---|
72 | #include <dev/usb/usb_bus.h> |
---|
73 | |
---|
74 | #define UHUB_INTR_INTERVAL 250 /* ms */ |
---|
75 | enum { |
---|
76 | UHUB_INTR_TRANSFER, |
---|
77 | #if USB_HAVE_TT_SUPPORT |
---|
78 | UHUB_RESET_TT_TRANSFER, |
---|
79 | #endif |
---|
80 | UHUB_N_TRANSFER, |
---|
81 | }; |
---|
82 | |
---|
83 | #ifdef USB_DEBUG |
---|
84 | static int uhub_debug = 0; |
---|
85 | |
---|
86 | static SYSCTL_NODE(_hw_usb, OID_AUTO, uhub, CTLFLAG_RW, 0, "USB HUB"); |
---|
87 | SYSCTL_INT(_hw_usb_uhub, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &uhub_debug, 0, |
---|
88 | "Debug level"); |
---|
89 | TUNABLE_INT("hw.usb.uhub.debug", &uhub_debug); |
---|
90 | #endif |
---|
91 | |
---|
92 | #if USB_HAVE_POWERD |
---|
93 | static int usb_power_timeout = 30; /* seconds */ |
---|
94 | |
---|
95 | SYSCTL_INT(_hw_usb, OID_AUTO, power_timeout, CTLFLAG_RW, |
---|
96 | &usb_power_timeout, 0, "USB power timeout"); |
---|
97 | #endif |
---|
98 | |
---|
99 | struct uhub_current_state { |
---|
100 | uint16_t port_change; |
---|
101 | uint16_t port_status; |
---|
102 | }; |
---|
103 | |
---|
104 | struct uhub_softc { |
---|
105 | struct uhub_current_state sc_st;/* current state */ |
---|
106 | device_t sc_dev; /* base device */ |
---|
107 | struct mtx sc_mtx; /* our mutex */ |
---|
108 | struct usb_device *sc_udev; /* USB device */ |
---|
109 | struct usb_xfer *sc_xfer[UHUB_N_TRANSFER]; /* interrupt xfer */ |
---|
110 | uint8_t sc_flags; |
---|
111 | #define UHUB_FLAG_DID_EXPLORE 0x01 |
---|
112 | char sc_name[32]; |
---|
113 | }; |
---|
114 | |
---|
115 | #define UHUB_PROTO(sc) ((sc)->sc_udev->ddesc.bDeviceProtocol) |
---|
116 | #define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB) |
---|
117 | #define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT) |
---|
118 | #define UHUB_IS_MULTI_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBMTT) |
---|
119 | #define UHUB_IS_SUPER_SPEED(sc) (UHUB_PROTO(sc) == UDPROTO_SSHUB) |
---|
120 | |
---|
121 | /* prototypes for type checking: */ |
---|
122 | |
---|
123 | static device_probe_t uhub_probe; |
---|
124 | static device_attach_t uhub_attach; |
---|
125 | static device_detach_t uhub_detach; |
---|
126 | static device_suspend_t uhub_suspend; |
---|
127 | static device_resume_t uhub_resume; |
---|
128 | |
---|
129 | static bus_driver_added_t uhub_driver_added; |
---|
130 | static bus_child_location_str_t uhub_child_location_string; |
---|
131 | static bus_child_pnpinfo_str_t uhub_child_pnpinfo_string; |
---|
132 | |
---|
133 | static usb_callback_t uhub_intr_callback; |
---|
134 | #if USB_HAVE_TT_SUPPORT |
---|
135 | static usb_callback_t uhub_reset_tt_callback; |
---|
136 | #endif |
---|
137 | |
---|
138 | static void usb_dev_resume_peer(struct usb_device *udev); |
---|
139 | static void usb_dev_suspend_peer(struct usb_device *udev); |
---|
140 | static uint8_t usb_peer_should_wakeup(struct usb_device *udev); |
---|
141 | |
---|
142 | static const struct usb_config uhub_config[UHUB_N_TRANSFER] = { |
---|
143 | |
---|
144 | [UHUB_INTR_TRANSFER] = { |
---|
145 | .type = UE_INTERRUPT, |
---|
146 | .endpoint = UE_ADDR_ANY, |
---|
147 | .direction = UE_DIR_ANY, |
---|
148 | .timeout = 0, |
---|
149 | .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, |
---|
150 | .bufsize = 0, /* use wMaxPacketSize */ |
---|
151 | .callback = &uhub_intr_callback, |
---|
152 | .interval = UHUB_INTR_INTERVAL, |
---|
153 | }, |
---|
154 | #if USB_HAVE_TT_SUPPORT |
---|
155 | [UHUB_RESET_TT_TRANSFER] = { |
---|
156 | .type = UE_CONTROL, |
---|
157 | .endpoint = 0x00, /* Control pipe */ |
---|
158 | .direction = UE_DIR_ANY, |
---|
159 | .bufsize = sizeof(struct usb_device_request), |
---|
160 | .callback = &uhub_reset_tt_callback, |
---|
161 | .timeout = 1000, /* 1 second */ |
---|
162 | .usb_mode = USB_MODE_HOST, |
---|
163 | }, |
---|
164 | #endif |
---|
165 | }; |
---|
166 | |
---|
167 | /* |
---|
168 | * driver instance for "hub" connected to "usb" |
---|
169 | * and "hub" connected to "hub" |
---|
170 | */ |
---|
171 | static devclass_t uhub_devclass; |
---|
172 | |
---|
173 | static device_method_t uhub_methods[] = { |
---|
174 | DEVMETHOD(device_probe, uhub_probe), |
---|
175 | DEVMETHOD(device_attach, uhub_attach), |
---|
176 | DEVMETHOD(device_detach, uhub_detach), |
---|
177 | |
---|
178 | DEVMETHOD(device_suspend, uhub_suspend), |
---|
179 | DEVMETHOD(device_resume, uhub_resume), |
---|
180 | |
---|
181 | DEVMETHOD(bus_child_location_str, uhub_child_location_string), |
---|
182 | DEVMETHOD(bus_child_pnpinfo_str, uhub_child_pnpinfo_string), |
---|
183 | DEVMETHOD(bus_driver_added, uhub_driver_added), |
---|
184 | {0, 0} |
---|
185 | }; |
---|
186 | |
---|
187 | static driver_t uhub_driver = { |
---|
188 | .name = "uhub", |
---|
189 | .methods = uhub_methods, |
---|
190 | .size = sizeof(struct uhub_softc) |
---|
191 | }; |
---|
192 | |
---|
193 | DRIVER_MODULE(uhub, usbus, uhub_driver, uhub_devclass, 0, 0); |
---|
194 | DRIVER_MODULE(uhub, uhub, uhub_driver, uhub_devclass, NULL, 0); |
---|
195 | MODULE_VERSION(uhub, 1); |
---|
196 | |
---|
197 | static void |
---|
198 | uhub_intr_callback(struct usb_xfer *xfer, usb_error_t error) |
---|
199 | { |
---|
200 | struct uhub_softc *sc = usbd_xfer_softc(xfer); |
---|
201 | |
---|
202 | switch (USB_GET_STATE(xfer)) { |
---|
203 | case USB_ST_TRANSFERRED: |
---|
204 | DPRINTFN(2, "\n"); |
---|
205 | /* |
---|
206 | * This is an indication that some port |
---|
207 | * has changed status. Notify the bus |
---|
208 | * event handler thread that we need |
---|
209 | * to be explored again: |
---|
210 | */ |
---|
211 | usb_needs_explore(sc->sc_udev->bus, 0); |
---|
212 | |
---|
213 | case USB_ST_SETUP: |
---|
214 | usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); |
---|
215 | usbd_transfer_submit(xfer); |
---|
216 | break; |
---|
217 | |
---|
218 | default: /* Error */ |
---|
219 | if (xfer->error != USB_ERR_CANCELLED) { |
---|
220 | /* |
---|
221 | * Do a clear-stall. The "stall_pipe" flag |
---|
222 | * will get cleared before next callback by |
---|
223 | * the USB stack. |
---|
224 | */ |
---|
225 | usbd_xfer_set_stall(xfer); |
---|
226 | usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); |
---|
227 | usbd_transfer_submit(xfer); |
---|
228 | } |
---|
229 | break; |
---|
230 | } |
---|
231 | } |
---|
232 | |
---|
233 | /*------------------------------------------------------------------------* |
---|
234 | * uhub_reset_tt_proc |
---|
235 | * |
---|
236 | * This function starts the TT reset USB request |
---|
237 | *------------------------------------------------------------------------*/ |
---|
238 | #if USB_HAVE_TT_SUPPORT |
---|
239 | static void |
---|
240 | uhub_reset_tt_proc(struct usb_proc_msg *_pm) |
---|
241 | { |
---|
242 | struct usb_udev_msg *pm = (void *)_pm; |
---|
243 | struct usb_device *udev = pm->udev; |
---|
244 | struct usb_hub *hub; |
---|
245 | struct uhub_softc *sc; |
---|
246 | |
---|
247 | hub = udev->hub; |
---|
248 | if (hub == NULL) |
---|
249 | return; |
---|
250 | sc = hub->hubsoftc; |
---|
251 | if (sc == NULL) |
---|
252 | return; |
---|
253 | |
---|
254 | /* Change lock */ |
---|
255 | USB_BUS_UNLOCK(udev->bus); |
---|
256 | mtx_lock(&sc->sc_mtx); |
---|
257 | /* Start transfer */ |
---|
258 | usbd_transfer_start(sc->sc_xfer[UHUB_RESET_TT_TRANSFER]); |
---|
259 | /* Change lock */ |
---|
260 | mtx_unlock(&sc->sc_mtx); |
---|
261 | USB_BUS_LOCK(udev->bus); |
---|
262 | } |
---|
263 | #endif |
---|
264 | |
---|
265 | /*------------------------------------------------------------------------* |
---|
266 | * uhub_tt_buffer_reset_async_locked |
---|
267 | * |
---|
268 | * This function queues a TT reset for the given USB device and endpoint. |
---|
269 | *------------------------------------------------------------------------*/ |
---|
270 | #if USB_HAVE_TT_SUPPORT |
---|
271 | void |
---|
272 | uhub_tt_buffer_reset_async_locked(struct usb_device *child, struct usb_endpoint *ep) |
---|
273 | { |
---|
274 | struct usb_device_request req; |
---|
275 | struct usb_device *udev; |
---|
276 | struct usb_hub *hub; |
---|
277 | struct usb_port *up; |
---|
278 | uint16_t wValue; |
---|
279 | uint8_t port; |
---|
280 | |
---|
281 | if (child == NULL || ep == NULL) |
---|
282 | return; |
---|
283 | |
---|
284 | udev = child->parent_hs_hub; |
---|
285 | port = child->hs_port_no; |
---|
286 | |
---|
287 | if (udev == NULL) |
---|
288 | return; |
---|
289 | |
---|
290 | hub = udev->hub; |
---|
291 | if ((hub == NULL) || |
---|
292 | (udev->speed != USB_SPEED_HIGH) || |
---|
293 | (child->speed != USB_SPEED_LOW && |
---|
294 | child->speed != USB_SPEED_FULL) || |
---|
295 | (child->flags.usb_mode != USB_MODE_HOST) || |
---|
296 | (port == 0) || (ep->edesc == NULL)) { |
---|
297 | /* not applicable */ |
---|
298 | return; |
---|
299 | } |
---|
300 | |
---|
301 | USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); |
---|
302 | |
---|
303 | up = hub->ports + port - 1; |
---|
304 | |
---|
305 | if (udev->ddesc.bDeviceClass == UDCLASS_HUB && |
---|
306 | udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) |
---|
307 | port = 1; |
---|
308 | |
---|
309 | /* if we already received a clear buffer request, reset the whole TT */ |
---|
310 | if (up->req_reset_tt.bRequest != 0) { |
---|
311 | req.bmRequestType = UT_WRITE_CLASS_OTHER; |
---|
312 | req.bRequest = UR_RESET_TT; |
---|
313 | USETW(req.wValue, 0); |
---|
314 | req.wIndex[0] = port; |
---|
315 | req.wIndex[1] = 0; |
---|
316 | USETW(req.wLength, 0); |
---|
317 | } else { |
---|
318 | wValue = (ep->edesc->bEndpointAddress & 0xF) | |
---|
319 | ((child->address & 0x7F) << 4) | |
---|
320 | ((ep->edesc->bEndpointAddress & 0x80) << 8) | |
---|
321 | ((ep->edesc->bmAttributes & 3) << 12); |
---|
322 | |
---|
323 | req.bmRequestType = UT_WRITE_CLASS_OTHER; |
---|
324 | req.bRequest = UR_CLEAR_TT_BUFFER; |
---|
325 | USETW(req.wValue, wValue); |
---|
326 | req.wIndex[0] = port; |
---|
327 | req.wIndex[1] = 0; |
---|
328 | USETW(req.wLength, 0); |
---|
329 | } |
---|
330 | up->req_reset_tt = req; |
---|
331 | /* get reset transfer started */ |
---|
332 | usb_proc_msignal(&udev->bus->non_giant_callback_proc, |
---|
333 | &hub->tt_msg[0], &hub->tt_msg[1]); |
---|
334 | } |
---|
335 | #endif |
---|
336 | |
---|
337 | #if USB_HAVE_TT_SUPPORT |
---|
338 | static void |
---|
339 | uhub_reset_tt_callback(struct usb_xfer *xfer, usb_error_t error) |
---|
340 | { |
---|
341 | struct uhub_softc *sc; |
---|
342 | struct usb_device *udev; |
---|
343 | struct usb_port *up; |
---|
344 | uint8_t x; |
---|
345 | |
---|
346 | DPRINTF("TT buffer reset\n"); |
---|
347 | |
---|
348 | sc = usbd_xfer_softc(xfer); |
---|
349 | udev = sc->sc_udev; |
---|
350 | |
---|
351 | switch (USB_GET_STATE(xfer)) { |
---|
352 | case USB_ST_TRANSFERRED: |
---|
353 | case USB_ST_SETUP: |
---|
354 | tr_setup: |
---|
355 | USB_BUS_LOCK(udev->bus); |
---|
356 | /* find first port which needs a TT reset */ |
---|
357 | for (x = 0; x != udev->hub->nports; x++) { |
---|
358 | up = udev->hub->ports + x; |
---|
359 | |
---|
360 | if (up->req_reset_tt.bRequest == 0) |
---|
361 | continue; |
---|
362 | |
---|
363 | /* copy in the transfer */ |
---|
364 | usbd_copy_in(xfer->frbuffers, 0, &up->req_reset_tt, |
---|
365 | sizeof(up->req_reset_tt)); |
---|
366 | /* reset buffer */ |
---|
367 | memset(&up->req_reset_tt, 0, sizeof(up->req_reset_tt)); |
---|
368 | |
---|
369 | /* set length */ |
---|
370 | usbd_xfer_set_frame_len(xfer, 0, sizeof(up->req_reset_tt)); |
---|
371 | xfer->nframes = 1; |
---|
372 | USB_BUS_UNLOCK(udev->bus); |
---|
373 | |
---|
374 | usbd_transfer_submit(xfer); |
---|
375 | return; |
---|
376 | } |
---|
377 | USB_BUS_UNLOCK(udev->bus); |
---|
378 | break; |
---|
379 | |
---|
380 | default: |
---|
381 | if (error == USB_ERR_CANCELLED) |
---|
382 | break; |
---|
383 | |
---|
384 | DPRINTF("TT buffer reset failed (%s)\n", usbd_errstr(error)); |
---|
385 | goto tr_setup; |
---|
386 | } |
---|
387 | } |
---|
388 | #endif |
---|
389 | |
---|
390 | /*------------------------------------------------------------------------* |
---|
391 | * uhub_count_active_host_ports |
---|
392 | * |
---|
393 | * This function counts the number of active ports at the given speed. |
---|
394 | *------------------------------------------------------------------------*/ |
---|
395 | uint8_t |
---|
396 | uhub_count_active_host_ports(struct usb_device *udev, enum usb_dev_speed speed) |
---|
397 | { |
---|
398 | struct uhub_softc *sc; |
---|
399 | struct usb_device *child; |
---|
400 | struct usb_hub *hub; |
---|
401 | struct usb_port *up; |
---|
402 | uint8_t retval = 0; |
---|
403 | uint8_t x; |
---|
404 | |
---|
405 | if (udev == NULL) |
---|
406 | goto done; |
---|
407 | hub = udev->hub; |
---|
408 | if (hub == NULL) |
---|
409 | goto done; |
---|
410 | sc = hub->hubsoftc; |
---|
411 | if (sc == NULL) |
---|
412 | goto done; |
---|
413 | |
---|
414 | for (x = 0; x != hub->nports; x++) { |
---|
415 | up = hub->ports + x; |
---|
416 | child = usb_bus_port_get_device(udev->bus, up); |
---|
417 | if (child != NULL && |
---|
418 | child->flags.usb_mode == USB_MODE_HOST && |
---|
419 | child->speed == speed) |
---|
420 | retval++; |
---|
421 | } |
---|
422 | done: |
---|
423 | return (retval); |
---|
424 | } |
---|
425 | |
---|
426 | void |
---|
427 | uhub_explore_handle_re_enumerate(struct usb_device *child) |
---|
428 | { |
---|
429 | uint8_t do_unlock; |
---|
430 | usb_error_t err; |
---|
431 | |
---|
432 | /* check if device should be re-enumerated */ |
---|
433 | if (child->flags.usb_mode != USB_MODE_HOST) |
---|
434 | return; |
---|
435 | |
---|
436 | do_unlock = usbd_enum_lock(child); |
---|
437 | switch (child->re_enumerate_wait) { |
---|
438 | case USB_RE_ENUM_START: |
---|
439 | err = usbd_set_config_index(child, |
---|
440 | USB_UNCONFIG_INDEX); |
---|
441 | if (err != 0) { |
---|
442 | DPRINTF("Unconfigure failed: %s: Ignored.\n", |
---|
443 | usbd_errstr(err)); |
---|
444 | } |
---|
445 | if (child->parent_hub == NULL) { |
---|
446 | /* the root HUB cannot be re-enumerated */ |
---|
447 | DPRINTFN(6, "cannot reset root HUB\n"); |
---|
448 | err = 0; |
---|
449 | } else { |
---|
450 | err = usbd_req_re_enumerate(child, NULL); |
---|
451 | } |
---|
452 | if (err == 0) |
---|
453 | err = usbd_set_config_index(child, 0); |
---|
454 | if (err == 0) { |
---|
455 | err = usb_probe_and_attach(child, |
---|
456 | USB_IFACE_INDEX_ANY); |
---|
457 | } |
---|
458 | child->re_enumerate_wait = USB_RE_ENUM_DONE; |
---|
459 | break; |
---|
460 | |
---|
461 | case USB_RE_ENUM_PWR_OFF: |
---|
462 | /* get the device unconfigured */ |
---|
463 | err = usbd_set_config_index(child, |
---|
464 | USB_UNCONFIG_INDEX); |
---|
465 | if (err) { |
---|
466 | DPRINTFN(0, "Could not unconfigure " |
---|
467 | "device (ignored)\n"); |
---|
468 | } |
---|
469 | if (child->parent_hub == NULL) { |
---|
470 | /* the root HUB cannot be re-enumerated */ |
---|
471 | DPRINTFN(6, "cannot set port feature\n"); |
---|
472 | err = 0; |
---|
473 | } else { |
---|
474 | /* clear port enable */ |
---|
475 | err = usbd_req_clear_port_feature(child->parent_hub, |
---|
476 | NULL, child->port_no, UHF_PORT_ENABLE); |
---|
477 | if (err) { |
---|
478 | DPRINTFN(0, "Could not disable port " |
---|
479 | "(ignored)\n"); |
---|
480 | } |
---|
481 | } |
---|
482 | child->re_enumerate_wait = USB_RE_ENUM_DONE; |
---|
483 | break; |
---|
484 | |
---|
485 | case USB_RE_ENUM_SET_CONFIG: |
---|
486 | err = usbd_set_config_index(child, |
---|
487 | child->next_config_index); |
---|
488 | if (err != 0) { |
---|
489 | DPRINTF("Configure failed: %s: Ignored.\n", |
---|
490 | usbd_errstr(err)); |
---|
491 | } else { |
---|
492 | err = usb_probe_and_attach(child, |
---|
493 | USB_IFACE_INDEX_ANY); |
---|
494 | } |
---|
495 | child->re_enumerate_wait = USB_RE_ENUM_DONE; |
---|
496 | break; |
---|
497 | |
---|
498 | default: |
---|
499 | child->re_enumerate_wait = USB_RE_ENUM_DONE; |
---|
500 | break; |
---|
501 | } |
---|
502 | if (do_unlock) |
---|
503 | usbd_enum_unlock(child); |
---|
504 | } |
---|
505 | |
---|
506 | /*------------------------------------------------------------------------* |
---|
507 | * uhub_explore_sub - subroutine |
---|
508 | * |
---|
509 | * Return values: |
---|
510 | * 0: Success |
---|
511 | * Else: A control transaction failed |
---|
512 | *------------------------------------------------------------------------*/ |
---|
513 | static usb_error_t |
---|
514 | uhub_explore_sub(struct uhub_softc *sc, struct usb_port *up) |
---|
515 | { |
---|
516 | struct usb_bus *bus; |
---|
517 | struct usb_device *child; |
---|
518 | uint8_t refcount; |
---|
519 | usb_error_t err; |
---|
520 | |
---|
521 | bus = sc->sc_udev->bus; |
---|
522 | err = 0; |
---|
523 | |
---|
524 | /* get driver added refcount from USB bus */ |
---|
525 | refcount = bus->driver_added_refcount; |
---|
526 | |
---|
527 | /* get device assosiated with the given port */ |
---|
528 | child = usb_bus_port_get_device(bus, up); |
---|
529 | if (child == NULL) { |
---|
530 | /* nothing to do */ |
---|
531 | goto done; |
---|
532 | } |
---|
533 | |
---|
534 | uhub_explore_handle_re_enumerate(child); |
---|
535 | |
---|
536 | /* check if probe and attach should be done */ |
---|
537 | |
---|
538 | if (child->driver_added_refcount != refcount) { |
---|
539 | child->driver_added_refcount = refcount; |
---|
540 | err = usb_probe_and_attach(child, |
---|
541 | USB_IFACE_INDEX_ANY); |
---|
542 | if (err) { |
---|
543 | goto done; |
---|
544 | } |
---|
545 | } |
---|
546 | /* start control transfer, if device mode */ |
---|
547 | |
---|
548 | if (child->flags.usb_mode == USB_MODE_DEVICE) |
---|
549 | usbd_ctrl_transfer_setup(child); |
---|
550 | |
---|
551 | /* if a HUB becomes present, do a recursive HUB explore */ |
---|
552 | |
---|
553 | if (child->hub) |
---|
554 | err = (child->hub->explore) (child); |
---|
555 | |
---|
556 | done: |
---|
557 | return (err); |
---|
558 | } |
---|
559 | |
---|
560 | /*------------------------------------------------------------------------* |
---|
561 | * uhub_read_port_status - factored out code |
---|
562 | *------------------------------------------------------------------------*/ |
---|
563 | static usb_error_t |
---|
564 | uhub_read_port_status(struct uhub_softc *sc, uint8_t portno) |
---|
565 | { |
---|
566 | struct usb_port_status ps; |
---|
567 | usb_error_t err; |
---|
568 | |
---|
569 | err = usbd_req_get_port_status( |
---|
570 | sc->sc_udev, NULL, &ps, portno); |
---|
571 | |
---|
572 | /* update status regardless of error */ |
---|
573 | |
---|
574 | sc->sc_st.port_status = UGETW(ps.wPortStatus); |
---|
575 | sc->sc_st.port_change = UGETW(ps.wPortChange); |
---|
576 | |
---|
577 | /* debugging print */ |
---|
578 | |
---|
579 | DPRINTFN(4, "port %d, wPortStatus=0x%04x, " |
---|
580 | "wPortChange=0x%04x, err=%s\n", |
---|
581 | portno, sc->sc_st.port_status, |
---|
582 | sc->sc_st.port_change, usbd_errstr(err)); |
---|
583 | return (err); |
---|
584 | } |
---|
585 | |
---|
586 | /*------------------------------------------------------------------------* |
---|
587 | * uhub_reattach_port |
---|
588 | * |
---|
589 | * Returns: |
---|
590 | * 0: Success |
---|
591 | * Else: A control transaction failed |
---|
592 | *------------------------------------------------------------------------*/ |
---|
593 | static usb_error_t |
---|
594 | uhub_reattach_port(struct uhub_softc *sc, uint8_t portno) |
---|
595 | { |
---|
596 | struct usb_device *child; |
---|
597 | struct usb_device *udev; |
---|
598 | enum usb_dev_speed speed; |
---|
599 | enum usb_hc_mode mode; |
---|
600 | usb_error_t err; |
---|
601 | uint16_t power_mask; |
---|
602 | uint8_t timeout; |
---|
603 | |
---|
604 | DPRINTF("reattaching port %d\n", portno); |
---|
605 | |
---|
606 | timeout = 0; |
---|
607 | udev = sc->sc_udev; |
---|
608 | child = usb_bus_port_get_device(udev->bus, |
---|
609 | udev->hub->ports + portno - 1); |
---|
610 | |
---|
611 | repeat: |
---|
612 | |
---|
613 | /* first clear the port connection change bit */ |
---|
614 | |
---|
615 | err = usbd_req_clear_port_feature(udev, NULL, |
---|
616 | portno, UHF_C_PORT_CONNECTION); |
---|
617 | |
---|
618 | if (err) { |
---|
619 | goto error; |
---|
620 | } |
---|
621 | /* check if there is a child */ |
---|
622 | |
---|
623 | if (child != NULL) { |
---|
624 | /* |
---|
625 | * Free USB device and all subdevices, if any. |
---|
626 | */ |
---|
627 | usb_free_device(child, 0); |
---|
628 | child = NULL; |
---|
629 | } |
---|
630 | /* get fresh status */ |
---|
631 | |
---|
632 | err = uhub_read_port_status(sc, portno); |
---|
633 | if (err) { |
---|
634 | goto error; |
---|
635 | } |
---|
636 | /* check if nothing is connected to the port */ |
---|
637 | |
---|
638 | if (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS)) { |
---|
639 | goto error; |
---|
640 | } |
---|
641 | /* check if there is no power on the port and print a warning */ |
---|
642 | |
---|
643 | switch (udev->speed) { |
---|
644 | case USB_SPEED_HIGH: |
---|
645 | case USB_SPEED_FULL: |
---|
646 | case USB_SPEED_LOW: |
---|
647 | power_mask = UPS_PORT_POWER; |
---|
648 | break; |
---|
649 | case USB_SPEED_SUPER: |
---|
650 | if (udev->parent_hub == NULL) |
---|
651 | power_mask = UPS_PORT_POWER; |
---|
652 | else |
---|
653 | power_mask = UPS_PORT_POWER_SS; |
---|
654 | break; |
---|
655 | default: |
---|
656 | power_mask = 0; |
---|
657 | break; |
---|
658 | } |
---|
659 | if (!(sc->sc_st.port_status & power_mask)) { |
---|
660 | DPRINTF("WARNING: strange, connected port %d " |
---|
661 | "has no power\n", portno); |
---|
662 | } |
---|
663 | |
---|
664 | /* check if the device is in Host Mode */ |
---|
665 | |
---|
666 | if (!(sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)) { |
---|
667 | |
---|
668 | DPRINTF("Port %d is in Host Mode\n", portno); |
---|
669 | |
---|
670 | if (sc->sc_st.port_status & UPS_SUSPEND) { |
---|
671 | /* |
---|
672 | * NOTE: Should not get here in SuperSpeed |
---|
673 | * mode, because the HUB should report this |
---|
674 | * bit as zero. |
---|
675 | */ |
---|
676 | DPRINTF("Port %d was still " |
---|
677 | "suspended, clearing.\n", portno); |
---|
678 | err = usbd_req_clear_port_feature(udev, |
---|
679 | NULL, portno, UHF_PORT_SUSPEND); |
---|
680 | } |
---|
681 | |
---|
682 | /* USB Host Mode */ |
---|
683 | |
---|
684 | /* wait for maximum device power up time */ |
---|
685 | |
---|
686 | usb_pause_mtx(NULL, |
---|
687 | USB_MS_TO_TICKS(usb_port_powerup_delay)); |
---|
688 | |
---|
689 | /* reset port, which implies enabling it */ |
---|
690 | |
---|
691 | err = usbd_req_reset_port(udev, NULL, portno); |
---|
692 | |
---|
693 | if (err) { |
---|
694 | DPRINTFN(0, "port %d reset " |
---|
695 | "failed, error=%s\n", |
---|
696 | portno, usbd_errstr(err)); |
---|
697 | goto error; |
---|
698 | } |
---|
699 | /* get port status again, it might have changed during reset */ |
---|
700 | |
---|
701 | err = uhub_read_port_status(sc, portno); |
---|
702 | if (err) { |
---|
703 | goto error; |
---|
704 | } |
---|
705 | /* check if something changed during port reset */ |
---|
706 | |
---|
707 | if ((sc->sc_st.port_change & UPS_C_CONNECT_STATUS) || |
---|
708 | (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS))) { |
---|
709 | if (timeout) { |
---|
710 | DPRINTFN(0, "giving up port reset " |
---|
711 | "- device vanished\n"); |
---|
712 | goto error; |
---|
713 | } |
---|
714 | timeout = 1; |
---|
715 | goto repeat; |
---|
716 | } |
---|
717 | } else { |
---|
718 | DPRINTF("Port %d is in Device Mode\n", portno); |
---|
719 | } |
---|
720 | |
---|
721 | /* |
---|
722 | * Figure out the device speed |
---|
723 | */ |
---|
724 | switch (udev->speed) { |
---|
725 | case USB_SPEED_HIGH: |
---|
726 | if (sc->sc_st.port_status & UPS_HIGH_SPEED) |
---|
727 | speed = USB_SPEED_HIGH; |
---|
728 | else if (sc->sc_st.port_status & UPS_LOW_SPEED) |
---|
729 | speed = USB_SPEED_LOW; |
---|
730 | else |
---|
731 | speed = USB_SPEED_FULL; |
---|
732 | break; |
---|
733 | case USB_SPEED_FULL: |
---|
734 | if (sc->sc_st.port_status & UPS_LOW_SPEED) |
---|
735 | speed = USB_SPEED_LOW; |
---|
736 | else |
---|
737 | speed = USB_SPEED_FULL; |
---|
738 | break; |
---|
739 | case USB_SPEED_LOW: |
---|
740 | speed = USB_SPEED_LOW; |
---|
741 | break; |
---|
742 | case USB_SPEED_SUPER: |
---|
743 | if (udev->parent_hub == NULL) { |
---|
744 | /* Root HUB - special case */ |
---|
745 | switch (sc->sc_st.port_status & UPS_OTHER_SPEED) { |
---|
746 | case 0: |
---|
747 | speed = USB_SPEED_FULL; |
---|
748 | break; |
---|
749 | case UPS_LOW_SPEED: |
---|
750 | speed = USB_SPEED_LOW; |
---|
751 | break; |
---|
752 | case UPS_HIGH_SPEED: |
---|
753 | speed = USB_SPEED_HIGH; |
---|
754 | break; |
---|
755 | default: |
---|
756 | speed = USB_SPEED_SUPER; |
---|
757 | break; |
---|
758 | } |
---|
759 | } else { |
---|
760 | speed = USB_SPEED_SUPER; |
---|
761 | } |
---|
762 | break; |
---|
763 | default: |
---|
764 | /* same speed like parent */ |
---|
765 | speed = udev->speed; |
---|
766 | break; |
---|
767 | } |
---|
768 | if (speed == USB_SPEED_SUPER) { |
---|
769 | err = usbd_req_set_hub_u1_timeout(udev, NULL, |
---|
770 | portno, 128 - (2 * udev->depth)); |
---|
771 | if (err) { |
---|
772 | DPRINTFN(0, "port %d U1 timeout " |
---|
773 | "failed, error=%s\n", |
---|
774 | portno, usbd_errstr(err)); |
---|
775 | } |
---|
776 | err = usbd_req_set_hub_u2_timeout(udev, NULL, |
---|
777 | portno, 128 - (2 * udev->depth)); |
---|
778 | if (err) { |
---|
779 | DPRINTFN(0, "port %d U2 timeout " |
---|
780 | "failed, error=%s\n", |
---|
781 | portno, usbd_errstr(err)); |
---|
782 | } |
---|
783 | } |
---|
784 | |
---|
785 | /* |
---|
786 | * Figure out the device mode |
---|
787 | * |
---|
788 | * NOTE: This part is currently FreeBSD specific. |
---|
789 | */ |
---|
790 | if (udev->parent_hub != NULL) { |
---|
791 | /* inherit mode from the parent HUB */ |
---|
792 | mode = udev->parent_hub->flags.usb_mode; |
---|
793 | } else if (sc->sc_st.port_status & UPS_PORT_MODE_DEVICE) |
---|
794 | mode = USB_MODE_DEVICE; |
---|
795 | else |
---|
796 | mode = USB_MODE_HOST; |
---|
797 | |
---|
798 | /* need to create a new child */ |
---|
799 | child = usb_alloc_device(sc->sc_dev, udev->bus, udev, |
---|
800 | udev->depth + 1, portno - 1, portno, speed, mode); |
---|
801 | if (child == NULL) { |
---|
802 | DPRINTFN(0, "could not allocate new device\n"); |
---|
803 | goto error; |
---|
804 | } |
---|
805 | return (0); /* success */ |
---|
806 | |
---|
807 | error: |
---|
808 | if (child != NULL) { |
---|
809 | /* |
---|
810 | * Free USB device and all subdevices, if any. |
---|
811 | */ |
---|
812 | usb_free_device(child, 0); |
---|
813 | child = NULL; |
---|
814 | } |
---|
815 | if (err == 0) { |
---|
816 | if (sc->sc_st.port_status & UPS_PORT_ENABLED) { |
---|
817 | err = usbd_req_clear_port_feature( |
---|
818 | sc->sc_udev, NULL, |
---|
819 | portno, UHF_PORT_ENABLE); |
---|
820 | } |
---|
821 | } |
---|
822 | if (err) { |
---|
823 | DPRINTFN(0, "device problem (%s), " |
---|
824 | "disabling port %d\n", usbd_errstr(err), portno); |
---|
825 | } |
---|
826 | return (err); |
---|
827 | } |
---|
828 | |
---|
829 | /*------------------------------------------------------------------------* |
---|
830 | * usb_device_20_compatible |
---|
831 | * |
---|
832 | * Returns: |
---|
833 | * 0: HUB does not support suspend and resume |
---|
834 | * Else: HUB supports suspend and resume |
---|
835 | *------------------------------------------------------------------------*/ |
---|
836 | static uint8_t |
---|
837 | usb_device_20_compatible(struct usb_device *udev) |
---|
838 | { |
---|
839 | if (udev == NULL) |
---|
840 | return (0); |
---|
841 | switch (udev->speed) { |
---|
842 | case USB_SPEED_LOW: |
---|
843 | case USB_SPEED_FULL: |
---|
844 | case USB_SPEED_HIGH: |
---|
845 | return (1); |
---|
846 | default: |
---|
847 | return (0); |
---|
848 | } |
---|
849 | } |
---|
850 | |
---|
851 | /*------------------------------------------------------------------------* |
---|
852 | * uhub_suspend_resume_port |
---|
853 | * |
---|
854 | * Returns: |
---|
855 | * 0: Success |
---|
856 | * Else: A control transaction failed |
---|
857 | *------------------------------------------------------------------------*/ |
---|
858 | static usb_error_t |
---|
859 | uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno) |
---|
860 | { |
---|
861 | struct usb_device *child; |
---|
862 | struct usb_device *udev; |
---|
863 | uint8_t is_suspend; |
---|
864 | usb_error_t err; |
---|
865 | |
---|
866 | DPRINTF("port %d\n", portno); |
---|
867 | |
---|
868 | udev = sc->sc_udev; |
---|
869 | child = usb_bus_port_get_device(udev->bus, |
---|
870 | udev->hub->ports + portno - 1); |
---|
871 | |
---|
872 | /* first clear the port suspend change bit */ |
---|
873 | |
---|
874 | if (usb_device_20_compatible(udev)) { |
---|
875 | err = usbd_req_clear_port_feature(udev, NULL, |
---|
876 | portno, UHF_C_PORT_SUSPEND); |
---|
877 | } else { |
---|
878 | err = usbd_req_clear_port_feature(udev, NULL, |
---|
879 | portno, UHF_C_PORT_LINK_STATE); |
---|
880 | } |
---|
881 | |
---|
882 | if (err) { |
---|
883 | DPRINTF("clearing suspend failed.\n"); |
---|
884 | goto done; |
---|
885 | } |
---|
886 | /* get fresh status */ |
---|
887 | |
---|
888 | err = uhub_read_port_status(sc, portno); |
---|
889 | if (err) { |
---|
890 | DPRINTF("reading port status failed.\n"); |
---|
891 | goto done; |
---|
892 | } |
---|
893 | /* convert current state */ |
---|
894 | |
---|
895 | if (usb_device_20_compatible(udev)) { |
---|
896 | if (sc->sc_st.port_status & UPS_SUSPEND) { |
---|
897 | is_suspend = 1; |
---|
898 | } else { |
---|
899 | is_suspend = 0; |
---|
900 | } |
---|
901 | } else { |
---|
902 | switch (UPS_PORT_LINK_STATE_GET(sc->sc_st.port_status)) { |
---|
903 | case UPS_PORT_LS_U3: |
---|
904 | is_suspend = 1; |
---|
905 | break; |
---|
906 | case UPS_PORT_LS_SS_INA: |
---|
907 | usbd_req_warm_reset_port(udev, NULL, portno); |
---|
908 | is_suspend = 0; |
---|
909 | break; |
---|
910 | default: |
---|
911 | is_suspend = 0; |
---|
912 | break; |
---|
913 | } |
---|
914 | } |
---|
915 | |
---|
916 | DPRINTF("suspended=%u\n", is_suspend); |
---|
917 | |
---|
918 | /* do the suspend or resume */ |
---|
919 | |
---|
920 | if (child) { |
---|
921 | /* |
---|
922 | * This code handle two cases: 1) Host Mode - we can only |
---|
923 | * receive resume here 2) Device Mode - we can receive |
---|
924 | * suspend and resume here |
---|
925 | */ |
---|
926 | if (is_suspend == 0) |
---|
927 | usb_dev_resume_peer(child); |
---|
928 | else if (child->flags.usb_mode == USB_MODE_DEVICE) |
---|
929 | usb_dev_suspend_peer(child); |
---|
930 | } |
---|
931 | done: |
---|
932 | return (err); |
---|
933 | } |
---|
934 | |
---|
935 | /*------------------------------------------------------------------------* |
---|
936 | * uhub_root_interrupt |
---|
937 | * |
---|
938 | * This function is called when a Root HUB interrupt has |
---|
939 | * happened. "ptr" and "len" makes up the Root HUB interrupt |
---|
940 | * packet. This function is called having the "bus_mtx" locked. |
---|
941 | *------------------------------------------------------------------------*/ |
---|
942 | void |
---|
943 | uhub_root_intr(struct usb_bus *bus, const uint8_t *ptr, uint8_t len) |
---|
944 | { |
---|
945 | USB_BUS_LOCK_ASSERT(bus, MA_OWNED); |
---|
946 | |
---|
947 | usb_needs_explore(bus, 0); |
---|
948 | } |
---|
949 | |
---|
950 | static uint8_t |
---|
951 | uhub_is_too_deep(struct usb_device *udev) |
---|
952 | { |
---|
953 | switch (udev->speed) { |
---|
954 | case USB_SPEED_FULL: |
---|
955 | case USB_SPEED_LOW: |
---|
956 | case USB_SPEED_HIGH: |
---|
957 | if (udev->depth > USB_HUB_MAX_DEPTH) |
---|
958 | return (1); |
---|
959 | break; |
---|
960 | case USB_SPEED_SUPER: |
---|
961 | if (udev->depth > USB_SS_HUB_DEPTH_MAX) |
---|
962 | return (1); |
---|
963 | break; |
---|
964 | default: |
---|
965 | break; |
---|
966 | } |
---|
967 | return (0); |
---|
968 | } |
---|
969 | |
---|
970 | /*------------------------------------------------------------------------* |
---|
971 | * uhub_explore |
---|
972 | * |
---|
973 | * Returns: |
---|
974 | * 0: Success |
---|
975 | * Else: Failure |
---|
976 | *------------------------------------------------------------------------*/ |
---|
977 | static usb_error_t |
---|
978 | uhub_explore(struct usb_device *udev) |
---|
979 | { |
---|
980 | struct usb_hub *hub; |
---|
981 | struct uhub_softc *sc; |
---|
982 | struct usb_port *up; |
---|
983 | usb_error_t err; |
---|
984 | uint8_t portno; |
---|
985 | uint8_t x; |
---|
986 | uint8_t do_unlock; |
---|
987 | |
---|
988 | hub = udev->hub; |
---|
989 | sc = hub->hubsoftc; |
---|
990 | |
---|
991 | DPRINTFN(11, "udev=%p addr=%d\n", udev, udev->address); |
---|
992 | |
---|
993 | /* ignore devices that are too deep */ |
---|
994 | if (uhub_is_too_deep(udev)) |
---|
995 | return (USB_ERR_TOO_DEEP); |
---|
996 | |
---|
997 | /* check if device is suspended */ |
---|
998 | if (udev->flags.self_suspended) { |
---|
999 | /* need to wait until the child signals resume */ |
---|
1000 | DPRINTF("Device is suspended!\n"); |
---|
1001 | return (0); |
---|
1002 | } |
---|
1003 | |
---|
1004 | /* |
---|
1005 | * Make sure we don't race against user-space applications |
---|
1006 | * like LibUSB: |
---|
1007 | */ |
---|
1008 | do_unlock = usbd_enum_lock(udev); |
---|
1009 | |
---|
1010 | for (x = 0; x != hub->nports; x++) { |
---|
1011 | up = hub->ports + x; |
---|
1012 | portno = x + 1; |
---|
1013 | |
---|
1014 | err = uhub_read_port_status(sc, portno); |
---|
1015 | if (err) { |
---|
1016 | /* most likely the HUB is gone */ |
---|
1017 | break; |
---|
1018 | } |
---|
1019 | if (sc->sc_st.port_change & UPS_C_OVERCURRENT_INDICATOR) { |
---|
1020 | DPRINTF("Overcurrent on port %u.\n", portno); |
---|
1021 | err = usbd_req_clear_port_feature( |
---|
1022 | udev, NULL, portno, UHF_C_PORT_OVER_CURRENT); |
---|
1023 | if (err) { |
---|
1024 | /* most likely the HUB is gone */ |
---|
1025 | break; |
---|
1026 | } |
---|
1027 | } |
---|
1028 | if (!(sc->sc_flags & UHUB_FLAG_DID_EXPLORE)) { |
---|
1029 | /* |
---|
1030 | * Fake a connect status change so that the |
---|
1031 | * status gets checked initially! |
---|
1032 | */ |
---|
1033 | sc->sc_st.port_change |= |
---|
1034 | UPS_C_CONNECT_STATUS; |
---|
1035 | } |
---|
1036 | if (sc->sc_st.port_change & UPS_C_PORT_ENABLED) { |
---|
1037 | err = usbd_req_clear_port_feature( |
---|
1038 | udev, NULL, portno, UHF_C_PORT_ENABLE); |
---|
1039 | if (err) { |
---|
1040 | /* most likely the HUB is gone */ |
---|
1041 | break; |
---|
1042 | } |
---|
1043 | if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) { |
---|
1044 | /* |
---|
1045 | * Ignore the port error if the device |
---|
1046 | * has vanished ! |
---|
1047 | */ |
---|
1048 | } else if (sc->sc_st.port_status & UPS_PORT_ENABLED) { |
---|
1049 | DPRINTFN(0, "illegal enable change, " |
---|
1050 | "port %d\n", portno); |
---|
1051 | } else { |
---|
1052 | |
---|
1053 | if (up->restartcnt == USB_RESTART_MAX) { |
---|
1054 | /* XXX could try another speed ? */ |
---|
1055 | DPRINTFN(0, "port error, giving up " |
---|
1056 | "port %d\n", portno); |
---|
1057 | } else { |
---|
1058 | sc->sc_st.port_change |= |
---|
1059 | UPS_C_CONNECT_STATUS; |
---|
1060 | up->restartcnt++; |
---|
1061 | } |
---|
1062 | } |
---|
1063 | } |
---|
1064 | if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) { |
---|
1065 | err = uhub_reattach_port(sc, portno); |
---|
1066 | if (err) { |
---|
1067 | /* most likely the HUB is gone */ |
---|
1068 | break; |
---|
1069 | } |
---|
1070 | } |
---|
1071 | if (sc->sc_st.port_change & (UPS_C_SUSPEND | |
---|
1072 | UPS_C_PORT_LINK_STATE)) { |
---|
1073 | err = uhub_suspend_resume_port(sc, portno); |
---|
1074 | if (err) { |
---|
1075 | /* most likely the HUB is gone */ |
---|
1076 | break; |
---|
1077 | } |
---|
1078 | } |
---|
1079 | err = uhub_explore_sub(sc, up); |
---|
1080 | if (err) { |
---|
1081 | /* no device(s) present */ |
---|
1082 | continue; |
---|
1083 | } |
---|
1084 | /* explore succeeded - reset restart counter */ |
---|
1085 | up->restartcnt = 0; |
---|
1086 | } |
---|
1087 | |
---|
1088 | if (do_unlock) |
---|
1089 | usbd_enum_unlock(udev); |
---|
1090 | |
---|
1091 | /* initial status checked */ |
---|
1092 | sc->sc_flags |= UHUB_FLAG_DID_EXPLORE; |
---|
1093 | |
---|
1094 | /* return success */ |
---|
1095 | return (USB_ERR_NORMAL_COMPLETION); |
---|
1096 | } |
---|
1097 | |
---|
1098 | static int |
---|
1099 | uhub_probe(device_t dev) |
---|
1100 | { |
---|
1101 | struct usb_attach_arg *uaa = device_get_ivars(dev); |
---|
1102 | |
---|
1103 | if (uaa->usb_mode != USB_MODE_HOST) |
---|
1104 | return (ENXIO); |
---|
1105 | |
---|
1106 | /* |
---|
1107 | * The subclass for USB HUBs is currently ignored because it |
---|
1108 | * is 0 for some and 1 for others. |
---|
1109 | */ |
---|
1110 | if (uaa->info.bConfigIndex == 0 && |
---|
1111 | uaa->info.bDeviceClass == UDCLASS_HUB) |
---|
1112 | return (0); |
---|
1113 | |
---|
1114 | return (ENXIO); |
---|
1115 | } |
---|
1116 | |
---|
1117 | /* NOTE: The information returned by this function can be wrong. */ |
---|
1118 | usb_error_t |
---|
1119 | uhub_query_info(struct usb_device *udev, uint8_t *pnports, uint8_t *ptt) |
---|
1120 | { |
---|
1121 | struct usb_hub_descriptor hubdesc20; |
---|
1122 | struct usb_hub_ss_descriptor hubdesc30; |
---|
1123 | usb_error_t err; |
---|
1124 | uint8_t nports; |
---|
1125 | uint8_t tt; |
---|
1126 | |
---|
1127 | if (udev->ddesc.bDeviceClass != UDCLASS_HUB) |
---|
1128 | return (USB_ERR_INVAL); |
---|
1129 | |
---|
1130 | nports = 0; |
---|
1131 | tt = 0; |
---|
1132 | |
---|
1133 | switch (udev->speed) { |
---|
1134 | case USB_SPEED_LOW: |
---|
1135 | case USB_SPEED_FULL: |
---|
1136 | case USB_SPEED_HIGH: |
---|
1137 | /* assuming that there is one port */ |
---|
1138 | err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, 1); |
---|
1139 | if (err) { |
---|
1140 | DPRINTFN(0, "getting USB 2.0 HUB descriptor failed," |
---|
1141 | "error=%s\n", usbd_errstr(err)); |
---|
1142 | break; |
---|
1143 | } |
---|
1144 | nports = hubdesc20.bNbrPorts; |
---|
1145 | if (nports > 127) |
---|
1146 | nports = 127; |
---|
1147 | |
---|
1148 | if (udev->speed == USB_SPEED_HIGH) |
---|
1149 | tt = (UGETW(hubdesc20.wHubCharacteristics) >> 5) & 3; |
---|
1150 | break; |
---|
1151 | |
---|
1152 | case USB_SPEED_SUPER: |
---|
1153 | err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, 1); |
---|
1154 | if (err) { |
---|
1155 | DPRINTFN(0, "Getting USB 3.0 HUB descriptor failed," |
---|
1156 | "error=%s\n", usbd_errstr(err)); |
---|
1157 | break; |
---|
1158 | } |
---|
1159 | nports = hubdesc30.bNbrPorts; |
---|
1160 | if (nports > 16) |
---|
1161 | nports = 16; |
---|
1162 | break; |
---|
1163 | |
---|
1164 | default: |
---|
1165 | err = USB_ERR_INVAL; |
---|
1166 | break; |
---|
1167 | } |
---|
1168 | |
---|
1169 | if (pnports != NULL) |
---|
1170 | *pnports = nports; |
---|
1171 | |
---|
1172 | if (ptt != NULL) |
---|
1173 | *ptt = tt; |
---|
1174 | |
---|
1175 | return (err); |
---|
1176 | } |
---|
1177 | |
---|
1178 | static int |
---|
1179 | uhub_attach(device_t dev) |
---|
1180 | { |
---|
1181 | struct uhub_softc *sc = device_get_softc(dev); |
---|
1182 | struct usb_attach_arg *uaa = device_get_ivars(dev); |
---|
1183 | struct usb_device *udev = uaa->device; |
---|
1184 | struct usb_device *parent_hub = udev->parent_hub; |
---|
1185 | struct usb_hub *hub; |
---|
1186 | struct usb_hub_descriptor hubdesc20; |
---|
1187 | struct usb_hub_ss_descriptor hubdesc30; |
---|
1188 | uint16_t pwrdly; |
---|
1189 | uint8_t x; |
---|
1190 | uint8_t nports; |
---|
1191 | uint8_t portno; |
---|
1192 | uint8_t removable; |
---|
1193 | uint8_t iface_index; |
---|
1194 | usb_error_t err; |
---|
1195 | |
---|
1196 | sc->sc_udev = udev; |
---|
1197 | sc->sc_dev = dev; |
---|
1198 | |
---|
1199 | mtx_init(&sc->sc_mtx, "USB HUB mutex", NULL, MTX_DEF); |
---|
1200 | |
---|
1201 | snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", |
---|
1202 | device_get_nameunit(dev)); |
---|
1203 | |
---|
1204 | device_set_usb_desc(dev); |
---|
1205 | |
---|
1206 | DPRINTFN(2, "depth=%d selfpowered=%d, parent=%p, " |
---|
1207 | "parent->selfpowered=%d\n", |
---|
1208 | udev->depth, |
---|
1209 | udev->flags.self_powered, |
---|
1210 | parent_hub, |
---|
1211 | parent_hub ? |
---|
1212 | parent_hub->flags.self_powered : 0); |
---|
1213 | |
---|
1214 | if (uhub_is_too_deep(udev)) { |
---|
1215 | DPRINTFN(0, "HUB at depth %d, " |
---|
1216 | "exceeds maximum. HUB ignored\n", (int)udev->depth); |
---|
1217 | goto error; |
---|
1218 | } |
---|
1219 | |
---|
1220 | if (!udev->flags.self_powered && parent_hub && |
---|
1221 | !parent_hub->flags.self_powered) { |
---|
1222 | DPRINTFN(0, "Bus powered HUB connected to " |
---|
1223 | "bus powered HUB. HUB ignored\n"); |
---|
1224 | goto error; |
---|
1225 | } |
---|
1226 | |
---|
1227 | if (UHUB_IS_MULTI_TT(sc)) { |
---|
1228 | err = usbd_set_alt_interface_index(udev, 0, 1); |
---|
1229 | if (err) { |
---|
1230 | device_printf(dev, "MTT could not be enabled\n"); |
---|
1231 | goto error; |
---|
1232 | } |
---|
1233 | device_printf(dev, "MTT enabled\n"); |
---|
1234 | } |
---|
1235 | |
---|
1236 | /* get HUB descriptor */ |
---|
1237 | |
---|
1238 | DPRINTFN(2, "Getting HUB descriptor\n"); |
---|
1239 | |
---|
1240 | switch (udev->speed) { |
---|
1241 | case USB_SPEED_LOW: |
---|
1242 | case USB_SPEED_FULL: |
---|
1243 | case USB_SPEED_HIGH: |
---|
1244 | /* assuming that there is one port */ |
---|
1245 | err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, 1); |
---|
1246 | if (err) { |
---|
1247 | DPRINTFN(0, "getting USB 2.0 HUB descriptor failed," |
---|
1248 | "error=%s\n", usbd_errstr(err)); |
---|
1249 | goto error; |
---|
1250 | } |
---|
1251 | /* get number of ports */ |
---|
1252 | nports = hubdesc20.bNbrPorts; |
---|
1253 | |
---|
1254 | /* get power delay */ |
---|
1255 | pwrdly = ((hubdesc20.bPwrOn2PwrGood * UHD_PWRON_FACTOR) + |
---|
1256 | usb_extra_power_up_time); |
---|
1257 | |
---|
1258 | /* get complete HUB descriptor */ |
---|
1259 | if (nports >= 8) { |
---|
1260 | /* check number of ports */ |
---|
1261 | if (nports > 127) { |
---|
1262 | DPRINTFN(0, "Invalid number of USB 2.0 ports," |
---|
1263 | "error=%s\n", usbd_errstr(err)); |
---|
1264 | goto error; |
---|
1265 | } |
---|
1266 | /* get complete HUB descriptor */ |
---|
1267 | err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, nports); |
---|
1268 | |
---|
1269 | if (err) { |
---|
1270 | DPRINTFN(0, "Getting USB 2.0 HUB descriptor failed," |
---|
1271 | "error=%s\n", usbd_errstr(err)); |
---|
1272 | goto error; |
---|
1273 | } |
---|
1274 | if (hubdesc20.bNbrPorts != nports) { |
---|
1275 | DPRINTFN(0, "Number of ports changed\n"); |
---|
1276 | goto error; |
---|
1277 | } |
---|
1278 | } |
---|
1279 | break; |
---|
1280 | case USB_SPEED_SUPER: |
---|
1281 | if (udev->parent_hub != NULL) { |
---|
1282 | err = usbd_req_set_hub_depth(udev, NULL, |
---|
1283 | udev->depth - 1); |
---|
1284 | if (err) { |
---|
1285 | DPRINTFN(0, "Setting USB 3.0 HUB depth failed," |
---|
1286 | "error=%s\n", usbd_errstr(err)); |
---|
1287 | goto error; |
---|
1288 | } |
---|
1289 | } |
---|
1290 | err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, 1); |
---|
1291 | if (err) { |
---|
1292 | DPRINTFN(0, "Getting USB 3.0 HUB descriptor failed," |
---|
1293 | "error=%s\n", usbd_errstr(err)); |
---|
1294 | goto error; |
---|
1295 | } |
---|
1296 | /* get number of ports */ |
---|
1297 | nports = hubdesc30.bNbrPorts; |
---|
1298 | |
---|
1299 | /* get power delay */ |
---|
1300 | pwrdly = ((hubdesc30.bPwrOn2PwrGood * UHD_PWRON_FACTOR) + |
---|
1301 | usb_extra_power_up_time); |
---|
1302 | |
---|
1303 | /* get complete HUB descriptor */ |
---|
1304 | if (nports >= 8) { |
---|
1305 | /* check number of ports */ |
---|
1306 | if (nports > ((udev->parent_hub != NULL) ? 15 : 127)) { |
---|
1307 | DPRINTFN(0, "Invalid number of USB 3.0 ports," |
---|
1308 | "error=%s\n", usbd_errstr(err)); |
---|
1309 | goto error; |
---|
1310 | } |
---|
1311 | /* get complete HUB descriptor */ |
---|
1312 | err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, nports); |
---|
1313 | |
---|
1314 | if (err) { |
---|
1315 | DPRINTFN(0, "Getting USB 2.0 HUB descriptor failed," |
---|
1316 | "error=%s\n", usbd_errstr(err)); |
---|
1317 | goto error; |
---|
1318 | } |
---|
1319 | if (hubdesc30.bNbrPorts != nports) { |
---|
1320 | DPRINTFN(0, "Number of ports changed\n"); |
---|
1321 | goto error; |
---|
1322 | } |
---|
1323 | } |
---|
1324 | break; |
---|
1325 | default: |
---|
1326 | DPRINTF("Assuming HUB has only one port\n"); |
---|
1327 | /* default number of ports */ |
---|
1328 | nports = 1; |
---|
1329 | /* default power delay */ |
---|
1330 | pwrdly = ((10 * UHD_PWRON_FACTOR) + usb_extra_power_up_time); |
---|
1331 | break; |
---|
1332 | } |
---|
1333 | if (nports == 0) { |
---|
1334 | DPRINTFN(0, "portless HUB\n"); |
---|
1335 | goto error; |
---|
1336 | } |
---|
1337 | hub = malloc(sizeof(hub[0]) + (sizeof(hub->ports[0]) * nports), |
---|
1338 | M_USBDEV, M_WAITOK | M_ZERO); |
---|
1339 | |
---|
1340 | if (hub == NULL) { |
---|
1341 | goto error; |
---|
1342 | } |
---|
1343 | udev->hub = hub; |
---|
1344 | |
---|
1345 | /* initialize HUB structure */ |
---|
1346 | hub->hubsoftc = sc; |
---|
1347 | hub->explore = &uhub_explore; |
---|
1348 | hub->nports = nports; |
---|
1349 | hub->hubudev = udev; |
---|
1350 | #if USB_HAVE_TT_SUPPORT |
---|
1351 | hub->tt_msg[0].hdr.pm_callback = &uhub_reset_tt_proc; |
---|
1352 | hub->tt_msg[0].udev = udev; |
---|
1353 | hub->tt_msg[1].hdr.pm_callback = &uhub_reset_tt_proc; |
---|
1354 | hub->tt_msg[1].udev = udev; |
---|
1355 | #endif |
---|
1356 | /* if self powered hub, give ports maximum current */ |
---|
1357 | if (udev->flags.self_powered) { |
---|
1358 | hub->portpower = USB_MAX_POWER; |
---|
1359 | } else { |
---|
1360 | hub->portpower = USB_MIN_POWER; |
---|
1361 | } |
---|
1362 | |
---|
1363 | /* set up interrupt pipe */ |
---|
1364 | iface_index = 0; |
---|
1365 | if (udev->parent_hub == NULL) { |
---|
1366 | /* root HUB is special */ |
---|
1367 | err = 0; |
---|
1368 | } else { |
---|
1369 | /* normal HUB */ |
---|
1370 | err = usbd_transfer_setup(udev, &iface_index, sc->sc_xfer, |
---|
1371 | uhub_config, UHUB_N_TRANSFER, sc, &sc->sc_mtx); |
---|
1372 | } |
---|
1373 | if (err) { |
---|
1374 | DPRINTFN(0, "cannot setup interrupt transfer, " |
---|
1375 | "errstr=%s\n", usbd_errstr(err)); |
---|
1376 | goto error; |
---|
1377 | } |
---|
1378 | /* wait with power off for a while */ |
---|
1379 | usb_pause_mtx(NULL, USB_MS_TO_TICKS(USB_POWER_DOWN_TIME)); |
---|
1380 | |
---|
1381 | /* |
---|
1382 | * To have the best chance of success we do things in the exact same |
---|
1383 | * order as Windoze98. This should not be necessary, but some |
---|
1384 | * devices do not follow the USB specs to the letter. |
---|
1385 | * |
---|
1386 | * These are the events on the bus when a hub is attached: |
---|
1387 | * Get device and config descriptors (see attach code) |
---|
1388 | * Get hub descriptor (see above) |
---|
1389 | * For all ports |
---|
1390 | * turn on power |
---|
1391 | * wait for power to become stable |
---|
1392 | * (all below happens in explore code) |
---|
1393 | * For all ports |
---|
1394 | * clear C_PORT_CONNECTION |
---|
1395 | * For all ports |
---|
1396 | * get port status |
---|
1397 | * if device connected |
---|
1398 | * wait 100 ms |
---|
1399 | * turn on reset |
---|
1400 | * wait |
---|
1401 | * clear C_PORT_RESET |
---|
1402 | * get port status |
---|
1403 | * proceed with device attachment |
---|
1404 | */ |
---|
1405 | |
---|
1406 | /* XXX should check for none, individual, or ganged power? */ |
---|
1407 | |
---|
1408 | removable = 0; |
---|
1409 | |
---|
1410 | for (x = 0; x != nports; x++) { |
---|
1411 | /* set up data structures */ |
---|
1412 | struct usb_port *up = hub->ports + x; |
---|
1413 | |
---|
1414 | up->device_index = 0; |
---|
1415 | up->restartcnt = 0; |
---|
1416 | portno = x + 1; |
---|
1417 | |
---|
1418 | /* check if port is removable */ |
---|
1419 | switch (udev->speed) { |
---|
1420 | case USB_SPEED_LOW: |
---|
1421 | case USB_SPEED_FULL: |
---|
1422 | case USB_SPEED_HIGH: |
---|
1423 | if (!UHD_NOT_REMOV(&hubdesc20, portno)) |
---|
1424 | removable++; |
---|
1425 | break; |
---|
1426 | case USB_SPEED_SUPER: |
---|
1427 | if (!UHD_NOT_REMOV(&hubdesc30, portno)) |
---|
1428 | removable++; |
---|
1429 | break; |
---|
1430 | default: |
---|
1431 | DPRINTF("Assuming removable port\n"); |
---|
1432 | removable++; |
---|
1433 | break; |
---|
1434 | } |
---|
1435 | if (!err) { |
---|
1436 | /* turn the power on */ |
---|
1437 | err = usbd_req_set_port_feature(udev, NULL, |
---|
1438 | portno, UHF_PORT_POWER); |
---|
1439 | } |
---|
1440 | if (err) { |
---|
1441 | DPRINTFN(0, "port %d power on failed, %s\n", |
---|
1442 | portno, usbd_errstr(err)); |
---|
1443 | } |
---|
1444 | DPRINTF("turn on port %d power\n", |
---|
1445 | portno); |
---|
1446 | |
---|
1447 | /* wait for stable power */ |
---|
1448 | usb_pause_mtx(NULL, USB_MS_TO_TICKS(pwrdly)); |
---|
1449 | } |
---|
1450 | |
---|
1451 | device_printf(dev, "%d port%s with %d " |
---|
1452 | "removable, %s powered\n", nports, (nports != 1) ? "s" : "", |
---|
1453 | removable, udev->flags.self_powered ? "self" : "bus"); |
---|
1454 | |
---|
1455 | /* Start the interrupt endpoint, if any */ |
---|
1456 | |
---|
1457 | mtx_lock(&sc->sc_mtx); |
---|
1458 | usbd_transfer_start(sc->sc_xfer[UHUB_INTR_TRANSFER]); |
---|
1459 | mtx_unlock(&sc->sc_mtx); |
---|
1460 | |
---|
1461 | /* Enable automatic power save on all USB HUBs */ |
---|
1462 | |
---|
1463 | usbd_set_power_mode(udev, USB_POWER_MODE_SAVE); |
---|
1464 | |
---|
1465 | return (0); |
---|
1466 | |
---|
1467 | error: |
---|
1468 | usbd_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER); |
---|
1469 | |
---|
1470 | if (udev->hub) { |
---|
1471 | free(udev->hub, M_USBDEV); |
---|
1472 | udev->hub = NULL; |
---|
1473 | } |
---|
1474 | |
---|
1475 | mtx_destroy(&sc->sc_mtx); |
---|
1476 | |
---|
1477 | return (ENXIO); |
---|
1478 | } |
---|
1479 | |
---|
1480 | /* |
---|
1481 | * Called from process context when the hub is gone. |
---|
1482 | * Detach all devices on active ports. |
---|
1483 | */ |
---|
1484 | static int |
---|
1485 | uhub_detach(device_t dev) |
---|
1486 | { |
---|
1487 | struct uhub_softc *sc = device_get_softc(dev); |
---|
1488 | struct usb_hub *hub = sc->sc_udev->hub; |
---|
1489 | struct usb_bus *bus = sc->sc_udev->bus; |
---|
1490 | struct usb_device *child; |
---|
1491 | uint8_t x; |
---|
1492 | |
---|
1493 | if (hub == NULL) /* must be partially working */ |
---|
1494 | return (0); |
---|
1495 | |
---|
1496 | /* Make sure interrupt transfer is gone. */ |
---|
1497 | usbd_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER); |
---|
1498 | |
---|
1499 | /* Detach all ports */ |
---|
1500 | for (x = 0; x != hub->nports; x++) { |
---|
1501 | |
---|
1502 | child = usb_bus_port_get_device(bus, hub->ports + x); |
---|
1503 | |
---|
1504 | if (child == NULL) { |
---|
1505 | continue; |
---|
1506 | } |
---|
1507 | |
---|
1508 | /* |
---|
1509 | * Free USB device and all subdevices, if any. |
---|
1510 | */ |
---|
1511 | usb_free_device(child, 0); |
---|
1512 | } |
---|
1513 | |
---|
1514 | #if USB_HAVE_TT_SUPPORT |
---|
1515 | /* Make sure our TT messages are not queued anywhere */ |
---|
1516 | USB_BUS_LOCK(bus); |
---|
1517 | usb_proc_mwait(&bus->non_giant_callback_proc, |
---|
1518 | &hub->tt_msg[0], &hub->tt_msg[1]); |
---|
1519 | USB_BUS_UNLOCK(bus); |
---|
1520 | #endif |
---|
1521 | free(hub, M_USBDEV); |
---|
1522 | sc->sc_udev->hub = NULL; |
---|
1523 | |
---|
1524 | mtx_destroy(&sc->sc_mtx); |
---|
1525 | |
---|
1526 | return (0); |
---|
1527 | } |
---|
1528 | |
---|
1529 | static int |
---|
1530 | uhub_suspend(device_t dev) |
---|
1531 | { |
---|
1532 | DPRINTF("\n"); |
---|
1533 | /* Sub-devices are not suspended here! */ |
---|
1534 | return (0); |
---|
1535 | } |
---|
1536 | |
---|
1537 | static int |
---|
1538 | uhub_resume(device_t dev) |
---|
1539 | { |
---|
1540 | DPRINTF("\n"); |
---|
1541 | /* Sub-devices are not resumed here! */ |
---|
1542 | return (0); |
---|
1543 | } |
---|
1544 | |
---|
1545 | static void |
---|
1546 | uhub_driver_added(device_t dev, driver_t *driver) |
---|
1547 | { |
---|
1548 | usb_needs_explore_all(); |
---|
1549 | } |
---|
1550 | |
---|
1551 | struct hub_result { |
---|
1552 | struct usb_device *udev; |
---|
1553 | uint8_t portno; |
---|
1554 | uint8_t iface_index; |
---|
1555 | }; |
---|
1556 | |
---|
1557 | static void |
---|
1558 | uhub_find_iface_index(struct usb_hub *hub, device_t child, |
---|
1559 | struct hub_result *res) |
---|
1560 | { |
---|
1561 | struct usb_interface *iface; |
---|
1562 | struct usb_device *udev; |
---|
1563 | uint8_t nports; |
---|
1564 | uint8_t x; |
---|
1565 | uint8_t i; |
---|
1566 | |
---|
1567 | nports = hub->nports; |
---|
1568 | for (x = 0; x != nports; x++) { |
---|
1569 | udev = usb_bus_port_get_device(hub->hubudev->bus, |
---|
1570 | hub->ports + x); |
---|
1571 | if (!udev) { |
---|
1572 | continue; |
---|
1573 | } |
---|
1574 | for (i = 0; i != USB_IFACE_MAX; i++) { |
---|
1575 | iface = usbd_get_iface(udev, i); |
---|
1576 | if (iface && |
---|
1577 | (iface->subdev == child)) { |
---|
1578 | res->iface_index = i; |
---|
1579 | res->udev = udev; |
---|
1580 | res->portno = x + 1; |
---|
1581 | return; |
---|
1582 | } |
---|
1583 | } |
---|
1584 | } |
---|
1585 | res->iface_index = 0; |
---|
1586 | res->udev = NULL; |
---|
1587 | res->portno = 0; |
---|
1588 | } |
---|
1589 | |
---|
1590 | static int |
---|
1591 | uhub_child_location_string(device_t parent, device_t child, |
---|
1592 | char *buf, size_t buflen) |
---|
1593 | { |
---|
1594 | struct uhub_softc *sc; |
---|
1595 | struct usb_hub *hub; |
---|
1596 | struct hub_result res; |
---|
1597 | char *ugen_name; |
---|
1598 | |
---|
1599 | if (!device_is_attached(parent)) { |
---|
1600 | if (buflen) |
---|
1601 | buf[0] = 0; |
---|
1602 | return (0); |
---|
1603 | } |
---|
1604 | |
---|
1605 | sc = device_get_softc(parent); |
---|
1606 | hub = sc->sc_udev->hub; |
---|
1607 | |
---|
1608 | mtx_lock(&Giant); |
---|
1609 | uhub_find_iface_index(hub, child, &res); |
---|
1610 | if (!res.udev) { |
---|
1611 | DPRINTF("device not on hub\n"); |
---|
1612 | if (buflen) { |
---|
1613 | buf[0] = '\0'; |
---|
1614 | } |
---|
1615 | goto done; |
---|
1616 | } |
---|
1617 | #if USB_HAVE_UGEN |
---|
1618 | ugen_name = res.udev->ugen_name; |
---|
1619 | #else |
---|
1620 | ugen_name = "?"; |
---|
1621 | #endif |
---|
1622 | snprintf(buf, buflen, "bus=%u hubaddr=%u port=%u devaddr=%u interface=%u" |
---|
1623 | " ugen=%s", |
---|
1624 | (res.udev->parent_hub != NULL) ? res.udev->parent_hub->device_index : 0, |
---|
1625 | res.portno, device_get_unit(res.udev->bus->bdev), |
---|
1626 | res.udev->device_index, res.iface_index, ugen_name); |
---|
1627 | done: |
---|
1628 | mtx_unlock(&Giant); |
---|
1629 | |
---|
1630 | return (0); |
---|
1631 | } |
---|
1632 | |
---|
1633 | static int |
---|
1634 | uhub_child_pnpinfo_string(device_t parent, device_t child, |
---|
1635 | char *buf, size_t buflen) |
---|
1636 | { |
---|
1637 | struct uhub_softc *sc; |
---|
1638 | struct usb_hub *hub; |
---|
1639 | struct usb_interface *iface; |
---|
1640 | struct hub_result res; |
---|
1641 | |
---|
1642 | if (!device_is_attached(parent)) { |
---|
1643 | if (buflen) |
---|
1644 | buf[0] = 0; |
---|
1645 | return (0); |
---|
1646 | } |
---|
1647 | |
---|
1648 | sc = device_get_softc(parent); |
---|
1649 | hub = sc->sc_udev->hub; |
---|
1650 | |
---|
1651 | mtx_lock(&Giant); |
---|
1652 | uhub_find_iface_index(hub, child, &res); |
---|
1653 | if (!res.udev) { |
---|
1654 | DPRINTF("device not on hub\n"); |
---|
1655 | if (buflen) { |
---|
1656 | buf[0] = '\0'; |
---|
1657 | } |
---|
1658 | goto done; |
---|
1659 | } |
---|
1660 | iface = usbd_get_iface(res.udev, res.iface_index); |
---|
1661 | if (iface && iface->idesc) { |
---|
1662 | snprintf(buf, buflen, "vendor=0x%04x product=0x%04x " |
---|
1663 | "devclass=0x%02x devsubclass=0x%02x " |
---|
1664 | "sernum=\"%s\" " |
---|
1665 | "release=0x%04x " |
---|
1666 | "mode=%s " |
---|
1667 | "intclass=0x%02x intsubclass=0x%02x " |
---|
1668 | "intprotocol=0x%02x" "%s%s", |
---|
1669 | UGETW(res.udev->ddesc.idVendor), |
---|
1670 | UGETW(res.udev->ddesc.idProduct), |
---|
1671 | res.udev->ddesc.bDeviceClass, |
---|
1672 | res.udev->ddesc.bDeviceSubClass, |
---|
1673 | usb_get_serial(res.udev), |
---|
1674 | UGETW(res.udev->ddesc.bcdDevice), |
---|
1675 | (res.udev->flags.usb_mode == USB_MODE_HOST) ? "host" : "device", |
---|
1676 | iface->idesc->bInterfaceClass, |
---|
1677 | iface->idesc->bInterfaceSubClass, |
---|
1678 | iface->idesc->bInterfaceProtocol, |
---|
1679 | iface->pnpinfo ? " " : "", |
---|
1680 | iface->pnpinfo ? iface->pnpinfo : ""); |
---|
1681 | } else { |
---|
1682 | if (buflen) { |
---|
1683 | buf[0] = '\0'; |
---|
1684 | } |
---|
1685 | goto done; |
---|
1686 | } |
---|
1687 | done: |
---|
1688 | mtx_unlock(&Giant); |
---|
1689 | |
---|
1690 | return (0); |
---|
1691 | } |
---|
1692 | |
---|
1693 | /* |
---|
1694 | * The USB Transaction Translator: |
---|
1695 | * =============================== |
---|
1696 | * |
---|
1697 | * When doing LOW- and FULL-speed USB transfers accross a HIGH-speed |
---|
1698 | * USB HUB, bandwidth must be allocated for ISOCHRONOUS and INTERRUPT |
---|
1699 | * USB transfers. To utilize bandwidth dynamically the "scatter and |
---|
1700 | * gather" principle must be applied. This means that bandwidth must |
---|
1701 | * be divided into equal parts of bandwidth. With regard to USB all |
---|
1702 | * data is transferred in smaller packets with length |
---|
1703 | * "wMaxPacketSize". The problem however is that "wMaxPacketSize" is |
---|
1704 | * not a constant! |
---|
1705 | * |
---|
1706 | * The bandwidth scheduler which I have implemented will simply pack |
---|
1707 | * the USB transfers back to back until there is no more space in the |
---|
1708 | * schedule. Out of the 8 microframes which the USB 2.0 standard |
---|
1709 | * provides, only 6 are available for non-HIGH-speed devices. I have |
---|
1710 | * reserved the first 4 microframes for ISOCHRONOUS transfers. The |
---|
1711 | * last 2 microframes I have reserved for INTERRUPT transfers. Without |
---|
1712 | * this division, it is very difficult to allocate and free bandwidth |
---|
1713 | * dynamically. |
---|
1714 | * |
---|
1715 | * NOTE about the Transaction Translator in USB HUBs: |
---|
1716 | * |
---|
1717 | * USB HUBs have a very simple Transaction Translator, that will |
---|
1718 | * simply pipeline all the SPLIT transactions. That means that the |
---|
1719 | * transactions will be executed in the order they are queued! |
---|
1720 | * |
---|
1721 | */ |
---|
1722 | |
---|
1723 | /*------------------------------------------------------------------------* |
---|
1724 | * usb_intr_find_best_slot |
---|
1725 | * |
---|
1726 | * Return value: |
---|
1727 | * The best Transaction Translation slot for an interrupt endpoint. |
---|
1728 | *------------------------------------------------------------------------*/ |
---|
1729 | static uint8_t |
---|
1730 | usb_intr_find_best_slot(usb_size_t *ptr, uint8_t start, |
---|
1731 | uint8_t end, uint8_t mask) |
---|
1732 | { |
---|
1733 | usb_size_t min = (usb_size_t)-1; |
---|
1734 | usb_size_t sum; |
---|
1735 | uint8_t x; |
---|
1736 | uint8_t y; |
---|
1737 | uint8_t z; |
---|
1738 | |
---|
1739 | y = 0; |
---|
1740 | |
---|
1741 | /* find the last slot with lesser used bandwidth */ |
---|
1742 | |
---|
1743 | for (x = start; x < end; x++) { |
---|
1744 | |
---|
1745 | sum = 0; |
---|
1746 | |
---|
1747 | /* compute sum of bandwidth */ |
---|
1748 | for (z = x; z < end; z++) { |
---|
1749 | if (mask & (1U << (z - x))) |
---|
1750 | sum += ptr[z]; |
---|
1751 | } |
---|
1752 | |
---|
1753 | /* check if the current multi-slot is more optimal */ |
---|
1754 | if (min >= sum) { |
---|
1755 | min = sum; |
---|
1756 | y = x; |
---|
1757 | } |
---|
1758 | |
---|
1759 | /* check if the mask is about to be shifted out */ |
---|
1760 | if (mask & (1U << (end - 1 - x))) |
---|
1761 | break; |
---|
1762 | } |
---|
1763 | return (y); |
---|
1764 | } |
---|
1765 | |
---|
1766 | /*------------------------------------------------------------------------* |
---|
1767 | * usb_hs_bandwidth_adjust |
---|
1768 | * |
---|
1769 | * This function will update the bandwith usage for the microframe |
---|
1770 | * having index "slot" by "len" bytes. "len" can be negative. If the |
---|
1771 | * "slot" argument is greater or equal to "USB_HS_MICRO_FRAMES_MAX" |
---|
1772 | * the "slot" argument will be replaced by the slot having least used |
---|
1773 | * bandwidth. The "mask" argument is used for multi-slot allocations. |
---|
1774 | * |
---|
1775 | * Returns: |
---|
1776 | * The slot in which the bandwidth update was done: 0..7 |
---|
1777 | *------------------------------------------------------------------------*/ |
---|
1778 | static uint8_t |
---|
1779 | usb_hs_bandwidth_adjust(struct usb_device *udev, int16_t len, |
---|
1780 | uint8_t slot, uint8_t mask) |
---|
1781 | { |
---|
1782 | struct usb_bus *bus = udev->bus; |
---|
1783 | struct usb_hub *hub; |
---|
1784 | enum usb_dev_speed speed; |
---|
1785 | uint8_t x; |
---|
1786 | |
---|
1787 | USB_BUS_LOCK_ASSERT(bus, MA_OWNED); |
---|
1788 | |
---|
1789 | speed = usbd_get_speed(udev); |
---|
1790 | |
---|
1791 | switch (speed) { |
---|
1792 | case USB_SPEED_LOW: |
---|
1793 | case USB_SPEED_FULL: |
---|
1794 | if (speed == USB_SPEED_LOW) { |
---|
1795 | len *= 8; |
---|
1796 | } |
---|
1797 | /* |
---|
1798 | * The Host Controller Driver should have |
---|
1799 | * performed checks so that the lookup |
---|
1800 | * below does not result in a NULL pointer |
---|
1801 | * access. |
---|
1802 | */ |
---|
1803 | |
---|
1804 | hub = udev->parent_hs_hub->hub; |
---|
1805 | if (slot >= USB_HS_MICRO_FRAMES_MAX) { |
---|
1806 | slot = usb_intr_find_best_slot(hub->uframe_usage, |
---|
1807 | USB_FS_ISOC_UFRAME_MAX, 6, mask); |
---|
1808 | } |
---|
1809 | for (x = slot; x < 8; x++) { |
---|
1810 | if (mask & (1U << (x - slot))) { |
---|
1811 | hub->uframe_usage[x] += len; |
---|
1812 | bus->uframe_usage[x] += len; |
---|
1813 | } |
---|
1814 | } |
---|
1815 | break; |
---|
1816 | default: |
---|
1817 | if (slot >= USB_HS_MICRO_FRAMES_MAX) { |
---|
1818 | slot = usb_intr_find_best_slot(bus->uframe_usage, 0, |
---|
1819 | USB_HS_MICRO_FRAMES_MAX, mask); |
---|
1820 | } |
---|
1821 | for (x = slot; x < 8; x++) { |
---|
1822 | if (mask & (1U << (x - slot))) { |
---|
1823 | bus->uframe_usage[x] += len; |
---|
1824 | } |
---|
1825 | } |
---|
1826 | break; |
---|
1827 | } |
---|
1828 | return (slot); |
---|
1829 | } |
---|
1830 | |
---|
1831 | /*------------------------------------------------------------------------* |
---|
1832 | * usb_hs_bandwidth_alloc |
---|
1833 | * |
---|
1834 | * This function is a wrapper function for "usb_hs_bandwidth_adjust()". |
---|
1835 | *------------------------------------------------------------------------*/ |
---|
1836 | void |
---|
1837 | usb_hs_bandwidth_alloc(struct usb_xfer *xfer) |
---|
1838 | { |
---|
1839 | struct usb_device *udev; |
---|
1840 | uint8_t slot; |
---|
1841 | uint8_t mask; |
---|
1842 | uint8_t speed; |
---|
1843 | |
---|
1844 | udev = xfer->xroot->udev; |
---|
1845 | |
---|
1846 | if (udev->flags.usb_mode != USB_MODE_HOST) |
---|
1847 | return; /* not supported */ |
---|
1848 | |
---|
1849 | xfer->endpoint->refcount_bw++; |
---|
1850 | if (xfer->endpoint->refcount_bw != 1) |
---|
1851 | return; /* already allocated */ |
---|
1852 | |
---|
1853 | speed = usbd_get_speed(udev); |
---|
1854 | |
---|
1855 | switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) { |
---|
1856 | case UE_INTERRUPT: |
---|
1857 | /* allocate a microframe slot */ |
---|
1858 | |
---|
1859 | mask = 0x01; |
---|
1860 | slot = usb_hs_bandwidth_adjust(udev, |
---|
1861 | xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX, mask); |
---|
1862 | |
---|
1863 | xfer->endpoint->usb_uframe = slot; |
---|
1864 | xfer->endpoint->usb_smask = mask << slot; |
---|
1865 | |
---|
1866 | if ((speed != USB_SPEED_FULL) && |
---|
1867 | (speed != USB_SPEED_LOW)) { |
---|
1868 | xfer->endpoint->usb_cmask = 0x00 ; |
---|
1869 | } else { |
---|
1870 | xfer->endpoint->usb_cmask = (-(0x04 << slot)) & 0xFE; |
---|
1871 | } |
---|
1872 | break; |
---|
1873 | |
---|
1874 | case UE_ISOCHRONOUS: |
---|
1875 | switch (usbd_xfer_get_fps_shift(xfer)) { |
---|
1876 | case 0: |
---|
1877 | mask = 0xFF; |
---|
1878 | break; |
---|
1879 | case 1: |
---|
1880 | mask = 0x55; |
---|
1881 | break; |
---|
1882 | case 2: |
---|
1883 | mask = 0x11; |
---|
1884 | break; |
---|
1885 | default: |
---|
1886 | mask = 0x01; |
---|
1887 | break; |
---|
1888 | } |
---|
1889 | |
---|
1890 | /* allocate a microframe multi-slot */ |
---|
1891 | |
---|
1892 | slot = usb_hs_bandwidth_adjust(udev, |
---|
1893 | xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX, mask); |
---|
1894 | |
---|
1895 | xfer->endpoint->usb_uframe = slot; |
---|
1896 | xfer->endpoint->usb_cmask = 0; |
---|
1897 | xfer->endpoint->usb_smask = mask << slot; |
---|
1898 | break; |
---|
1899 | |
---|
1900 | default: |
---|
1901 | xfer->endpoint->usb_uframe = 0; |
---|
1902 | xfer->endpoint->usb_cmask = 0; |
---|
1903 | xfer->endpoint->usb_smask = 0; |
---|
1904 | break; |
---|
1905 | } |
---|
1906 | |
---|
1907 | DPRINTFN(11, "slot=%d, mask=0x%02x\n", |
---|
1908 | xfer->endpoint->usb_uframe, |
---|
1909 | xfer->endpoint->usb_smask >> xfer->endpoint->usb_uframe); |
---|
1910 | } |
---|
1911 | |
---|
1912 | /*------------------------------------------------------------------------* |
---|
1913 | * usb_hs_bandwidth_free |
---|
1914 | * |
---|
1915 | * This function is a wrapper function for "usb_hs_bandwidth_adjust()". |
---|
1916 | *------------------------------------------------------------------------*/ |
---|
1917 | void |
---|
1918 | usb_hs_bandwidth_free(struct usb_xfer *xfer) |
---|
1919 | { |
---|
1920 | struct usb_device *udev; |
---|
1921 | uint8_t slot; |
---|
1922 | uint8_t mask; |
---|
1923 | |
---|
1924 | udev = xfer->xroot->udev; |
---|
1925 | |
---|
1926 | if (udev->flags.usb_mode != USB_MODE_HOST) |
---|
1927 | return; /* not supported */ |
---|
1928 | |
---|
1929 | xfer->endpoint->refcount_bw--; |
---|
1930 | if (xfer->endpoint->refcount_bw != 0) |
---|
1931 | return; /* still allocated */ |
---|
1932 | |
---|
1933 | switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) { |
---|
1934 | case UE_INTERRUPT: |
---|
1935 | case UE_ISOCHRONOUS: |
---|
1936 | |
---|
1937 | slot = xfer->endpoint->usb_uframe; |
---|
1938 | mask = xfer->endpoint->usb_smask; |
---|
1939 | |
---|
1940 | /* free microframe slot(s): */ |
---|
1941 | usb_hs_bandwidth_adjust(udev, |
---|
1942 | -xfer->max_frame_size, slot, mask >> slot); |
---|
1943 | |
---|
1944 | DPRINTFN(11, "slot=%d, mask=0x%02x\n", |
---|
1945 | slot, mask >> slot); |
---|
1946 | |
---|
1947 | xfer->endpoint->usb_uframe = 0; |
---|
1948 | xfer->endpoint->usb_cmask = 0; |
---|
1949 | xfer->endpoint->usb_smask = 0; |
---|
1950 | break; |
---|
1951 | |
---|
1952 | default: |
---|
1953 | break; |
---|
1954 | } |
---|
1955 | } |
---|
1956 | |
---|
1957 | /*------------------------------------------------------------------------* |
---|
1958 | * usb_isoc_time_expand |
---|
1959 | * |
---|
1960 | * This function will expand the time counter from 7-bit to 16-bit. |
---|
1961 | * |
---|
1962 | * Returns: |
---|
1963 | * 16-bit isochronous time counter. |
---|
1964 | *------------------------------------------------------------------------*/ |
---|
1965 | uint16_t |
---|
1966 | usb_isoc_time_expand(struct usb_bus *bus, uint16_t isoc_time_curr) |
---|
1967 | { |
---|
1968 | uint16_t rem; |
---|
1969 | |
---|
1970 | USB_BUS_LOCK_ASSERT(bus, MA_OWNED); |
---|
1971 | |
---|
1972 | rem = bus->isoc_time_last & (USB_ISOC_TIME_MAX - 1); |
---|
1973 | |
---|
1974 | isoc_time_curr &= (USB_ISOC_TIME_MAX - 1); |
---|
1975 | |
---|
1976 | if (isoc_time_curr < rem) { |
---|
1977 | /* the time counter wrapped around */ |
---|
1978 | bus->isoc_time_last += USB_ISOC_TIME_MAX; |
---|
1979 | } |
---|
1980 | /* update the remainder */ |
---|
1981 | |
---|
1982 | bus->isoc_time_last &= ~(USB_ISOC_TIME_MAX - 1); |
---|
1983 | bus->isoc_time_last |= isoc_time_curr; |
---|
1984 | |
---|
1985 | return (bus->isoc_time_last); |
---|
1986 | } |
---|
1987 | |
---|
1988 | /*------------------------------------------------------------------------* |
---|
1989 | * usbd_fs_isoc_schedule_alloc_slot |
---|
1990 | * |
---|
1991 | * This function will allocate bandwidth for an isochronous FULL speed |
---|
1992 | * transaction in the FULL speed schedule. |
---|
1993 | * |
---|
1994 | * Returns: |
---|
1995 | * <8: Success |
---|
1996 | * Else: Error |
---|
1997 | *------------------------------------------------------------------------*/ |
---|
1998 | #if USB_HAVE_TT_SUPPORT |
---|
1999 | uint8_t |
---|
2000 | usbd_fs_isoc_schedule_alloc_slot(struct usb_xfer *isoc_xfer, uint16_t isoc_time) |
---|
2001 | { |
---|
2002 | struct usb_xfer *xfer; |
---|
2003 | struct usb_xfer *pipe_xfer; |
---|
2004 | struct usb_bus *bus; |
---|
2005 | usb_frlength_t len; |
---|
2006 | usb_frlength_t data_len; |
---|
2007 | uint16_t delta; |
---|
2008 | uint16_t slot; |
---|
2009 | uint8_t retval; |
---|
2010 | |
---|
2011 | data_len = 0; |
---|
2012 | slot = 0; |
---|
2013 | |
---|
2014 | bus = isoc_xfer->xroot->bus; |
---|
2015 | |
---|
2016 | TAILQ_FOREACH(xfer, &bus->intr_q.head, wait_entry) { |
---|
2017 | |
---|
2018 | /* skip self, if any */ |
---|
2019 | |
---|
2020 | if (xfer == isoc_xfer) |
---|
2021 | continue; |
---|
2022 | |
---|
2023 | /* check if this USB transfer is going through the same TT */ |
---|
2024 | |
---|
2025 | if (xfer->xroot->udev->parent_hs_hub != |
---|
2026 | isoc_xfer->xroot->udev->parent_hs_hub) { |
---|
2027 | continue; |
---|
2028 | } |
---|
2029 | if ((isoc_xfer->xroot->udev->parent_hs_hub-> |
---|
2030 | ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) && |
---|
2031 | (xfer->xroot->udev->hs_port_no != |
---|
2032 | isoc_xfer->xroot->udev->hs_port_no)) { |
---|
2033 | continue; |
---|
2034 | } |
---|
2035 | if (xfer->endpoint->methods != isoc_xfer->endpoint->methods) |
---|
2036 | continue; |
---|
2037 | |
---|
2038 | /* check if isoc_time is part of this transfer */ |
---|
2039 | |
---|
2040 | delta = xfer->isoc_time_complete - isoc_time; |
---|
2041 | if (delta > 0 && delta <= xfer->nframes) { |
---|
2042 | delta = xfer->nframes - delta; |
---|
2043 | |
---|
2044 | len = xfer->frlengths[delta]; |
---|
2045 | len += 8; |
---|
2046 | len *= 7; |
---|
2047 | len /= 6; |
---|
2048 | |
---|
2049 | data_len += len; |
---|
2050 | } |
---|
2051 | |
---|
2052 | /* check double buffered transfers */ |
---|
2053 | |
---|
2054 | TAILQ_FOREACH(pipe_xfer, &xfer->endpoint->endpoint_q.head, |
---|
2055 | wait_entry) { |
---|
2056 | |
---|
2057 | /* skip self, if any */ |
---|
2058 | |
---|
2059 | if (pipe_xfer == isoc_xfer) |
---|
2060 | continue; |
---|
2061 | |
---|
2062 | /* check if isoc_time is part of this transfer */ |
---|
2063 | |
---|
2064 | delta = pipe_xfer->isoc_time_complete - isoc_time; |
---|
2065 | if (delta > 0 && delta <= pipe_xfer->nframes) { |
---|
2066 | delta = pipe_xfer->nframes - delta; |
---|
2067 | |
---|
2068 | len = pipe_xfer->frlengths[delta]; |
---|
2069 | len += 8; |
---|
2070 | len *= 7; |
---|
2071 | len /= 6; |
---|
2072 | |
---|
2073 | data_len += len; |
---|
2074 | } |
---|
2075 | } |
---|
2076 | } |
---|
2077 | |
---|
2078 | while (data_len >= USB_FS_BYTES_PER_HS_UFRAME) { |
---|
2079 | data_len -= USB_FS_BYTES_PER_HS_UFRAME; |
---|
2080 | slot++; |
---|
2081 | } |
---|
2082 | |
---|
2083 | /* check for overflow */ |
---|
2084 | |
---|
2085 | if (slot >= USB_FS_ISOC_UFRAME_MAX) |
---|
2086 | return (255); |
---|
2087 | |
---|
2088 | retval = slot; |
---|
2089 | |
---|
2090 | delta = isoc_xfer->isoc_time_complete - isoc_time; |
---|
2091 | if (delta > 0 && delta <= isoc_xfer->nframes) { |
---|
2092 | delta = isoc_xfer->nframes - delta; |
---|
2093 | |
---|
2094 | len = isoc_xfer->frlengths[delta]; |
---|
2095 | len += 8; |
---|
2096 | len *= 7; |
---|
2097 | len /= 6; |
---|
2098 | |
---|
2099 | data_len += len; |
---|
2100 | } |
---|
2101 | |
---|
2102 | while (data_len >= USB_FS_BYTES_PER_HS_UFRAME) { |
---|
2103 | data_len -= USB_FS_BYTES_PER_HS_UFRAME; |
---|
2104 | slot++; |
---|
2105 | } |
---|
2106 | |
---|
2107 | /* check for overflow */ |
---|
2108 | |
---|
2109 | if (slot >= USB_FS_ISOC_UFRAME_MAX) |
---|
2110 | return (255); |
---|
2111 | |
---|
2112 | return (retval); |
---|
2113 | } |
---|
2114 | #endif |
---|
2115 | |
---|
2116 | /*------------------------------------------------------------------------* |
---|
2117 | * usb_bus_port_get_device |
---|
2118 | * |
---|
2119 | * This function is NULL safe. |
---|
2120 | *------------------------------------------------------------------------*/ |
---|
2121 | struct usb_device * |
---|
2122 | usb_bus_port_get_device(struct usb_bus *bus, struct usb_port *up) |
---|
2123 | { |
---|
2124 | if ((bus == NULL) || (up == NULL)) { |
---|
2125 | /* be NULL safe */ |
---|
2126 | return (NULL); |
---|
2127 | } |
---|
2128 | if (up->device_index == 0) { |
---|
2129 | /* nothing to do */ |
---|
2130 | return (NULL); |
---|
2131 | } |
---|
2132 | return (bus->devices[up->device_index]); |
---|
2133 | } |
---|
2134 | |
---|
2135 | /*------------------------------------------------------------------------* |
---|
2136 | * usb_bus_port_set_device |
---|
2137 | * |
---|
2138 | * This function is NULL safe. |
---|
2139 | *------------------------------------------------------------------------*/ |
---|
2140 | void |
---|
2141 | usb_bus_port_set_device(struct usb_bus *bus, struct usb_port *up, |
---|
2142 | struct usb_device *udev, uint8_t device_index) |
---|
2143 | { |
---|
2144 | if (bus == NULL) { |
---|
2145 | /* be NULL safe */ |
---|
2146 | return; |
---|
2147 | } |
---|
2148 | /* |
---|
2149 | * There is only one case where we don't |
---|
2150 | * have an USB port, and that is the Root Hub! |
---|
2151 | */ |
---|
2152 | if (up) { |
---|
2153 | if (udev) { |
---|
2154 | up->device_index = device_index; |
---|
2155 | } else { |
---|
2156 | device_index = up->device_index; |
---|
2157 | up->device_index = 0; |
---|
2158 | } |
---|
2159 | } |
---|
2160 | /* |
---|
2161 | * Make relationships to our new device |
---|
2162 | */ |
---|
2163 | if (device_index != 0) { |
---|
2164 | #if USB_HAVE_UGEN |
---|
2165 | mtx_lock(&usb_ref_lock); |
---|
2166 | #endif |
---|
2167 | bus->devices[device_index] = udev; |
---|
2168 | #if USB_HAVE_UGEN |
---|
2169 | mtx_unlock(&usb_ref_lock); |
---|
2170 | #endif |
---|
2171 | } |
---|
2172 | /* |
---|
2173 | * Debug print |
---|
2174 | */ |
---|
2175 | DPRINTFN(2, "bus %p devices[%u] = %p\n", bus, device_index, udev); |
---|
2176 | } |
---|
2177 | |
---|
2178 | /*------------------------------------------------------------------------* |
---|
2179 | * usb_needs_explore |
---|
2180 | * |
---|
2181 | * This functions is called when the USB event thread needs to run. |
---|
2182 | *------------------------------------------------------------------------*/ |
---|
2183 | void |
---|
2184 | usb_needs_explore(struct usb_bus *bus, uint8_t do_probe) |
---|
2185 | { |
---|
2186 | uint8_t do_unlock; |
---|
2187 | |
---|
2188 | DPRINTF("\n"); |
---|
2189 | |
---|
2190 | if (bus == NULL) { |
---|
2191 | DPRINTF("No bus pointer!\n"); |
---|
2192 | return; |
---|
2193 | } |
---|
2194 | if ((bus->devices == NULL) || |
---|
2195 | (bus->devices[USB_ROOT_HUB_ADDR] == NULL)) { |
---|
2196 | DPRINTF("No root HUB\n"); |
---|
2197 | return; |
---|
2198 | } |
---|
2199 | if (mtx_owned(&bus->bus_mtx)) { |
---|
2200 | do_unlock = 0; |
---|
2201 | } else { |
---|
2202 | USB_BUS_LOCK(bus); |
---|
2203 | do_unlock = 1; |
---|
2204 | } |
---|
2205 | if (do_probe) { |
---|
2206 | bus->do_probe = 1; |
---|
2207 | } |
---|
2208 | if (usb_proc_msignal(&bus->explore_proc, |
---|
2209 | &bus->explore_msg[0], &bus->explore_msg[1])) { |
---|
2210 | /* ignore */ |
---|
2211 | } |
---|
2212 | if (do_unlock) { |
---|
2213 | USB_BUS_UNLOCK(bus); |
---|
2214 | } |
---|
2215 | } |
---|
2216 | |
---|
2217 | /*------------------------------------------------------------------------* |
---|
2218 | * usb_needs_explore_all |
---|
2219 | * |
---|
2220 | * This function is called whenever a new driver is loaded and will |
---|
2221 | * cause that all USB busses are re-explored. |
---|
2222 | *------------------------------------------------------------------------*/ |
---|
2223 | void |
---|
2224 | usb_needs_explore_all(void) |
---|
2225 | { |
---|
2226 | struct usb_bus *bus; |
---|
2227 | devclass_t dc; |
---|
2228 | device_t dev; |
---|
2229 | int max; |
---|
2230 | |
---|
2231 | DPRINTFN(3, "\n"); |
---|
2232 | |
---|
2233 | dc = usb_devclass_ptr; |
---|
2234 | if (dc == NULL) { |
---|
2235 | DPRINTFN(0, "no devclass\n"); |
---|
2236 | return; |
---|
2237 | } |
---|
2238 | /* |
---|
2239 | * Explore all USB busses in parallell. |
---|
2240 | */ |
---|
2241 | max = devclass_get_maxunit(dc); |
---|
2242 | while (max >= 0) { |
---|
2243 | dev = devclass_get_device(dc, max); |
---|
2244 | if (dev) { |
---|
2245 | bus = device_get_softc(dev); |
---|
2246 | if (bus) { |
---|
2247 | usb_needs_explore(bus, 1); |
---|
2248 | } |
---|
2249 | } |
---|
2250 | max--; |
---|
2251 | } |
---|
2252 | } |
---|
2253 | |
---|
2254 | /*------------------------------------------------------------------------* |
---|
2255 | * usb_bus_power_update |
---|
2256 | * |
---|
2257 | * This function will ensure that all USB devices on the given bus are |
---|
2258 | * properly suspended or resumed according to the device transfer |
---|
2259 | * state. |
---|
2260 | *------------------------------------------------------------------------*/ |
---|
2261 | #if USB_HAVE_POWERD |
---|
2262 | void |
---|
2263 | usb_bus_power_update(struct usb_bus *bus) |
---|
2264 | { |
---|
2265 | usb_needs_explore(bus, 0 /* no probe */ ); |
---|
2266 | } |
---|
2267 | #endif |
---|
2268 | |
---|
2269 | /*------------------------------------------------------------------------* |
---|
2270 | * usbd_transfer_power_ref |
---|
2271 | * |
---|
2272 | * This function will modify the power save reference counts and |
---|
2273 | * wakeup the USB device associated with the given USB transfer, if |
---|
2274 | * needed. |
---|
2275 | *------------------------------------------------------------------------*/ |
---|
2276 | #if USB_HAVE_POWERD |
---|
2277 | void |
---|
2278 | usbd_transfer_power_ref(struct usb_xfer *xfer, int val) |
---|
2279 | { |
---|
2280 | static const usb_power_mask_t power_mask[4] = { |
---|
2281 | [UE_CONTROL] = USB_HW_POWER_CONTROL, |
---|
2282 | [UE_BULK] = USB_HW_POWER_BULK, |
---|
2283 | [UE_INTERRUPT] = USB_HW_POWER_INTERRUPT, |
---|
2284 | [UE_ISOCHRONOUS] = USB_HW_POWER_ISOC, |
---|
2285 | }; |
---|
2286 | struct usb_device *udev; |
---|
2287 | uint8_t needs_explore; |
---|
2288 | uint8_t needs_hw_power; |
---|
2289 | uint8_t xfer_type; |
---|
2290 | |
---|
2291 | udev = xfer->xroot->udev; |
---|
2292 | |
---|
2293 | if (udev->device_index == USB_ROOT_HUB_ADDR) { |
---|
2294 | /* no power save for root HUB */ |
---|
2295 | return; |
---|
2296 | } |
---|
2297 | USB_BUS_LOCK(udev->bus); |
---|
2298 | |
---|
2299 | xfer_type = xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE; |
---|
2300 | |
---|
2301 | udev->pwr_save.last_xfer_time = ticks; |
---|
2302 | udev->pwr_save.type_refs[xfer_type] += val; |
---|
2303 | |
---|
2304 | if (xfer->flags_int.control_xfr) { |
---|
2305 | udev->pwr_save.read_refs += val; |
---|
2306 | if (xfer->flags_int.usb_mode == USB_MODE_HOST) { |
---|
2307 | /* |
---|
2308 | * It is not allowed to suspend during a |
---|
2309 | * control transfer: |
---|
2310 | */ |
---|
2311 | udev->pwr_save.write_refs += val; |
---|
2312 | } |
---|
2313 | } else if (USB_GET_DATA_ISREAD(xfer)) { |
---|
2314 | udev->pwr_save.read_refs += val; |
---|
2315 | } else { |
---|
2316 | udev->pwr_save.write_refs += val; |
---|
2317 | } |
---|
2318 | |
---|
2319 | if (val > 0) { |
---|
2320 | if (udev->flags.self_suspended) |
---|
2321 | needs_explore = usb_peer_should_wakeup(udev); |
---|
2322 | else |
---|
2323 | needs_explore = 0; |
---|
2324 | |
---|
2325 | if (!(udev->bus->hw_power_state & power_mask[xfer_type])) { |
---|
2326 | DPRINTF("Adding type %u to power state\n", xfer_type); |
---|
2327 | udev->bus->hw_power_state |= power_mask[xfer_type]; |
---|
2328 | needs_hw_power = 1; |
---|
2329 | } else { |
---|
2330 | needs_hw_power = 0; |
---|
2331 | } |
---|
2332 | } else { |
---|
2333 | needs_explore = 0; |
---|
2334 | needs_hw_power = 0; |
---|
2335 | } |
---|
2336 | |
---|
2337 | USB_BUS_UNLOCK(udev->bus); |
---|
2338 | |
---|
2339 | if (needs_explore) { |
---|
2340 | DPRINTF("update\n"); |
---|
2341 | usb_bus_power_update(udev->bus); |
---|
2342 | } else if (needs_hw_power) { |
---|
2343 | DPRINTF("needs power\n"); |
---|
2344 | if (udev->bus->methods->set_hw_power != NULL) { |
---|
2345 | (udev->bus->methods->set_hw_power) (udev->bus); |
---|
2346 | } |
---|
2347 | } |
---|
2348 | } |
---|
2349 | #endif |
---|
2350 | |
---|
2351 | /*------------------------------------------------------------------------* |
---|
2352 | * usb_peer_should_wakeup |
---|
2353 | * |
---|
2354 | * This function returns non-zero if the current device should wake up. |
---|
2355 | *------------------------------------------------------------------------*/ |
---|
2356 | static uint8_t |
---|
2357 | usb_peer_should_wakeup(struct usb_device *udev) |
---|
2358 | { |
---|
2359 | return (((udev->power_mode == USB_POWER_MODE_ON) && |
---|
2360 | (udev->flags.usb_mode == USB_MODE_HOST)) || |
---|
2361 | (udev->driver_added_refcount != udev->bus->driver_added_refcount) || |
---|
2362 | (udev->re_enumerate_wait != USB_RE_ENUM_DONE) || |
---|
2363 | (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) || |
---|
2364 | (udev->pwr_save.write_refs != 0) || |
---|
2365 | ((udev->pwr_save.read_refs != 0) && |
---|
2366 | (udev->flags.usb_mode == USB_MODE_HOST) && |
---|
2367 | (usb_peer_can_wakeup(udev) == 0))); |
---|
2368 | } |
---|
2369 | |
---|
2370 | /*------------------------------------------------------------------------* |
---|
2371 | * usb_bus_powerd |
---|
2372 | * |
---|
2373 | * This function implements the USB power daemon and is called |
---|
2374 | * regularly from the USB explore thread. |
---|
2375 | *------------------------------------------------------------------------*/ |
---|
2376 | #if USB_HAVE_POWERD |
---|
2377 | void |
---|
2378 | usb_bus_powerd(struct usb_bus *bus) |
---|
2379 | { |
---|
2380 | struct usb_device *udev; |
---|
2381 | usb_ticks_t temp; |
---|
2382 | usb_ticks_t limit; |
---|
2383 | usb_ticks_t mintime; |
---|
2384 | usb_size_t type_refs[5]; |
---|
2385 | uint8_t x; |
---|
2386 | |
---|
2387 | limit = usb_power_timeout; |
---|
2388 | if (limit == 0) |
---|
2389 | limit = hz; |
---|
2390 | else if (limit > 255) |
---|
2391 | limit = 255 * hz; |
---|
2392 | else |
---|
2393 | limit = limit * hz; |
---|
2394 | |
---|
2395 | DPRINTF("bus=%p\n", bus); |
---|
2396 | |
---|
2397 | USB_BUS_LOCK(bus); |
---|
2398 | |
---|
2399 | /* |
---|
2400 | * The root HUB device is never suspended |
---|
2401 | * and we simply skip it. |
---|
2402 | */ |
---|
2403 | for (x = USB_ROOT_HUB_ADDR + 1; |
---|
2404 | x != bus->devices_max; x++) { |
---|
2405 | |
---|
2406 | udev = bus->devices[x]; |
---|
2407 | if (udev == NULL) |
---|
2408 | continue; |
---|
2409 | |
---|
2410 | temp = ticks - udev->pwr_save.last_xfer_time; |
---|
2411 | |
---|
2412 | if (usb_peer_should_wakeup(udev)) { |
---|
2413 | /* check if we are suspended */ |
---|
2414 | if (udev->flags.self_suspended != 0) { |
---|
2415 | USB_BUS_UNLOCK(bus); |
---|
2416 | usb_dev_resume_peer(udev); |
---|
2417 | USB_BUS_LOCK(bus); |
---|
2418 | } |
---|
2419 | } else if ((temp >= limit) && |
---|
2420 | (udev->flags.usb_mode == USB_MODE_HOST) && |
---|
2421 | (udev->flags.self_suspended == 0)) { |
---|
2422 | /* try to do suspend */ |
---|
2423 | |
---|
2424 | USB_BUS_UNLOCK(bus); |
---|
2425 | usb_dev_suspend_peer(udev); |
---|
2426 | USB_BUS_LOCK(bus); |
---|
2427 | } |
---|
2428 | } |
---|
2429 | |
---|
2430 | /* reset counters */ |
---|
2431 | |
---|
2432 | mintime = (usb_ticks_t)-1; |
---|
2433 | type_refs[0] = 0; |
---|
2434 | type_refs[1] = 0; |
---|
2435 | type_refs[2] = 0; |
---|
2436 | type_refs[3] = 0; |
---|
2437 | type_refs[4] = 0; |
---|
2438 | |
---|
2439 | /* Re-loop all the devices to get the actual state */ |
---|
2440 | |
---|
2441 | for (x = USB_ROOT_HUB_ADDR + 1; |
---|
2442 | x != bus->devices_max; x++) { |
---|
2443 | |
---|
2444 | udev = bus->devices[x]; |
---|
2445 | if (udev == NULL) |
---|
2446 | continue; |
---|
2447 | |
---|
2448 | /* we found a non-Root-Hub USB device */ |
---|
2449 | type_refs[4] += 1; |
---|
2450 | |
---|
2451 | /* "last_xfer_time" can be updated by a resume */ |
---|
2452 | temp = ticks - udev->pwr_save.last_xfer_time; |
---|
2453 | |
---|
2454 | /* |
---|
2455 | * Compute minimum time since last transfer for the complete |
---|
2456 | * bus: |
---|
2457 | */ |
---|
2458 | if (temp < mintime) |
---|
2459 | mintime = temp; |
---|
2460 | |
---|
2461 | if (udev->flags.self_suspended == 0) { |
---|
2462 | type_refs[0] += udev->pwr_save.type_refs[0]; |
---|
2463 | type_refs[1] += udev->pwr_save.type_refs[1]; |
---|
2464 | type_refs[2] += udev->pwr_save.type_refs[2]; |
---|
2465 | type_refs[3] += udev->pwr_save.type_refs[3]; |
---|
2466 | } |
---|
2467 | } |
---|
2468 | |
---|
2469 | if (mintime >= (usb_ticks_t)(1 * hz)) { |
---|
2470 | /* recompute power masks */ |
---|
2471 | DPRINTF("Recomputing power masks\n"); |
---|
2472 | bus->hw_power_state = 0; |
---|
2473 | if (type_refs[UE_CONTROL] != 0) |
---|
2474 | bus->hw_power_state |= USB_HW_POWER_CONTROL; |
---|
2475 | if (type_refs[UE_BULK] != 0) |
---|
2476 | bus->hw_power_state |= USB_HW_POWER_BULK; |
---|
2477 | if (type_refs[UE_INTERRUPT] != 0) |
---|
2478 | bus->hw_power_state |= USB_HW_POWER_INTERRUPT; |
---|
2479 | if (type_refs[UE_ISOCHRONOUS] != 0) |
---|
2480 | bus->hw_power_state |= USB_HW_POWER_ISOC; |
---|
2481 | if (type_refs[4] != 0) |
---|
2482 | bus->hw_power_state |= USB_HW_POWER_NON_ROOT_HUB; |
---|
2483 | } |
---|
2484 | USB_BUS_UNLOCK(bus); |
---|
2485 | |
---|
2486 | if (bus->methods->set_hw_power != NULL) { |
---|
2487 | /* always update hardware power! */ |
---|
2488 | (bus->methods->set_hw_power) (bus); |
---|
2489 | } |
---|
2490 | return; |
---|
2491 | } |
---|
2492 | #endif |
---|
2493 | |
---|
2494 | /*------------------------------------------------------------------------* |
---|
2495 | * usb_dev_resume_peer |
---|
2496 | * |
---|
2497 | * This function will resume an USB peer and do the required USB |
---|
2498 | * signalling to get an USB device out of the suspended state. |
---|
2499 | *------------------------------------------------------------------------*/ |
---|
2500 | static void |
---|
2501 | usb_dev_resume_peer(struct usb_device *udev) |
---|
2502 | { |
---|
2503 | struct usb_bus *bus; |
---|
2504 | int err; |
---|
2505 | |
---|
2506 | /* be NULL safe */ |
---|
2507 | if (udev == NULL) |
---|
2508 | return; |
---|
2509 | |
---|
2510 | /* check if already resumed */ |
---|
2511 | if (udev->flags.self_suspended == 0) |
---|
2512 | return; |
---|
2513 | |
---|
2514 | /* we need a parent HUB to do resume */ |
---|
2515 | if (udev->parent_hub == NULL) |
---|
2516 | return; |
---|
2517 | |
---|
2518 | DPRINTF("udev=%p\n", udev); |
---|
2519 | |
---|
2520 | if ((udev->flags.usb_mode == USB_MODE_DEVICE) && |
---|
2521 | (udev->flags.remote_wakeup == 0)) { |
---|
2522 | /* |
---|
2523 | * If the host did not set the remote wakeup feature, we can |
---|
2524 | * not wake it up either! |
---|
2525 | */ |
---|
2526 | DPRINTF("remote wakeup is not set!\n"); |
---|
2527 | return; |
---|
2528 | } |
---|
2529 | /* get bus pointer */ |
---|
2530 | bus = udev->bus; |
---|
2531 | |
---|
2532 | /* resume parent hub first */ |
---|
2533 | usb_dev_resume_peer(udev->parent_hub); |
---|
2534 | |
---|
2535 | /* reduce chance of instant resume failure by waiting a little bit */ |
---|
2536 | usb_pause_mtx(NULL, USB_MS_TO_TICKS(20)); |
---|
2537 | |
---|
2538 | if (usb_device_20_compatible(udev)) { |
---|
2539 | /* resume current port (Valid in Host and Device Mode) */ |
---|
2540 | err = usbd_req_clear_port_feature(udev->parent_hub, |
---|
2541 | NULL, udev->port_no, UHF_PORT_SUSPEND); |
---|
2542 | if (err) { |
---|
2543 | DPRINTFN(0, "Resuming port failed\n"); |
---|
2544 | return; |
---|
2545 | } |
---|
2546 | } else { |
---|
2547 | /* resume current port (Valid in Host and Device Mode) */ |
---|
2548 | err = usbd_req_set_port_link_state(udev->parent_hub, |
---|
2549 | NULL, udev->port_no, UPS_PORT_LS_U0); |
---|
2550 | if (err) { |
---|
2551 | DPRINTFN(0, "Resuming port failed\n"); |
---|
2552 | return; |
---|
2553 | } |
---|
2554 | } |
---|
2555 | |
---|
2556 | /* resume settle time */ |
---|
2557 | usb_pause_mtx(NULL, USB_MS_TO_TICKS(usb_port_resume_delay)); |
---|
2558 | |
---|
2559 | if (bus->methods->device_resume != NULL) { |
---|
2560 | /* resume USB device on the USB controller */ |
---|
2561 | (bus->methods->device_resume) (udev); |
---|
2562 | } |
---|
2563 | USB_BUS_LOCK(bus); |
---|
2564 | /* set that this device is now resumed */ |
---|
2565 | udev->flags.self_suspended = 0; |
---|
2566 | #if USB_HAVE_POWERD |
---|
2567 | /* make sure that we don't go into suspend right away */ |
---|
2568 | udev->pwr_save.last_xfer_time = ticks; |
---|
2569 | |
---|
2570 | /* make sure the needed power masks are on */ |
---|
2571 | if (udev->pwr_save.type_refs[UE_CONTROL] != 0) |
---|
2572 | bus->hw_power_state |= USB_HW_POWER_CONTROL; |
---|
2573 | if (udev->pwr_save.type_refs[UE_BULK] != 0) |
---|
2574 | bus->hw_power_state |= USB_HW_POWER_BULK; |
---|
2575 | if (udev->pwr_save.type_refs[UE_INTERRUPT] != 0) |
---|
2576 | bus->hw_power_state |= USB_HW_POWER_INTERRUPT; |
---|
2577 | if (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) |
---|
2578 | bus->hw_power_state |= USB_HW_POWER_ISOC; |
---|
2579 | #endif |
---|
2580 | USB_BUS_UNLOCK(bus); |
---|
2581 | |
---|
2582 | if (bus->methods->set_hw_power != NULL) { |
---|
2583 | /* always update hardware power! */ |
---|
2584 | (bus->methods->set_hw_power) (bus); |
---|
2585 | } |
---|
2586 | |
---|
2587 | usbd_sr_lock(udev); |
---|
2588 | |
---|
2589 | /* notify all sub-devices about resume */ |
---|
2590 | err = usb_suspend_resume(udev, 0); |
---|
2591 | |
---|
2592 | usbd_sr_unlock(udev); |
---|
2593 | |
---|
2594 | /* check if peer has wakeup capability */ |
---|
2595 | if (usb_peer_can_wakeup(udev)) { |
---|
2596 | /* clear remote wakeup */ |
---|
2597 | err = usbd_req_clear_device_feature(udev, |
---|
2598 | NULL, UF_DEVICE_REMOTE_WAKEUP); |
---|
2599 | if (err) { |
---|
2600 | DPRINTFN(0, "Clearing device " |
---|
2601 | "remote wakeup failed: %s\n", |
---|
2602 | usbd_errstr(err)); |
---|
2603 | } |
---|
2604 | } |
---|
2605 | } |
---|
2606 | |
---|
2607 | /*------------------------------------------------------------------------* |
---|
2608 | * usb_dev_suspend_peer |
---|
2609 | * |
---|
2610 | * This function will suspend an USB peer and do the required USB |
---|
2611 | * signalling to get an USB device into the suspended state. |
---|
2612 | *------------------------------------------------------------------------*/ |
---|
2613 | static void |
---|
2614 | usb_dev_suspend_peer(struct usb_device *udev) |
---|
2615 | { |
---|
2616 | struct usb_device *child; |
---|
2617 | int err; |
---|
2618 | uint8_t x; |
---|
2619 | uint8_t nports; |
---|
2620 | |
---|
2621 | repeat: |
---|
2622 | /* be NULL safe */ |
---|
2623 | if (udev == NULL) |
---|
2624 | return; |
---|
2625 | |
---|
2626 | /* check if already suspended */ |
---|
2627 | if (udev->flags.self_suspended) |
---|
2628 | return; |
---|
2629 | |
---|
2630 | /* we need a parent HUB to do suspend */ |
---|
2631 | if (udev->parent_hub == NULL) |
---|
2632 | return; |
---|
2633 | |
---|
2634 | DPRINTF("udev=%p\n", udev); |
---|
2635 | |
---|
2636 | /* check if the current device is a HUB */ |
---|
2637 | if (udev->hub != NULL) { |
---|
2638 | nports = udev->hub->nports; |
---|
2639 | |
---|
2640 | /* check if all devices on the HUB are suspended */ |
---|
2641 | for (x = 0; x != nports; x++) { |
---|
2642 | child = usb_bus_port_get_device(udev->bus, |
---|
2643 | udev->hub->ports + x); |
---|
2644 | |
---|
2645 | if (child == NULL) |
---|
2646 | continue; |
---|
2647 | |
---|
2648 | if (child->flags.self_suspended) |
---|
2649 | continue; |
---|
2650 | |
---|
2651 | DPRINTFN(1, "Port %u is busy on the HUB!\n", x + 1); |
---|
2652 | return; |
---|
2653 | } |
---|
2654 | } |
---|
2655 | |
---|
2656 | if (usb_peer_can_wakeup(udev)) { |
---|
2657 | /* |
---|
2658 | * This request needs to be done before we set |
---|
2659 | * "udev->flags.self_suspended": |
---|
2660 | */ |
---|
2661 | |
---|
2662 | /* allow device to do remote wakeup */ |
---|
2663 | err = usbd_req_set_device_feature(udev, |
---|
2664 | NULL, UF_DEVICE_REMOTE_WAKEUP); |
---|
2665 | if (err) { |
---|
2666 | DPRINTFN(0, "Setting device " |
---|
2667 | "remote wakeup failed\n"); |
---|
2668 | } |
---|
2669 | } |
---|
2670 | |
---|
2671 | USB_BUS_LOCK(udev->bus); |
---|
2672 | /* |
---|
2673 | * Checking for suspend condition and setting suspended bit |
---|
2674 | * must be atomic! |
---|
2675 | */ |
---|
2676 | err = usb_peer_should_wakeup(udev); |
---|
2677 | if (err == 0) { |
---|
2678 | /* |
---|
2679 | * Set that this device is suspended. This variable |
---|
2680 | * must be set before calling USB controller suspend |
---|
2681 | * callbacks. |
---|
2682 | */ |
---|
2683 | udev->flags.self_suspended = 1; |
---|
2684 | } |
---|
2685 | USB_BUS_UNLOCK(udev->bus); |
---|
2686 | |
---|
2687 | if (err != 0) { |
---|
2688 | if (usb_peer_can_wakeup(udev)) { |
---|
2689 | /* allow device to do remote wakeup */ |
---|
2690 | err = usbd_req_clear_device_feature(udev, |
---|
2691 | NULL, UF_DEVICE_REMOTE_WAKEUP); |
---|
2692 | if (err) { |
---|
2693 | DPRINTFN(0, "Setting device " |
---|
2694 | "remote wakeup failed\n"); |
---|
2695 | } |
---|
2696 | } |
---|
2697 | |
---|
2698 | if (udev->flags.usb_mode == USB_MODE_DEVICE) { |
---|
2699 | /* resume parent HUB first */ |
---|
2700 | usb_dev_resume_peer(udev->parent_hub); |
---|
2701 | |
---|
2702 | /* reduce chance of instant resume failure by waiting a little bit */ |
---|
2703 | usb_pause_mtx(NULL, USB_MS_TO_TICKS(20)); |
---|
2704 | |
---|
2705 | /* resume current port (Valid in Host and Device Mode) */ |
---|
2706 | err = usbd_req_clear_port_feature(udev->parent_hub, |
---|
2707 | NULL, udev->port_no, UHF_PORT_SUSPEND); |
---|
2708 | |
---|
2709 | /* resume settle time */ |
---|
2710 | usb_pause_mtx(NULL, USB_MS_TO_TICKS(usb_port_resume_delay)); |
---|
2711 | } |
---|
2712 | DPRINTF("Suspend was cancelled!\n"); |
---|
2713 | return; |
---|
2714 | } |
---|
2715 | |
---|
2716 | usbd_sr_lock(udev); |
---|
2717 | |
---|
2718 | /* notify all sub-devices about suspend */ |
---|
2719 | err = usb_suspend_resume(udev, 1); |
---|
2720 | |
---|
2721 | usbd_sr_unlock(udev); |
---|
2722 | |
---|
2723 | if (udev->bus->methods->device_suspend != NULL) { |
---|
2724 | usb_timeout_t temp; |
---|
2725 | |
---|
2726 | /* suspend device on the USB controller */ |
---|
2727 | (udev->bus->methods->device_suspend) (udev); |
---|
2728 | |
---|
2729 | /* do DMA delay */ |
---|
2730 | temp = usbd_get_dma_delay(udev); |
---|
2731 | if (temp != 0) |
---|
2732 | usb_pause_mtx(NULL, USB_MS_TO_TICKS(temp)); |
---|
2733 | |
---|
2734 | } |
---|
2735 | |
---|
2736 | if (usb_device_20_compatible(udev)) { |
---|
2737 | /* suspend current port */ |
---|
2738 | err = usbd_req_set_port_feature(udev->parent_hub, |
---|
2739 | NULL, udev->port_no, UHF_PORT_SUSPEND); |
---|
2740 | if (err) { |
---|
2741 | DPRINTFN(0, "Suspending port failed\n"); |
---|
2742 | return; |
---|
2743 | } |
---|
2744 | } else { |
---|
2745 | /* suspend current port */ |
---|
2746 | err = usbd_req_set_port_link_state(udev->parent_hub, |
---|
2747 | NULL, udev->port_no, UPS_PORT_LS_U3); |
---|
2748 | if (err) { |
---|
2749 | DPRINTFN(0, "Suspending port failed\n"); |
---|
2750 | return; |
---|
2751 | } |
---|
2752 | } |
---|
2753 | |
---|
2754 | udev = udev->parent_hub; |
---|
2755 | goto repeat; |
---|
2756 | } |
---|
2757 | |
---|
2758 | /*------------------------------------------------------------------------* |
---|
2759 | * usbd_set_power_mode |
---|
2760 | * |
---|
2761 | * This function will set the power mode, see USB_POWER_MODE_XXX for a |
---|
2762 | * USB device. |
---|
2763 | *------------------------------------------------------------------------*/ |
---|
2764 | void |
---|
2765 | usbd_set_power_mode(struct usb_device *udev, uint8_t power_mode) |
---|
2766 | { |
---|
2767 | /* filter input argument */ |
---|
2768 | if ((power_mode != USB_POWER_MODE_ON) && |
---|
2769 | (power_mode != USB_POWER_MODE_OFF)) |
---|
2770 | power_mode = USB_POWER_MODE_SAVE; |
---|
2771 | |
---|
2772 | power_mode = usbd_filter_power_mode(udev, power_mode); |
---|
2773 | |
---|
2774 | udev->power_mode = power_mode; /* update copy of power mode */ |
---|
2775 | |
---|
2776 | #if USB_HAVE_POWERD |
---|
2777 | usb_bus_power_update(udev->bus); |
---|
2778 | #else |
---|
2779 | usb_needs_explore(udev->bus, 0 /* no probe */ ); |
---|
2780 | #endif |
---|
2781 | } |
---|
2782 | |
---|
2783 | /*------------------------------------------------------------------------* |
---|
2784 | * usbd_filter_power_mode |
---|
2785 | * |
---|
2786 | * This function filters the power mode based on hardware requirements. |
---|
2787 | *------------------------------------------------------------------------*/ |
---|
2788 | uint8_t |
---|
2789 | usbd_filter_power_mode(struct usb_device *udev, uint8_t power_mode) |
---|
2790 | { |
---|
2791 | struct usb_bus_methods *mtod; |
---|
2792 | int8_t temp; |
---|
2793 | |
---|
2794 | mtod = udev->bus->methods; |
---|
2795 | temp = -1; |
---|
2796 | |
---|
2797 | if (mtod->get_power_mode != NULL) |
---|
2798 | (mtod->get_power_mode) (udev, &temp); |
---|
2799 | |
---|
2800 | /* check if we should not filter */ |
---|
2801 | if (temp < 0) |
---|
2802 | return (power_mode); |
---|
2803 | |
---|
2804 | /* use fixed power mode given by hardware driver */ |
---|
2805 | return (temp); |
---|
2806 | } |
---|
2807 | |
---|
2808 | /*------------------------------------------------------------------------* |
---|
2809 | * usbd_start_re_enumerate |
---|
2810 | * |
---|
2811 | * This function starts re-enumeration of the given USB device. This |
---|
2812 | * function does not need to be called BUS-locked. This function does |
---|
2813 | * not wait until the re-enumeration is completed. |
---|
2814 | *------------------------------------------------------------------------*/ |
---|
2815 | void |
---|
2816 | usbd_start_re_enumerate(struct usb_device *udev) |
---|
2817 | { |
---|
2818 | if (udev->re_enumerate_wait == USB_RE_ENUM_DONE) { |
---|
2819 | udev->re_enumerate_wait = USB_RE_ENUM_START; |
---|
2820 | usb_needs_explore(udev->bus, 0); |
---|
2821 | } |
---|
2822 | } |
---|
2823 | |
---|
2824 | /*-----------------------------------------------------------------------* |
---|
2825 | * usbd_start_set_config |
---|
2826 | * |
---|
2827 | * This function starts setting a USB configuration. This function |
---|
2828 | * does not need to be called BUS-locked. This function does not wait |
---|
2829 | * until the set USB configuratino is completed. |
---|
2830 | *------------------------------------------------------------------------*/ |
---|
2831 | usb_error_t |
---|
2832 | usbd_start_set_config(struct usb_device *udev, uint8_t index) |
---|
2833 | { |
---|
2834 | if (udev->re_enumerate_wait == USB_RE_ENUM_DONE) { |
---|
2835 | if (udev->curr_config_index == index) { |
---|
2836 | /* no change needed */ |
---|
2837 | return (0); |
---|
2838 | } |
---|
2839 | udev->next_config_index = index; |
---|
2840 | udev->re_enumerate_wait = USB_RE_ENUM_SET_CONFIG; |
---|
2841 | usb_needs_explore(udev->bus, 0); |
---|
2842 | return (0); |
---|
2843 | } else if (udev->re_enumerate_wait == USB_RE_ENUM_SET_CONFIG) { |
---|
2844 | if (udev->next_config_index == index) { |
---|
2845 | /* no change needed */ |
---|
2846 | return (0); |
---|
2847 | } |
---|
2848 | } |
---|
2849 | return (USB_ERR_PENDING_REQUESTS); |
---|
2850 | } |
---|