Home | History | Annotate | Line # | Download | only in usb
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