1 | /* $NetBSD: usbroothub.c,v 1.2 2016/04/23 10:15:32 skrll Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1998, 2004, 2011, 2012 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Lennart Augustsson (lennart@augustsson.net) at |
9 | * Carlstedt Research & Technology, Jared D. McNeill (jmcneill@invisible.ca), |
10 | * Matthew R. Green (mrg@eterna.com.au) and Nick Hudson. |
11 | * |
12 | * Redistribution and use in source and binary forms, with or without |
13 | * modification, are permitted provided that the following conditions |
14 | * are met: |
15 | * 1. Redistributions of source code must retain the above copyright |
16 | * notice, this list of conditions and the following disclaimer. |
17 | * 2. Redistributions in binary form must reproduce the above copyright |
18 | * notice, this list of conditions and the following disclaimer in the |
19 | * documentation and/or other materials provided with the distribution. |
20 | * |
21 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
31 | * POSSIBILITY OF SUCH DAMAGE. |
32 | */ |
33 | |
34 | /* |
35 | * Copyright (c) 2008 |
36 | * Matthias Drochner. All rights reserved. |
37 | * |
38 | * Redistribution and use in source and binary forms, with or without |
39 | * modification, are permitted provided that the following conditions |
40 | * are met: |
41 | * 1. Redistributions of source code must retain the above copyright |
42 | * notice, this list of conditions and the following disclaimer. |
43 | * 2. Redistributions in binary form must reproduce the above copyright |
44 | * notice, this list of conditions and the following disclaimer in the |
45 | * documentation and/or other materials provided with the distribution. |
46 | * |
47 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
48 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
49 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
50 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
51 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
52 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
53 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
54 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
55 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
56 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
57 | * |
58 | */ |
59 | |
60 | #include <dev/usb/usb.h> |
61 | #include <dev/usb/usbdi.h> |
62 | #include <dev/usb/usbdivar.h> |
63 | #include <dev/usb/usbroothub.h> |
64 | #include <dev/usb/usbhist.h> |
65 | |
66 | extern int usbdebug; |
67 | |
68 | /* helper functions for USB root hub emulation */ |
69 | |
70 | static usbd_status roothub_ctrl_transfer(struct usbd_xfer *); |
71 | static usbd_status roothub_ctrl_start(struct usbd_xfer *); |
72 | static void roothub_ctrl_abort(struct usbd_xfer *); |
73 | static void roothub_ctrl_close(struct usbd_pipe *); |
74 | static void roothub_ctrl_done(struct usbd_xfer *); |
75 | static void roothub_noop(struct usbd_pipe *pipe); |
76 | |
77 | const struct usbd_pipe_methods roothub_ctrl_methods = { |
78 | .upm_transfer = roothub_ctrl_transfer, |
79 | .upm_start = roothub_ctrl_start, |
80 | .upm_abort = roothub_ctrl_abort, |
81 | .upm_close = roothub_ctrl_close, |
82 | .upm_cleartoggle = roothub_noop, |
83 | .upm_done = roothub_ctrl_done, |
84 | }; |
85 | |
86 | int |
87 | usb_makestrdesc(usb_string_descriptor_t *p, int l, const char *s) |
88 | { |
89 | int i; |
90 | |
91 | if (l == 0) |
92 | return 0; |
93 | p->bLength = 2 * strlen(s) + 2; |
94 | if (l == 1) |
95 | return 1; |
96 | p->bDescriptorType = UDESC_STRING; |
97 | l -= 2; |
98 | /* poor man's utf-16le conversion */ |
99 | for (i = 0; s[i] && l > 1; i++, l -= 2) |
100 | USETW2(p->bString[i], 0, s[i]); |
101 | return 2 * i + 2; |
102 | } |
103 | |
104 | int |
105 | usb_makelangtbl(usb_string_descriptor_t *p, int l) |
106 | { |
107 | |
108 | if (l == 0) |
109 | return 0; |
110 | p->bLength = 4; |
111 | if (l == 1) |
112 | return 1; |
113 | p->bDescriptorType = UDESC_STRING; |
114 | if (l < 4) |
115 | return 2; |
116 | USETW(p->bString[0], 0x0409); /* english/US */ |
117 | return 4; |
118 | } |
119 | |
120 | /* |
121 | * Data structures and routines to emulate the root hub. |
122 | */ |
123 | static const usb_device_descriptor_t usbroothub_devd1 = { |
124 | .bLength = sizeof(usb_device_descriptor_t), |
125 | .bDescriptorType = UDESC_DEVICE, |
126 | .bcdUSB = {0x00, 0x01}, |
127 | .bDeviceClass = UDCLASS_HUB, |
128 | .bDeviceSubClass = UDSUBCLASS_HUB, |
129 | .bDeviceProtocol = UDPROTO_FSHUB, |
130 | .bMaxPacketSize = 64, |
131 | .idVendor = {0}, |
132 | .idProduct = {0}, |
133 | .bcdDevice = {0x00, 0x01}, |
134 | .iManufacturer = 1, |
135 | .iProduct = 2, |
136 | .iSerialNumber = 0, |
137 | .bNumConfigurations = 1 |
138 | }; |
139 | |
140 | static const struct usb_roothub_descriptors usbroothub_confd1 = { |
141 | .urh_confd = { |
142 | .bLength = USB_CONFIG_DESCRIPTOR_SIZE, |
143 | .bDescriptorType = UDESC_CONFIG, |
144 | .wTotalLength = USETWD(sizeof(usbroothub_confd1)), |
145 | .bNumInterface = 1, |
146 | .bConfigurationValue = 1, |
147 | .iConfiguration = 0, |
148 | .bmAttributes = UC_ATTR_MBO | UC_SELF_POWERED, |
149 | .bMaxPower = 0, |
150 | }, |
151 | .urh_ifcd = { |
152 | .bLength = USB_INTERFACE_DESCRIPTOR_SIZE, |
153 | .bDescriptorType = UDESC_INTERFACE, |
154 | .bInterfaceNumber = 0, |
155 | .bAlternateSetting = 0, |
156 | .bNumEndpoints = 1, |
157 | .bInterfaceClass = UICLASS_HUB, |
158 | .bInterfaceSubClass = UISUBCLASS_HUB, |
159 | .bInterfaceProtocol = UIPROTO_FSHUB, |
160 | .iInterface = 0 |
161 | }, |
162 | .urh_endpd = { |
163 | .bLength = USB_ENDPOINT_DESCRIPTOR_SIZE, |
164 | .bDescriptorType = UDESC_ENDPOINT, |
165 | .bEndpointAddress = UE_DIR_IN | USBROOTHUB_INTR_ENDPT, |
166 | .bmAttributes = UE_INTERRUPT, |
167 | .wMaxPacketSize = USETWD(8), /* max packet */ |
168 | .bInterval = 255, |
169 | }, |
170 | }; |
171 | |
172 | static const usb_device_descriptor_t usbroothub_devd2 = { |
173 | .bLength = sizeof(usb_device_descriptor_t), |
174 | .bDescriptorType = UDESC_DEVICE, |
175 | .bcdUSB = {0x00, 0x02}, |
176 | .bDeviceClass = UDCLASS_HUB, |
177 | .bDeviceSubClass = UDSUBCLASS_HUB, |
178 | .bDeviceProtocol = UDPROTO_HSHUBSTT, |
179 | .bMaxPacketSize = 64, |
180 | .idVendor = {0}, |
181 | .idProduct = {0}, |
182 | .bcdDevice = {0x00, 0x01}, |
183 | .iManufacturer = 1, |
184 | .iProduct = 2, |
185 | .iSerialNumber = 0, |
186 | .bNumConfigurations = 1 |
187 | }; |
188 | |
189 | static const usb_device_qualifier_t usbroothub_odevd2 = { |
190 | .bLength = USB_DEVICE_QUALIFIER_SIZE, |
191 | .bDescriptorType = UDESC_DEVICE_QUALIFIER, |
192 | .bcdUSB = {0x00, 0x02}, |
193 | .bDeviceClass = UDCLASS_HUB, |
194 | .bDeviceSubClass = UDSUBCLASS_HUB, |
195 | .bDeviceProtocol = UDPROTO_FSHUB, |
196 | .bMaxPacketSize0 = 64, |
197 | .bNumConfigurations = 1, |
198 | }; |
199 | |
200 | static const struct usb_roothub_descriptors usbroothub_confd2 = { |
201 | .urh_confd = { |
202 | .bLength = USB_CONFIG_DESCRIPTOR_SIZE, |
203 | .bDescriptorType = UDESC_CONFIG, |
204 | .wTotalLength = USETWD(sizeof(usbroothub_confd2)), |
205 | .bNumInterface = 1, |
206 | .bConfigurationValue = 1, |
207 | .iConfiguration = 0, |
208 | .bmAttributes = UC_ATTR_MBO | UC_SELF_POWERED, |
209 | .bMaxPower = 0, |
210 | }, |
211 | .urh_ifcd = { |
212 | .bLength = USB_INTERFACE_DESCRIPTOR_SIZE, |
213 | .bDescriptorType = UDESC_INTERFACE, |
214 | .bInterfaceNumber = 0, |
215 | .bAlternateSetting = 0, |
216 | .bNumEndpoints = 1, |
217 | .bInterfaceClass = UICLASS_HUB, |
218 | .bInterfaceSubClass = UISUBCLASS_HUB, |
219 | .bInterfaceProtocol = UIPROTO_HSHUBSTT, |
220 | .iInterface = 0 |
221 | }, |
222 | .urh_endpd = { |
223 | .bLength = USB_ENDPOINT_DESCRIPTOR_SIZE, |
224 | .bDescriptorType = UDESC_ENDPOINT, |
225 | .bEndpointAddress = UE_DIR_IN | USBROOTHUB_INTR_ENDPT, |
226 | .bmAttributes = UE_INTERRUPT, |
227 | .wMaxPacketSize = USETWD(8), /* max packet */ |
228 | .bInterval = 12, |
229 | }, |
230 | }; |
231 | |
232 | static const usb_hub_descriptor_t usbroothub_hubd = { |
233 | .bDescLength = USB_HUB_DESCRIPTOR_SIZE, |
234 | .bDescriptorType = UDESC_HUB, |
235 | .bNbrPorts = 1, |
236 | .wHubCharacteristics = USETWD(UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL), |
237 | .bPwrOn2PwrGood = 50, |
238 | .bHubContrCurrent = 0, |
239 | .DeviceRemovable = {0}, /* port is removable */ |
240 | }; |
241 | |
242 | /* |
243 | * Simulate a hardware hub by handling all the necessary requests. |
244 | */ |
245 | usbd_status |
246 | roothub_ctrl_transfer(struct usbd_xfer *xfer) |
247 | { |
248 | struct usbd_pipe *pipe = xfer->ux_pipe; |
249 | struct usbd_bus *bus = pipe->up_dev->ud_bus; |
250 | usbd_status err; |
251 | |
252 | /* Insert last in queue. */ |
253 | mutex_enter(bus->ub_lock); |
254 | err = usb_insert_transfer(xfer); |
255 | mutex_exit(bus->ub_lock); |
256 | if (err) |
257 | return err; |
258 | |
259 | /* Pipe isn't running, start first */ |
260 | return roothub_ctrl_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue)); |
261 | } |
262 | |
263 | static usbd_status |
264 | roothub_ctrl_start(struct usbd_xfer *xfer) |
265 | { |
266 | struct usbd_pipe *pipe = xfer->ux_pipe; |
267 | struct usbd_bus *bus = pipe->up_dev->ud_bus; |
268 | usb_device_request_t *req; |
269 | usbd_status err = USBD_IOERROR; /* XXX STALL? */ |
270 | uint16_t len, value; |
271 | int buflen, actlen; |
272 | void *buf; |
273 | |
274 | USBHIST_FUNC(); USBHIST_CALLED(usbdebug); |
275 | |
276 | KASSERT(xfer->ux_rqflags & URQ_REQUEST); |
277 | req = &xfer->ux_request; |
278 | |
279 | USBHIST_LOG(usbdebug, "type=%#2x request=%#2x" , req->bmRequestType, |
280 | req->bRequest, 0, 0); |
281 | |
282 | len = UGETW(req->wLength); |
283 | value = UGETW(req->wValue); |
284 | |
285 | buf = len ? usbd_get_buffer(xfer) : NULL; |
286 | buflen = 0; |
287 | |
288 | #define C(x,y) ((x) | ((y) << 8)) |
289 | switch (C(req->bRequest, req->bmRequestType)) { |
290 | case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): |
291 | case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): |
292 | case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): |
293 | /* |
294 | * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops |
295 | * for the integrated root hub. |
296 | */ |
297 | break; |
298 | case C(UR_GET_CONFIG, UT_READ_DEVICE): |
299 | if (len > 0) { |
300 | uint8_t *out = buf; |
301 | |
302 | *out = bus->ub_rhconf; |
303 | buflen = sizeof(*out); |
304 | } |
305 | break; |
306 | case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): |
307 | USBHIST_LOG(usbdebug, "wValue=%#4x" , value, 0, 0, 0); |
308 | |
309 | if (len == 0) |
310 | break; |
311 | switch (value) { |
312 | case C(0, UDESC_DEVICE): |
313 | if (bus->ub_revision == USBREV_2_0) { |
314 | buflen = min(len, sizeof(usbroothub_devd2)); |
315 | memcpy(buf, &usbroothub_devd2, buflen); |
316 | } else { |
317 | buflen = min(len, sizeof(usbroothub_devd1)); |
318 | memcpy(buf, &usbroothub_devd1, buflen); |
319 | } |
320 | break; |
321 | case C(0, UDESC_CONFIG): |
322 | if (bus->ub_revision == USBREV_2_0) { |
323 | buflen = min(len, sizeof(usbroothub_confd2)); |
324 | memcpy(buf, &usbroothub_confd2, buflen); |
325 | } else { |
326 | buflen = min(len, sizeof(usbroothub_confd1)); |
327 | memcpy(buf, &usbroothub_confd1, buflen); |
328 | } |
329 | break; |
330 | case C(0, UDESC_DEVICE_QUALIFIER): |
331 | if (bus->ub_revision == USBREV_2_0) { |
332 | /* |
333 | * We can't really operate at another speed, |
334 | * but the spec says we need this descriptor. |
335 | */ |
336 | buflen = min(len, sizeof(usbroothub_odevd2)); |
337 | memcpy(buf, &usbroothub_odevd2, buflen); |
338 | } else |
339 | goto fail; |
340 | break; |
341 | case C(0, UDESC_OTHER_SPEED_CONFIGURATION): |
342 | if (bus->ub_revision == USBREV_2_0) { |
343 | struct usb_roothub_descriptors confd; |
344 | |
345 | /* |
346 | * We can't really operate at another speed, |
347 | * but the spec says we need this descriptor. |
348 | */ |
349 | buflen = min(len, sizeof(usbroothub_confd2)); |
350 | memcpy(&confd, &usbroothub_confd2, buflen); |
351 | confd.urh_confd.bDescriptorType = |
352 | UDESC_OTHER_SPEED_CONFIGURATION; |
353 | memcpy(buf, &confd, buflen); |
354 | } else |
355 | goto fail; |
356 | break; |
357 | #define sd ((usb_string_descriptor_t *)buf) |
358 | case C(0, UDESC_STRING): |
359 | /* Language table */ |
360 | buflen = usb_makelangtbl(sd, len); |
361 | break; |
362 | case C(1, UDESC_STRING): |
363 | /* Vendor */ |
364 | buflen = usb_makestrdesc(sd, len, "NetBSD" ); |
365 | break; |
366 | case C(2, UDESC_STRING): |
367 | /* Product */ |
368 | buflen = usb_makestrdesc(sd, len, "Root hub" ); |
369 | break; |
370 | #undef sd |
371 | default: |
372 | /* Default to error */ |
373 | buflen = -1; |
374 | } |
375 | break; |
376 | case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): |
377 | buflen = min(len, sizeof(usbroothub_hubd)); |
378 | memcpy(buf, &usbroothub_hubd, buflen); |
379 | break; |
380 | case C(UR_GET_INTERFACE, UT_READ_INTERFACE): |
381 | /* Get Interface, 9.4.4 */ |
382 | if (len > 0) { |
383 | uint8_t *out = buf; |
384 | |
385 | *out = 0; |
386 | buflen = sizeof(*out); |
387 | } |
388 | break; |
389 | case C(UR_GET_STATUS, UT_READ_DEVICE): |
390 | /* Get Status from device, 9.4.5 */ |
391 | if (len > 1) { |
392 | usb_status_t *out = buf; |
393 | |
394 | USETW(out->wStatus, UDS_SELF_POWERED); |
395 | buflen = sizeof(*out); |
396 | } |
397 | break; |
398 | case C(UR_GET_STATUS, UT_READ_INTERFACE): |
399 | case C(UR_GET_STATUS, UT_READ_ENDPOINT): |
400 | /* Get Status from interface, endpoint, 9.4.5 */ |
401 | if (len > 1) { |
402 | usb_status_t *out = buf; |
403 | |
404 | USETW(out->wStatus, 0); |
405 | buflen = sizeof(*out); |
406 | } |
407 | break; |
408 | case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): |
409 | /* Set Address, 9.4.6 */ |
410 | USBHIST_LOG(usbdebug, "UR_SET_ADDRESS, UT_WRITE_DEVICE: addr %d" , |
411 | value, 0, 0, 0); |
412 | if (value >= USB_MAX_DEVICES) { |
413 | goto fail; |
414 | } |
415 | bus->ub_rhaddr = value; |
416 | break; |
417 | case C(UR_SET_CONFIG, UT_WRITE_DEVICE): |
418 | /* Set Configuration, 9.4.7 */ |
419 | if (value != 0 && value != 1) { |
420 | goto fail; |
421 | } |
422 | bus->ub_rhconf = value; |
423 | break; |
424 | case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): |
425 | /* Set Descriptor, 9.4.8, not supported */ |
426 | break; |
427 | case C(UR_SET_FEATURE, UT_WRITE_DEVICE): |
428 | case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): |
429 | case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): |
430 | /* Set Feature, 9.4.9, not supported */ |
431 | goto fail; |
432 | case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): |
433 | /* Set Interface, 9.4.10, not supported */ |
434 | break; |
435 | case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): |
436 | /* Synch Frame, 9.4.11, not supported */ |
437 | break; |
438 | default: |
439 | /* Default to error */ |
440 | buflen = -1; |
441 | break; |
442 | } |
443 | |
444 | actlen = bus->ub_methods->ubm_rhctrl(bus, req, buf, buflen); |
445 | USBHIST_LOG(usbdebug, "xfer %p buflen %d actlen %d" , xfer, buflen, |
446 | actlen, 0); |
447 | if (actlen < 0) |
448 | goto fail; |
449 | |
450 | xfer->ux_actlen = actlen; |
451 | err = USBD_NORMAL_COMPLETION; |
452 | |
453 | fail: |
454 | USBHIST_LOG(usbdebug, "xfer %p err %d" , xfer, err, 0, 0); |
455 | |
456 | xfer->ux_status = err; |
457 | mutex_enter(bus->ub_lock); |
458 | usb_transfer_complete(xfer); |
459 | mutex_exit(bus->ub_lock); |
460 | |
461 | return USBD_NORMAL_COMPLETION; |
462 | } |
463 | |
464 | /* Abort a root control request. */ |
465 | Static void |
466 | roothub_ctrl_abort(struct usbd_xfer *xfer) |
467 | { |
468 | |
469 | /* Nothing to do, all transfers are synchronous. */ |
470 | } |
471 | |
472 | /* Close the root pipe. */ |
473 | Static void |
474 | roothub_ctrl_close(struct usbd_pipe *pipe) |
475 | { |
476 | |
477 | /* Nothing to do. */ |
478 | } |
479 | |
480 | Static void |
481 | roothub_ctrl_done(struct usbd_xfer *xfer) |
482 | { |
483 | |
484 | /* Nothing to do. */ |
485 | } |
486 | |
487 | static void |
488 | roothub_noop(struct usbd_pipe *pipe) |
489 | { |
490 | |
491 | } |
492 | |