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
32 #include "generic_hub.h"
34 /* assume that host_to_device is overwritten if necessary */
35 #define DR_PORT gen_bmRequestType(host_to_device, class_type, other_recp)
36 /* status (and status change) bits */
37 #define PORT_CONNECTION 0x01
38 #define PORT_ENABLE 0x02
39 #define PORT_SUSPEND 0x04
40 #define PORT_OVER_CURRENT 0x08
41 #define PORT_RESET 0x10
42 #define BH_PORT_RESET 0x20
43 #define PORT_LINK_STATE 0x40
44 #define PORT_CONFIG_ERROR 0x80
45 /* feature selectors (for setting / clearing features) */
46 #define SEL_PORT_RESET 0x04
47 #define SEL_PORT_POWER 0x08
48 #define SEL_C_PORT_CONNECTION 0x10
49 #define SEL_C_PORT_ENABLE 0x11
50 #define SEL_C_PORT_SUSPEND 0x12
51 #define SEL_C_PORT_OVER_CURRENT 0x13
52 #define SEL_C_PORT_RESET 0x14
53 #define SEL_C_PORT_LINK_STATE 0x19
54 #define SEL_C_PORT_CONFIG_ERROR 0x1a
55 #define SEL_C_BH_PORT_RESET 0x1d
56 /* request type (USB 3.0 hubs only) */
57 #define SET_HUB_DEPTH 12
60 usb_hub_interrupt_ep(usbdev_t
*const dev
)
63 for (i
= 0; i
< dev
->num_endp
; ++i
) {
64 if (dev
->endpoints
[i
].type
== INTERRUPT
&&
65 dev
->endpoints
[i
].direction
== IN
)
66 return &dev
->endpoints
[i
];
72 usb_hub_port_status_changed(usbdev_t
*const dev
, const int port
)
74 unsigned short buf
[2];
75 int ret
= get_status(dev
, port
, DR_PORT
, sizeof(buf
), buf
);
77 ret
= buf
[1] & PORT_CONNECTION
;
79 clear_feature(dev
, port
, SEL_C_PORT_CONNECTION
,
86 usb_hub_port_connected(usbdev_t
*const dev
, const int port
)
88 unsigned short buf
[2];
89 int ret
= get_status(dev
, port
, DR_PORT
, sizeof(buf
), buf
);
91 ret
= buf
[0] & PORT_CONNECTION
;
96 usb_hub_port_in_reset(usbdev_t
*const dev
, const int port
)
98 unsigned short buf
[2];
99 int ret
= get_status(dev
, port
, DR_PORT
, sizeof(buf
), buf
);
101 ret
= buf
[0] & PORT_RESET
;
106 usb_hub_port_enabled(usbdev_t
*const dev
, const int port
)
108 unsigned short buf
[2];
109 int ret
= get_status(dev
, port
, DR_PORT
, sizeof(buf
), buf
);
111 ret
= buf
[0] & PORT_ENABLE
;
116 usb_hub_port_speed(usbdev_t
*const dev
, const int port
)
118 unsigned short buf
[2];
119 int ret
= get_status(dev
, port
, DR_PORT
, sizeof(buf
), buf
);
120 if (ret
>= 0 && (buf
[0] & PORT_ENABLE
)) {
121 /* SuperSpeed hubs can only have SuperSpeed devices. */
122 if (is_usb_speed_ss(dev
->speed
))
125 /*[bit] 10 9 (USB 2.0 port status word)
131 ret
= (buf
[0] >> 9) & 0x3;
139 usb_hub_enable_port(usbdev_t
*const dev
, const int port
)
141 return set_feature(dev
, port
, SEL_PORT_POWER
, DR_PORT
);
145 usb_hub_start_port_reset(usbdev_t
*const dev
, const int port
)
147 return set_feature(dev
, port
, SEL_PORT_RESET
, DR_PORT
);
150 static void usb_hub_set_hub_depth(usbdev_t
*const dev
)
153 .bmRequestType
= gen_bmRequestType(host_to_device
,
154 class_type
, dev_recp
),
155 .bRequest
= SET_HUB_DEPTH
,
160 usbdev_t
*parent
= dev
;
161 while (parent
->hub
> 0) {
162 parent
= dev
->controller
->devices
[parent
->hub
];
165 int ret
= dev
->controller
->control(dev
, OUT
, sizeof(dr
), &dr
, 0, NULL
);
167 usb_debug("Failed SET_HUB_DEPTH(%d) on hub %d: %d\n",
168 dr
.wValue
, dev
->address
, ret
);
171 static const generic_hub_ops_t usb_hub_ops
= {
172 .hub_status_changed
= NULL
,
173 .port_status_changed
= usb_hub_port_status_changed
,
174 .port_connected
= usb_hub_port_connected
,
175 .port_in_reset
= usb_hub_port_in_reset
,
176 .port_enabled
= usb_hub_port_enabled
,
177 .port_speed
= usb_hub_port_speed
,
178 .enable_port
= usb_hub_enable_port
,
179 .disable_port
= NULL
,
180 .start_port_reset
= usb_hub_start_port_reset
,
181 .reset_port
= generic_hub_resetport
,
184 /* Clear CSC if set and enumerate port if it's connected regardless of change
185 bits. Some broken hubs don't set CSC if already connected during reset. */
187 usb_hub_port_initialize(usbdev_t
*const dev
, const int port
)
189 unsigned short buf
[2];
190 int ret
= get_status(dev
, port
, DR_PORT
, sizeof(buf
), buf
);
193 if (buf
[1] & PORT_CONNECTION
)
194 clear_feature(dev
, port
, SEL_C_PORT_CONNECTION
, DR_PORT
);
195 if (buf
[0] & PORT_CONNECTION
) {
196 usb_debug("usbhub: Port coldplug at %d\n", port
);
197 generic_hub_scanport(dev
, port
);
202 usb_hub_handle_port_change(usbdev_t
*const dev
, const int port
)
204 static const struct {
205 unsigned short change_bit
;
206 unsigned short clear_sel
;
208 { PORT_CONNECTION
, SEL_C_PORT_CONNECTION
},
209 { PORT_ENABLE
, SEL_C_PORT_ENABLE
},
210 { PORT_SUSPEND
, SEL_C_PORT_SUSPEND
},
211 { PORT_OVER_CURRENT
, SEL_C_PORT_OVER_CURRENT
},
212 { PORT_RESET
, SEL_C_PORT_RESET
},
213 { BH_PORT_RESET
, SEL_C_BH_PORT_RESET
},
214 { PORT_LINK_STATE
, SEL_C_PORT_LINK_STATE
},
215 { PORT_CONFIG_ERROR
, SEL_C_PORT_CONFIG_ERROR
},
219 unsigned short checked_bits
= 0;
220 unsigned short buf
[2] = { 0, 0 };
222 ret
= get_status(dev
, port
, DR_PORT
, sizeof(buf
), buf
);
227 * Second word holds the change bits. The interrupt transfer shows
228 * a logical or of these bits, so we have to clear them all.
230 for (i
= 0; i
< ARRAY_SIZE(change_bits
); ++i
) {
231 if (buf
[1] & change_bits
[i
].change_bit
)
232 clear_feature(dev
, port
, change_bits
[i
].clear_sel
, DR_PORT
);
233 checked_bits
|= change_bits
[i
].change_bit
;
235 if (buf
[1] & ~checked_bits
)
236 usb_debug("usbhub: Spurious change bit at port %d\n", port
);
238 /* Now, handle connection changes. */
239 if (buf
[1] & PORT_CONNECTION
) {
240 usb_debug("usbhub: Port change at %d\n", port
);
241 ret
= generic_hub_scanport(dev
, port
);
247 usb_hub_poll(usbdev_t
*const dev
)
249 unsigned int port
, i
;
253 /* First, gather all change bits from finished interrupt transfers. */
254 const size_t port_bytes
= MIN(ARRAY_SIZE(buf
),
255 div_round_up(GEN_HUB(dev
)->num_ports
+ 1, 8));
256 while ((ibuf
= dev
->controller
->poll_intr_queue(GEN_HUB(dev
)->data
))) {
257 for (i
= 0; i
< port_bytes
; ++i
)
261 for (port
= 1; port
<= GEN_HUB(dev
)->num_ports
; ++port
) {
262 /* ports start at bit1; bit0 is hub status change */
263 if (buf
[port
/ 8] & (1 << (port
% 8))) {
264 if (usb_hub_handle_port_change(dev
, port
) < 0)
271 usb_hub_destroy(usbdev_t
*const dev
)
273 endpoint_t
*const intr_ep
= usb_hub_interrupt_ep(dev
);
274 dev
->controller
->destroy_intr_queue(intr_ep
, GEN_HUB(dev
)->data
);
275 generic_hub_destroy(dev
);
279 usb_hub_init(usbdev_t
*const dev
)
281 endpoint_t
*const intr_ep
= usb_hub_interrupt_ep(dev
);
283 usb_debug("usbhub: ERROR: No interrupt-in endpoint found\n");
287 /* Get number of ports from hub descriptor */
288 int type
= is_usb_speed_ss(dev
->speed
) ? 0x2a : 0x29; /* similar enough */
289 hub_descriptor_t desc
; /* won't fit the whole thing, we don't care */
290 if (get_descriptor(dev
, gen_bmRequestType(device_to_host
, class_type
,
291 dev_recp
), type
, 0, &desc
, sizeof(desc
)) != sizeof(desc
)) {
292 usb_debug("get_descriptor(HUB) failed\n");
293 usb_detach_device(dev
->controller
, dev
->address
);
297 if (is_usb_speed_ss(dev
->speed
))
298 usb_hub_set_hub_depth(dev
);
301 * Register interrupt transfer:
302 * one bit per port + one bit for the hub,
303 * 20 transfers in the queue, like our HID driver,
304 * one transfer per 256ms
306 void *const intrq
= dev
->controller
->create_intr_queue(
307 intr_ep
, intr_ep
->maxpacketsize
, 20, 256);
309 usb_detach_device(dev
->controller
, dev
->address
);
314 * Limit the number of ports by the max packet size of
315 * the interrupt endpoint. This shouldn't be necessary
316 * but prevents a potential overflow in usb_hub_poll().
318 const unsigned int num_ports
=
319 MIN(desc
.bNbrPorts
, intr_ep
->maxpacketsize
* 8 - 1);
320 if (generic_hub_init(dev
, num_ports
, &usb_hub_ops
)) {
321 dev
->controller
->destroy_intr_queue(intr_ep
, intrq
);
322 usb_detach_device(dev
->controller
, dev
->address
);
327 for (port
= 1; port
<= num_ports
; ++port
)
328 usb_hub_port_initialize(dev
, port
);
330 GEN_HUB(dev
)->data
= intrq
;
331 dev
->poll
= usb_hub_poll
;
332 dev
->destroy
= usb_hub_destroy
;