3 * Copyright (C) 2013 secunet Security Networks AG
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #include "generic_hub.h"
36 generic_hub_destroy(usbdev_t
*const dev
)
38 generic_hub_t
*const hub
= GEN_HUB(dev
);
42 /* First, detach all devices behind this hub */
44 for (port
= 1; port
<= hub
->num_ports
; ++port
) {
45 if (hub
->ports
[port
] >= 0) {
46 usb_debug("generic_hub: Detachment at port %d\n", port
);
47 usb_detach_device(dev
->controller
, hub
->ports
[port
]);
48 hub
->ports
[port
] = NO_DEV
;
52 /* Disable all ports */
53 if (hub
->ops
->disable_port
) {
54 for (port
= 1; port
<= hub
->num_ports
; ++port
)
55 hub
->ops
->disable_port(dev
, port
);
63 generic_hub_debounce(usbdev_t
*const dev
, const int port
)
65 generic_hub_t
*const hub
= GEN_HUB(dev
);
67 const int step_ms
= 1; /* linux uses 25ms, we're busy anyway */
68 const int at_least_ms
= 100; /* 100ms as in usb20 spec 9.1.2 */
69 const int timeout_ms
= 1500; /* linux uses this value */
73 while (stable_ms
< at_least_ms
&& total_ms
< timeout_ms
) {
76 const int changed
= hub
->ops
->port_status_changed(dev
, port
);
77 const int connected
= hub
->ops
->port_connected(dev
, port
);
78 if (changed
< 0 || connected
< 0)
81 if (!changed
&& connected
) {
84 usb_debug("generic_hub: Unstable connection at %d\n",
90 if (total_ms
>= timeout_ms
)
91 usb_debug("generic_hub: Debouncing timed out at %d\n", port
);
92 return 0; /* ignore timeouts, try to always go on */
96 generic_hub_wait_for_port(usbdev_t
*const dev
, const int port
,
98 int (*const port_op
)(usbdev_t
*, int),
99 int timeout_steps
, const int step_us
)
103 state
= port_op(dev
, port
);
106 else if (!!state
== wait_for
)
107 return timeout_steps
;
110 } while (timeout_steps
);
115 generic_hub_resetport(usbdev_t
*const dev
, const int port
)
117 generic_hub_t
*const hub
= GEN_HUB(dev
);
119 if (hub
->ops
->start_port_reset(dev
, port
) < 0)
122 /* wait for 10ms (usb20 spec 11.5.1.5: reset should take 10 to 20ms) */
125 /* now wait 12ms for the hub to finish the reset */
126 const int ret
= generic_hub_wait_for_port(
127 /* time out after 120 * 100us = 12ms */
128 dev
, port
, 0, hub
->ops
->port_in_reset
, 120, 100);
132 usb_debug("generic_hub: Reset timed out at port %d\n", port
);
134 return 0; /* ignore timeouts, try to always go on */
138 generic_hub_detach_dev(usbdev_t
*const dev
, const int port
)
140 generic_hub_t
*const hub
= GEN_HUB(dev
);
142 usb_detach_device(dev
->controller
, hub
->ports
[port
]);
143 hub
->ports
[port
] = NO_DEV
;
149 generic_hub_attach_dev(usbdev_t
*const dev
, const int port
)
151 generic_hub_t
*const hub
= GEN_HUB(dev
);
153 if (generic_hub_debounce(dev
, port
) < 0)
156 if (hub
->ops
->reset_port
) {
157 if (hub
->ops
->reset_port(dev
, port
) < 0)
160 if (!hub
->ops
->port_connected(dev
, port
)) {
162 "generic_hub: Port %d disconnected after "
163 "reset. Possibly upgraded, rescan required.\n",
168 /* after reset the port will be enabled automatically */
169 const int ret
= generic_hub_wait_for_port(
170 /* time out after 1,000 * 10us = 10ms */
171 dev
, port
, 1, hub
->ops
->port_enabled
, 1000, 10);
175 usb_debug("generic_hub: Port %d still "
176 "disabled after 10ms\n", port
);
179 const usb_speed speed
= hub
->ops
->port_speed(dev
, port
);
181 usb_debug("generic_hub: Success at port %d\n", port
);
182 if (hub
->ops
->reset_port
)
183 mdelay(10); /* Reset recovery time
184 (usb20 spec 7.1.7.5) */
185 hub
->ports
[port
] = usb_attach_device(
186 dev
->controller
, dev
->address
, port
, speed
);
192 generic_hub_scanport(usbdev_t
*const dev
, const int port
)
194 generic_hub_t
*const hub
= GEN_HUB(dev
);
196 if (hub
->ports
[port
] >= 0) {
197 usb_debug("generic_hub: Detachment at port %d\n", port
);
199 const int ret
= generic_hub_detach_dev(dev
, port
);
204 if (hub
->ops
->port_connected(dev
, port
)) {
205 usb_debug("generic_hub: Attachment at port %d\n", port
);
207 return generic_hub_attach_dev(dev
, port
);
214 generic_hub_poll(usbdev_t
*const dev
)
216 generic_hub_t
*const hub
= GEN_HUB(dev
);
220 if (!(dev
->quirks
& USB_QUIRK_HUB_NO_USBSTS_PCD
) &&
221 hub
->ops
->hub_status_changed
&&
222 hub
->ops
->hub_status_changed(dev
) != 1) {
227 for (port
= 1; port
<= hub
->num_ports
; ++port
) {
228 const int ret
= hub
->ops
->port_status_changed(dev
, port
);
231 } else if (ret
== 1) {
232 usb_debug("generic_hub: Port change at %d\n", port
);
233 if (generic_hub_scanport(dev
, port
) < 0)
240 generic_hub_init(usbdev_t
*const dev
, const int num_ports
,
241 const generic_hub_ops_t
*const ops
)
245 dev
->destroy
= generic_hub_destroy
;
246 dev
->poll
= generic_hub_poll
;
247 dev
->data
= malloc(sizeof(generic_hub_t
));
249 usb_debug("generic_hub: ERROR: Out of memory\n");
253 generic_hub_t
*const hub
= GEN_HUB(dev
);
254 hub
->num_ports
= num_ports
;
255 hub
->ports
= malloc(sizeof(*hub
->ports
) * (num_ports
+ 1));
258 usb_debug("generic_hub: ERROR: Out of memory\n");
263 for (port
= 1; port
<= num_ports
; ++port
)
264 hub
->ports
[port
] = NO_DEV
;
266 /* Enable all ports */
267 if (ops
->enable_port
) {
268 for (port
= 1; port
<= num_ports
; ++port
)
269 ops
->enable_port(dev
, port
);
270 /* wait once for all ports */