usbroothub.c revision 1.1.2.4 1 /* $NetBSD: usbroothub.c,v 1.1.2.4 2015/01/31 22:28:15 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 (at) augustsson.net) at
9 * Carlstedt Research & Technology, Jared D. McNeill (jmcneill (at) invisible.ca),
10 * Matthew R. Green (mrg (at) 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
65 #ifdef USB_DEBUG
66 #define DPRINTFN(n,fmt,...) do { \
67 if (usbdebug >= (n)) { \
68 printf("%s: " fmt, \
69 __FUNCTION__,## __VA_ARGS__); \
70 } \
71 } while (0)
72 #define DPRINTF(...) DPRINTFN(1, __VA_ARGS__)
73 extern int usbdebug;
74 #else
75 #define DPRINTF(...) do { } while (0)
76 #define DPRINTFN(...) do { } while (0)
77 #endif
78
79 /* helper functions for USB root hub emulation */
80
81 static usbd_status roothub_ctrl_transfer(usbd_xfer_handle);
82 static usbd_status roothub_ctrl_start(usbd_xfer_handle);
83 static void roothub_ctrl_abort(usbd_xfer_handle);
84 static void roothub_ctrl_close(usbd_pipe_handle);
85 static void roothub_ctrl_done(usbd_xfer_handle);
86 static void roothub_noop(usbd_pipe_handle pipe);
87
88 const struct usbd_pipe_methods roothub_ctrl_methods = {
89 .upm_transfer = roothub_ctrl_transfer,
90 .upm_start = roothub_ctrl_start,
91 .upm_abort = roothub_ctrl_abort,
92 .upm_close = roothub_ctrl_close,
93 .upm_cleartoggle = roothub_noop,
94 .upm_done = roothub_ctrl_done,
95 };
96
97 int
98 usb_makestrdesc(usb_string_descriptor_t *p, int l, const char *s)
99 {
100 int i;
101
102 if (l == 0)
103 return 0;
104 p->bLength = 2 * strlen(s) + 2;
105 if (l == 1)
106 return 1;
107 p->bDescriptorType = UDESC_STRING;
108 l -= 2;
109 /* poor man's utf-16le conversion */
110 for (i = 0; s[i] && l > 1; i++, l -= 2)
111 USETW2(p->bString[i], 0, s[i]);
112 return 2 * i + 2;
113 }
114
115 int
116 usb_makelangtbl(usb_string_descriptor_t *p, int l)
117 {
118
119 if (l == 0)
120 return 0;
121 p->bLength = 4;
122 if (l == 1)
123 return 1;
124 p->bDescriptorType = UDESC_STRING;
125 if (l < 4)
126 return 2;
127 USETW(p->bString[0], 0x0409); /* english/US */
128 return 4;
129 }
130
131 /*
132 * Data structures and routines to emulate the root hub.
133 */
134 static const usb_device_descriptor_t usbroothub_devd1 = {
135 .bLength = sizeof(usb_device_descriptor_t),
136 .bDescriptorType = UDESC_DEVICE,
137 .bcdUSB = {0x00, 0x01},
138 .bDeviceClass = UDCLASS_HUB,
139 .bDeviceSubClass = UDSUBCLASS_HUB,
140 .bDeviceProtocol = UDPROTO_FSHUB,
141 .bMaxPacketSize = 64,
142 .idVendor = {0},
143 .idProduct = {0},
144 .bcdDevice = {0x00, 0x01},
145 .iManufacturer = 1,
146 .iProduct = 2,
147 .iSerialNumber = 0,
148 .bNumConfigurations = 1
149 };
150
151 static const struct usb_roothub_descriptors usbroothub_confd1 = {
152 .urh_confd = {
153 .bLength = USB_CONFIG_DESCRIPTOR_SIZE,
154 .bDescriptorType = UDESC_CONFIG,
155 .wTotalLength = USETWD(sizeof(usbroothub_confd1)),
156 .bNumInterface = 1,
157 .bConfigurationValue = 1,
158 .iConfiguration = 0,
159 .bmAttributes = UC_ATTR_MBO | UC_SELF_POWERED,
160 .bMaxPower = 0,
161 },
162 .urh_ifcd = {
163 .bLength = USB_INTERFACE_DESCRIPTOR_SIZE,
164 .bDescriptorType = UDESC_INTERFACE,
165 .bInterfaceNumber = 0,
166 .bAlternateSetting = 0,
167 .bNumEndpoints = 1,
168 .bInterfaceClass = UICLASS_HUB,
169 .bInterfaceSubClass = UISUBCLASS_HUB,
170 .bInterfaceProtocol = UIPROTO_FSHUB,
171 .iInterface = 0
172 },
173 .urh_endpd = {
174 .bLength = USB_ENDPOINT_DESCRIPTOR_SIZE,
175 .bDescriptorType = UDESC_ENDPOINT,
176 .bEndpointAddress = UE_DIR_IN | USBROOTHUB_INTR_ENDPT,
177 .bmAttributes = UE_INTERRUPT,
178 .wMaxPacketSize = USETWD(8), /* max packet */
179 .bInterval = 255,
180 },
181 };
182
183 static const usb_device_descriptor_t usbroothub_devd2 = {
184 .bLength = sizeof(usb_device_descriptor_t),
185 .bDescriptorType = UDESC_DEVICE,
186 .bcdUSB = {0x00, 0x02},
187 .bDeviceClass = UDCLASS_HUB,
188 .bDeviceSubClass = UDSUBCLASS_HUB,
189 .bDeviceProtocol = UDPROTO_HSHUBSTT,
190 .bMaxPacketSize = 64,
191 .idVendor = {0},
192 .idProduct = {0},
193 .bcdDevice = {0x00, 0x01},
194 .iManufacturer = 1,
195 .iProduct = 2,
196 .iSerialNumber = 0,
197 .bNumConfigurations = 1
198 };
199
200 static const usb_device_qualifier_t usbroothub_odevd2 = {
201 .bLength = USB_DEVICE_QUALIFIER_SIZE,
202 .bDescriptorType = UDESC_DEVICE_QUALIFIER,
203 .bcdUSB = {0x00, 0x02},
204 .bDeviceClass = UDCLASS_HUB,
205 .bDeviceSubClass = UDSUBCLASS_HUB,
206 .bDeviceProtocol = UDPROTO_FSHUB,
207 .bMaxPacketSize0 = 64,
208 .bNumConfigurations = 1,
209 };
210
211 static const struct usb_roothub_descriptors usbroothub_confd2 = {
212 .urh_confd = {
213 .bLength = USB_CONFIG_DESCRIPTOR_SIZE,
214 .bDescriptorType = UDESC_CONFIG,
215 .wTotalLength = USETWD(sizeof(usbroothub_confd2)),
216 .bNumInterface = 1,
217 .bConfigurationValue = 1,
218 .iConfiguration = 0,
219 .bmAttributes = UC_ATTR_MBO | UC_SELF_POWERED,
220 .bMaxPower = 0,
221 },
222 .urh_ifcd = {
223 .bLength = USB_INTERFACE_DESCRIPTOR_SIZE,
224 .bDescriptorType = UDESC_INTERFACE,
225 .bInterfaceNumber = 0,
226 .bAlternateSetting = 0,
227 .bNumEndpoints = 1,
228 .bInterfaceClass = UICLASS_HUB,
229 .bInterfaceSubClass = UISUBCLASS_HUB,
230 .bInterfaceProtocol = UIPROTO_HSHUBSTT,
231 .iInterface = 0
232 },
233 .urh_endpd = {
234 .bLength = USB_ENDPOINT_DESCRIPTOR_SIZE,
235 .bDescriptorType = UDESC_ENDPOINT,
236 .bEndpointAddress = UE_DIR_IN | USBROOTHUB_INTR_ENDPT,
237 .bmAttributes = UE_INTERRUPT,
238 .wMaxPacketSize = USETWD(8), /* max packet */
239 .bInterval = 12,
240 },
241 };
242
243 static const usb_hub_descriptor_t usbroothub_hubd = {
244 .bDescLength = USB_HUB_DESCRIPTOR_SIZE,
245 .bDescriptorType = UDESC_HUB,
246 .bNbrPorts = 1,
247 .wHubCharacteristics = USETWD(UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL),
248 .bPwrOn2PwrGood = 50,
249 .bHubContrCurrent = 0,
250 .DeviceRemovable = {0}, /* port is removable */
251 };
252
253 /*
254 * Simulate a hardware hub by handling all the necessary requests.
255 */
256 usbd_status
257 roothub_ctrl_transfer(usbd_xfer_handle xfer)
258 {
259 usbd_pipe_handle pipe = xfer->ux_pipe;
260 struct usbd_bus *bus = pipe->up_dev->ud_bus;
261 usbd_status err;
262
263 /* Insert last in queue. */
264 mutex_enter(bus->ub_lock);
265 err = usb_insert_transfer(xfer);
266 mutex_exit(bus->ub_lock);
267 if (err)
268 return err;
269
270 /* Pipe isn't running, start first */
271 return roothub_ctrl_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue));
272 }
273
274 static usbd_status
275 roothub_ctrl_start(usbd_xfer_handle xfer)
276 {
277 usbd_pipe_handle pipe = xfer->ux_pipe;
278 struct usbd_bus *bus = pipe->up_dev->ud_bus;
279 usb_device_request_t *req;
280 usbd_status err = USBD_IOERROR; /* XXX STALL? */
281 uint16_t len, value;
282 int buflen, actlen;
283 void *buf;
284
285 KASSERT(xfer->ux_rqflags & URQ_REQUEST);
286 req = &xfer->ux_request;
287
288 DPRINTFN(4, "type=%#2x request=%#2x\n",
289 req->bmRequestType, req->bRequest);
290
291 len = UGETW(req->wLength);
292 value = UGETW(req->wValue);
293
294 buf = len ? usbd_get_buffer(xfer) : NULL;
295 buflen = 0;
296
297 #define C(x,y) ((x) | ((y) << 8))
298 switch (C(req->bRequest, req->bmRequestType)) {
299 case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
300 case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
301 case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
302 /*
303 * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops
304 * for the integrated root hub.
305 */
306 break;
307 case C(UR_GET_CONFIG, UT_READ_DEVICE):
308 if (len > 0) {
309 uint8_t *out = buf;
310
311 *out = bus->ub_rhconf;
312 buflen = sizeof(*out);
313 }
314 break;
315 case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
316 DPRINTFN(8, "wValue=%#4x\n", value);
317
318 if (len == 0)
319 break;
320 switch (value) {
321 case C(0, UDESC_DEVICE):
322 if (bus->ub_revision == USBREV_2_0) {
323 buflen = min(len, sizeof(usbroothub_devd2));
324 memcpy(buf, &usbroothub_devd2, buflen);
325 } else {
326 buflen = min(len, sizeof(usbroothub_devd1));
327 memcpy(buf, &usbroothub_devd1, buflen);
328 }
329 break;
330 case C(0, UDESC_CONFIG):
331 if (bus->ub_revision == USBREV_2_0) {
332 buflen = min(len, sizeof(usbroothub_confd2));
333 memcpy(buf, &usbroothub_confd2, buflen);
334 } else {
335 buflen = min(len, sizeof(usbroothub_confd1));
336 memcpy(buf, &usbroothub_confd1, buflen);
337 }
338 break;
339 case C(0, UDESC_DEVICE_QUALIFIER):
340 if (bus->ub_revision == USBREV_2_0) {
341 /*
342 * We can't really operate at another speed,
343 * but the spec says we need this descriptor.
344 */
345 buflen = min(len, sizeof(usbroothub_odevd2));
346 memcpy(buf, &usbroothub_odevd2, buflen);
347 } else
348 goto fail;
349 break;
350 case C(0, UDESC_OTHER_SPEED_CONFIGURATION):
351 if (bus->ub_revision == USBREV_2_0) {
352 struct usb_roothub_descriptors confd;
353
354 /*
355 * We can't really operate at another speed,
356 * but the spec says we need this descriptor.
357 */
358 buflen = min(len, sizeof(usbroothub_confd2));
359 memcpy(&confd, &usbroothub_confd2, buflen);
360 confd.urh_confd.bDescriptorType =
361 UDESC_OTHER_SPEED_CONFIGURATION;
362 memcpy(buf, &confd, buflen);
363 } else
364 goto fail;
365 break;
366 #define sd ((usb_string_descriptor_t *)buf)
367 case C(0, UDESC_STRING):
368 /* Language table */
369 buflen = usb_makelangtbl(sd, len);
370 break;
371 case C(1, UDESC_STRING):
372 /* Vendor */
373 buflen = usb_makestrdesc(sd, len, "NetBSD");
374 break;
375 case C(2, UDESC_STRING):
376 /* Product */
377 buflen = usb_makestrdesc(sd, len, "Root hub");
378 break;
379 #undef sd
380 default:
381 /* Default to error */
382 buflen = -1;
383 }
384 break;
385 case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
386 buflen = min(len, sizeof(usbroothub_hubd));
387 memcpy(buf, &usbroothub_hubd, buflen);
388 break;
389 case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
390 /* Get Interface, 9.4.4 */
391 if (len > 0) {
392 uint8_t *out = buf;
393
394 *out = 0;
395 buflen = sizeof(*out);
396 }
397 break;
398 case C(UR_GET_STATUS, UT_READ_DEVICE):
399 /* Get Status from device, 9.4.5 */
400 if (len > 1) {
401 usb_status_t *out = buf;
402
403 USETW(out->wStatus, UDS_SELF_POWERED);
404 buflen = sizeof(*out);
405 }
406 break;
407 case C(UR_GET_STATUS, UT_READ_INTERFACE):
408 case C(UR_GET_STATUS, UT_READ_ENDPOINT):
409 /* Get Status from interface, endpoint, 9.4.5 */
410 if (len > 1) {
411 usb_status_t *out = buf;
412
413 USETW(out->wStatus, 0);
414 buflen = sizeof(*out);
415 }
416 break;
417 case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
418 /* Set Address, 9.4.6 */
419 DPRINTF("UR_SET_ADDRESS, UT_WRITE_DEVICE: addr %d\n",
420 value);
421 if (value >= USB_MAX_DEVICES) {
422 goto fail;
423 }
424 bus->ub_rhaddr = value;
425 break;
426 case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
427 /* Set Configuration, 9.4.7 */
428 if (value != 0 && value != 1) {
429 goto fail;
430 }
431 bus->ub_rhconf = value;
432 break;
433 case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
434 /* Set Descriptor, 9.4.8, not supported */
435 break;
436 case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
437 case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
438 case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
439 /* Set Feature, 9.4.9, not supported */
440 goto fail;
441 case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
442 /* Set Interface, 9.4.10, not supported */
443 break;
444 case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
445 /* Synch Frame, 9.4.11, not supported */
446 break;
447 default:
448 /* Default to error */
449 buflen = -1;
450 break;
451 }
452
453 actlen = bus->ub_methods->ubm_rhctrl(bus, req, buf, buflen);
454 if (actlen < 0)
455 goto fail;
456
457 xfer->ux_actlen = actlen;
458 err = USBD_NORMAL_COMPLETION;
459
460 fail:
461 xfer->ux_status = err;
462 mutex_enter(bus->ub_lock);
463 usb_transfer_complete(xfer);
464 mutex_exit(bus->ub_lock);
465
466 return err;
467 }
468
469 /* Abort a root control request. */
470 Static void
471 roothub_ctrl_abort(usbd_xfer_handle xfer)
472 {
473
474 DPRINTF("\n");
475 /* Nothing to do, all transfers are synchronous. */
476 }
477
478 /* Close the root pipe. */
479 Static void
480 roothub_ctrl_close(usbd_pipe_handle pipe)
481 {
482
483 DPRINTF("\n");
484 /* Nothing to do. */
485 }
486
487 Static void
488 roothub_ctrl_done(usbd_xfer_handle xfer)
489 {
490
491 DPRINTF("\n");
492 /* Nothing to do. */
493 }
494
495 static void
496 roothub_noop(usbd_pipe_handle pipe)
497 {
498
499 }
500