This will make the code work with the different styles of Kconfig (emit unset bools vs don't emit unset bools) Roughly, the patch does this, and a little bit of fixing up: perl -pi -e 's,ifdef (CONFIG_LP_.+?)\b,if IS_ENABLED\($1\),g' `find . -name *.[ch]` perl -pi -e 's,ifndef (CONFIG_LP_.+?)\b,if !IS_ENABLED\($1\),g' `find . -name *.[ch]` Change-Id: Ib8a839b056a1f806a8597052e1b571ea3d18a79f Signed-off-by: Stefan Reinauer <stefan.reinauer@coreboot.org> Reviewed-on: http://review.coreboot.org/10711 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi <pgeorgi@google.com>
		
			
				
	
	
		
			656 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			656 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * This file is part of the libpayload project.
 | |
|  *
 | |
|  * Copyright (C) 2008-2010 coresystems GmbH
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer in the
 | |
|  *    documentation and/or other materials provided with the distribution.
 | |
|  * 3. The name of the author may not be used to endorse or promote products
 | |
|  *    derived from this software without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | |
|  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | |
|  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | |
|  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | |
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | |
|  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | |
|  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | |
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | |
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|  * SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| //#define USB_DEBUG
 | |
| 
 | |
| #include <libpayload-config.h>
 | |
| #include <usb/usb.h>
 | |
| 
 | |
| #define DR_DESC gen_bmRequestType(device_to_host, standard_type, dev_recp)
 | |
| 
 | |
| hci_t *usb_hcs = 0;
 | |
| 
 | |
| hci_t *
 | |
| new_controller (void)
 | |
| {
 | |
| 	hci_t *controller = xzalloc(sizeof (hci_t));
 | |
| 	controller->next = usb_hcs;
 | |
| 	usb_hcs = controller;
 | |
| 	return controller;
 | |
| }
 | |
| 
 | |
| void
 | |
| detach_controller (hci_t *controller)
 | |
| {
 | |
| 	if (controller == NULL)
 | |
| 		return;
 | |
| 
 | |
| 	usb_detach_device(controller, 0);	/* tear down root hub tree */
 | |
| 
 | |
| 	if (usb_hcs == controller) {
 | |
| 		usb_hcs = controller->next;
 | |
| 	} else {
 | |
| 		hci_t *it = usb_hcs;
 | |
| 		while (it != NULL) {
 | |
| 			if (it->next == controller) {
 | |
| 				it->next = controller->next;
 | |
| 				return;
 | |
| 			}
 | |
| 			it = it->next;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Shut down all controllers
 | |
|  */
 | |
| int
 | |
| usb_exit (void)
 | |
| {
 | |
| 	while (usb_hcs != NULL) {
 | |
| 		usb_hcs->shutdown(usb_hcs);
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Polls all hubs on all USB controllers, to find out about device changes
 | |
|  */
 | |
| void
 | |
| usb_poll (void)
 | |
| {
 | |
| 	if (usb_hcs == 0)
 | |
| 		return;
 | |
| 	hci_t *controller = usb_hcs;
 | |
| 	while (controller != NULL) {
 | |
| 		int i;
 | |
| 		for (i = 0; i < 128; i++) {
 | |
| 			if (controller->devices[i] != 0) {
 | |
| 				controller->devices[i]->poll (controller->devices[i]);
 | |
| 			}
 | |
| 		}
 | |
| 		controller = controller->next;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| usbdev_t *
 | |
| init_device_entry (hci_t *controller, int i)
 | |
| {
 | |
| 	usbdev_t *dev = calloc(1, sizeof(usbdev_t));
 | |
| 	if (!dev) {
 | |
| 		usb_debug("no memory to allocate device structure\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	if (controller->devices[i] != 0)
 | |
| 		usb_debug("warning: device %d reassigned?\n", i);
 | |
| 	controller->devices[i] = dev;
 | |
| 	dev->controller = controller;
 | |
| 	dev->address = -1;
 | |
| 	dev->hub = -1;
 | |
| 	dev->port = -1;
 | |
| 	dev->init = usb_nop_init;
 | |
| 	dev->init (controller->devices[i]);
 | |
| 	return dev;
 | |
| }
 | |
| 
 | |
| int
 | |
| set_feature (usbdev_t *dev, int endp, int feature, int rtype)
 | |
| {
 | |
| 	dev_req_t dr;
 | |
| 
 | |
| 	dr.bmRequestType = rtype;
 | |
| 	dr.data_dir = host_to_device;
 | |
| 	dr.bRequest = SET_FEATURE;
 | |
| 	dr.wValue = feature;
 | |
| 	dr.wIndex = endp;
 | |
| 	dr.wLength = 0;
 | |
| 
 | |
| 	return dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
 | |
| }
 | |
| 
 | |
| int
 | |
| get_status (usbdev_t *dev, int intf, int rtype, int len, void *data)
 | |
| {
 | |
| 	dev_req_t dr;
 | |
| 
 | |
| 	dr.bmRequestType = rtype;
 | |
| 	dr.data_dir = device_to_host;
 | |
| 	dr.bRequest = GET_STATUS;
 | |
| 	dr.wValue = 0;
 | |
| 	dr.wIndex = intf;
 | |
| 	dr.wLength = len;
 | |
| 
 | |
| 	return dev->controller->control (dev, IN, sizeof (dr), &dr, len, data);
 | |
| }
 | |
| 
 | |
| int
 | |
| get_descriptor (usbdev_t *dev, int rtype, int descType, int descIdx,
 | |
| 		void *data, size_t len)
 | |
| {
 | |
| 	dev_req_t dr;
 | |
| 
 | |
| 	dr.bmRequestType = rtype;
 | |
| 	dr.bRequest = GET_DESCRIPTOR;
 | |
| 	dr.wValue = descType << 8 | descIdx;
 | |
| 	dr.wIndex = 0;
 | |
| 	dr.wLength = len;
 | |
| 
 | |
| 	return dev->controller->control (dev, IN, sizeof (dr), &dr, len, data);
 | |
| }
 | |
| 
 | |
| int
 | |
| set_configuration (usbdev_t *dev)
 | |
| {
 | |
| 	dev_req_t dr;
 | |
| 
 | |
| 	dr.bmRequestType = 0;
 | |
| 	dr.bRequest = SET_CONFIGURATION;
 | |
| 	dr.wValue = dev->configuration->bConfigurationValue;
 | |
| 	dr.wIndex = 0;
 | |
| 	dr.wLength = 0;
 | |
| 
 | |
| 	return dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
 | |
| }
 | |
| 
 | |
| int
 | |
| clear_feature (usbdev_t *dev, int endp, int feature, int rtype)
 | |
| {
 | |
| 	dev_req_t dr;
 | |
| 
 | |
| 	dr.bmRequestType = rtype;
 | |
| 	dr.data_dir = host_to_device;
 | |
| 	dr.bRequest = CLEAR_FEATURE;
 | |
| 	dr.wValue = feature;
 | |
| 	dr.wIndex = endp;
 | |
| 	dr.wLength = 0;
 | |
| 
 | |
| 	return dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0) < 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| clear_stall (endpoint_t *ep)
 | |
| {
 | |
| 	int ret = clear_feature (ep->dev, ep->endpoint, ENDPOINT_HALT,
 | |
| 		gen_bmRequestType (host_to_device, standard_type, endp_recp));
 | |
| 	ep->toggle = 0;
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /* returns free address or -1 */
 | |
| static int
 | |
| get_free_address (hci_t *controller)
 | |
| {
 | |
| 	int i = controller->latest_address + 1;
 | |
| 	for (; i != controller->latest_address; i++) {
 | |
| 		if (i >= ARRAY_SIZE(controller->devices) || i < 1) {
 | |
| 			usb_debug("WARNING: Device addresses for controller %#x"
 | |
| 				  " wrapped around!\n", controller->reg_base);
 | |
| 			i = 0;
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (controller->devices[i] == 0) {
 | |
| 			controller->latest_address = i;
 | |
| 			return i;
 | |
| 		}
 | |
| 	}
 | |
| 	usb_debug ("no free address found\n");
 | |
| 	return -1;		// no free address
 | |
| }
 | |
| 
 | |
| int
 | |
| usb_decode_mps0(usb_speed speed, u8 bMaxPacketSize0)
 | |
| {
 | |
| 	switch (speed) {
 | |
| 	case LOW_SPEED:
 | |
| 		if (bMaxPacketSize0 != 8) {
 | |
| 			usb_debug("Invalid MPS0: 0x%02x\n", bMaxPacketSize0);
 | |
| 			bMaxPacketSize0 = 8;
 | |
| 		}
 | |
| 		return bMaxPacketSize0;
 | |
| 	case FULL_SPEED:
 | |
| 		switch (bMaxPacketSize0) {
 | |
| 		case 8: case 16: case 32: case 64:
 | |
| 			return bMaxPacketSize0;
 | |
| 		default:
 | |
| 			usb_debug("Invalid MPS0: 0x%02x\n", bMaxPacketSize0);
 | |
| 			return 8;
 | |
| 		}
 | |
| 	case HIGH_SPEED:
 | |
| 		if (bMaxPacketSize0 != 64) {
 | |
| 			usb_debug("Invalid MPS0: 0x%02x\n", bMaxPacketSize0);
 | |
| 			bMaxPacketSize0 = 64;
 | |
| 		}
 | |
| 		return bMaxPacketSize0;
 | |
| 	case SUPER_SPEED:
 | |
| 		if (bMaxPacketSize0 != 9) {
 | |
| 			usb_debug("Invalid MPS0: 0x%02x\n", bMaxPacketSize0);
 | |
| 			bMaxPacketSize0 = 9;
 | |
| 		}
 | |
| 		return 1 << bMaxPacketSize0;
 | |
| 	default: 	/* GCC is stupid and cannot deal with enums correctly */
 | |
| 		return 8;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Normalize bInterval to log2 of microframes */
 | |
| static int
 | |
| usb_decode_interval(usb_speed speed, const endpoint_type type, const unsigned char bInterval)
 | |
| {
 | |
| #define LOG2(a) ((sizeof(unsigned) << 3) - __builtin_clz(a) - 1)
 | |
| 	switch (speed) {
 | |
| 	case LOW_SPEED:
 | |
| 		switch (type) {
 | |
| 		case ISOCHRONOUS: case INTERRUPT:
 | |
| 			return LOG2(bInterval) + 3;
 | |
| 		default:
 | |
| 			return 0;
 | |
| 		}
 | |
| 	case FULL_SPEED:
 | |
| 		switch (type) {
 | |
| 		case ISOCHRONOUS:
 | |
| 			return (bInterval - 1) + 3;
 | |
| 		case INTERRUPT:
 | |
| 			return LOG2(bInterval) + 3;
 | |
| 		default:
 | |
| 			return 0;
 | |
| 		}
 | |
| 	case HIGH_SPEED:
 | |
| 		switch (type) {
 | |
| 		case ISOCHRONOUS: case INTERRUPT:
 | |
| 			return bInterval - 1;
 | |
| 		default:
 | |
| 			return LOG2(bInterval);
 | |
| 		}
 | |
| 	case SUPER_SPEED:
 | |
| 		switch (type) {
 | |
| 		case ISOCHRONOUS: case INTERRUPT:
 | |
| 			return bInterval - 1;
 | |
| 		default:
 | |
| 			return 0;
 | |
| 		}
 | |
| 	default:
 | |
| 		return 0;
 | |
| 	}
 | |
| #undef LOG2
 | |
| }
 | |
| 
 | |
| usbdev_t *
 | |
| generic_set_address (hci_t *controller, usb_speed speed,
 | |
| 		     int hubport, int hubaddr)
 | |
| {
 | |
| 	int adr = get_free_address (controller);	// address to set
 | |
| 	dev_req_t dr;
 | |
| 
 | |
| 	memset (&dr, 0, sizeof (dr));
 | |
| 	dr.data_dir = host_to_device;
 | |
| 	dr.req_type = standard_type;
 | |
| 	dr.req_recp = dev_recp;
 | |
| 	dr.bRequest = SET_ADDRESS;
 | |
| 	dr.wValue = adr;
 | |
| 	dr.wIndex = 0;
 | |
| 	dr.wLength = 0;
 | |
| 
 | |
| 	usbdev_t *dev = init_device_entry(controller, adr);
 | |
| 	if (!dev)
 | |
| 		return NULL;
 | |
| 
 | |
| 	// dummy values for registering the address
 | |
| 	dev->address = 0;
 | |
| 	dev->hub = hubaddr;
 | |
| 	dev->port = hubport;
 | |
| 	dev->speed = speed;
 | |
| 	dev->endpoints[0].dev = dev;
 | |
| 	dev->endpoints[0].endpoint = 0;
 | |
| 	dev->endpoints[0].maxpacketsize = 8;
 | |
| 	dev->endpoints[0].toggle = 0;
 | |
| 	dev->endpoints[0].direction = SETUP;
 | |
| 	dev->endpoints[0].type = CONTROL;
 | |
| 	if (dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0) < 0) {
 | |
| 		usb_debug ("set_address failed\n");
 | |
| 		usb_detach_device (controller, adr);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	mdelay (SET_ADDRESS_MDELAY);
 | |
| 
 | |
| 	u8 buf[8];
 | |
| 	dev->address = adr;
 | |
| 	if (get_descriptor (dev, DR_DESC, DT_DEV, 0, buf, sizeof(buf))
 | |
| 			!= sizeof(buf)) {
 | |
| 		usb_debug("first get_descriptor(DT_DEV) failed\n");
 | |
| 		usb_detach_device (controller, adr);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	dev->endpoints[0].maxpacketsize = usb_decode_mps0(speed, buf[7]);
 | |
| 
 | |
| 	return dev;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_address (hci_t *controller, usb_speed speed, int hubport, int hubaddr)
 | |
| {
 | |
| 	usbdev_t *dev = controller->set_address(controller, speed,
 | |
| 						hubport, hubaddr);
 | |
| 	if (!dev) {
 | |
| 		usb_debug ("set_address failed\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	dev->descriptor = malloc(sizeof(*dev->descriptor));
 | |
| 	if (!dev->descriptor || get_descriptor (dev, DR_DESC, DT_DEV, 0,
 | |
| 			dev->descriptor, sizeof(*dev->descriptor))
 | |
| 			!= sizeof(*dev->descriptor)) {
 | |
| 		usb_debug ("get_descriptor(DT_DEV) failed\n");
 | |
| 		usb_detach_device (controller, dev->address);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	usb_debug ("* found device (0x%04x:0x%04x, USB %x.%x, MPS0: %d)\n",
 | |
| 		 dev->descriptor->idVendor, dev->descriptor->idProduct,
 | |
| 		 dev->descriptor->bcdUSB >> 8, dev->descriptor->bcdUSB & 0xff,
 | |
| 		 dev->endpoints[0].maxpacketsize);
 | |
| 	dev->quirks = usb_quirk_check(dev->descriptor->idVendor,
 | |
| 				      dev->descriptor->idProduct);
 | |
| 
 | |
| 	usb_debug ("device has %d configurations\n",
 | |
| 		   dev->descriptor->bNumConfigurations);
 | |
| 	if (dev->descriptor->bNumConfigurations == 0) {
 | |
| 		/* device isn't usable */
 | |
| 		usb_debug ("... no usable configuration!\n");
 | |
| 		usb_detach_device (controller, dev->address);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	u16 buf[2];
 | |
| 	if (get_descriptor (dev, DR_DESC, DT_CFG, 0, buf, sizeof(buf))
 | |
| 			!= sizeof(buf)) {
 | |
| 		usb_debug ("first get_descriptor(DT_CFG) failed\n");
 | |
| 		usb_detach_device (controller, dev->address);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	/* workaround for some USB devices: wait until they're ready, or
 | |
| 	 * they send a NAK when they're not allowed to do. 1ms is enough */
 | |
| 	mdelay(1);
 | |
| 	dev->configuration = malloc(buf[1]);
 | |
| 	if (!dev->configuration) {
 | |
| 		usb_debug ("could not allocate %d bytes for DT_CFG\n", buf[1]);
 | |
| 		usb_detach_device (controller, dev->address);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (get_descriptor (dev, DR_DESC, DT_CFG, 0, dev->configuration,
 | |
| 			    buf[1]) != buf[1]) {
 | |
| 		usb_debug ("get_descriptor(DT_CFG) failed\n");
 | |
| 		usb_detach_device (controller, dev->address);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	configuration_descriptor_t *cd = dev->configuration;
 | |
| 	if (cd->wTotalLength != buf[1]) {
 | |
| 		usb_debug ("configuration descriptor size changed, aborting\n");
 | |
| 		usb_detach_device (controller, dev->address);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * If the device is not well known (ifnum == -1), we use the first
 | |
| 	 * interface we encounter, as there was no need to implement something
 | |
| 	 * else for the time being. If you need it, see the SetInterface and
 | |
| 	 * GetInterface functions in the USB specification and set it yourself.
 | |
| 	 */
 | |
| 	usb_debug ("device has %x interfaces\n", cd->bNumInterfaces);
 | |
| 	int ifnum = usb_interface_check(dev->descriptor->idVendor,
 | |
| 					dev->descriptor->idProduct);
 | |
| 	if (cd->bNumInterfaces > 1 && ifnum < 0)
 | |
| 		usb_debug ("NOTICE: Your device has multiple interfaces and\n"
 | |
| 			   "this driver will only use the first one. That may\n"
 | |
| 			   "be the wrong choice and cause the device to not\n"
 | |
| 			   "work correctly. Please report this case\n"
 | |
| 			   "(including the above debugging output) to\n"
 | |
| 			   "coreboot@coreboot.org to have the device added to\n"
 | |
| 			   "the list of well-known quirks.\n");
 | |
| 
 | |
| 	u8 *end = (void *)dev->configuration + cd->wTotalLength;
 | |
| 	interface_descriptor_t *intf;
 | |
| 	u8 *ptr;
 | |
| 
 | |
| 	/* Find our interface (or the first good one if we don't know) */
 | |
| 	for (ptr = (void *)dev->configuration + sizeof(*cd); ; ptr += ptr[0]) {
 | |
| 		if (ptr + 2 > end || !ptr[0] || ptr + ptr[0] > end) {
 | |
| 			usb_debug ("Couldn't find usable DT_INTF\n");
 | |
| 			usb_detach_device (controller, dev->address);
 | |
| 			return -1;
 | |
| 		}
 | |
| 		if (ptr[1] != DT_INTF)
 | |
| 			continue;
 | |
| 		intf = (void *)ptr;
 | |
| 		if (intf->bLength != sizeof(*intf)) {
 | |
| 			usb_debug ("Skipping broken DT_INTF\n");
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (ifnum >= 0 && intf->bInterfaceNumber != ifnum)
 | |
| 			continue;
 | |
| 		usb_debug ("Interface %d: class 0x%x, sub 0x%x. proto 0x%x\n",
 | |
| 			intf->bInterfaceNumber, intf->bInterfaceClass,
 | |
| 			intf->bInterfaceSubClass, intf->bInterfaceProtocol);
 | |
| 		ptr += sizeof(*intf);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	/* Gather up all endpoints belonging to this inteface */
 | |
| 	dev->num_endp = 1;
 | |
| 	for (; ptr + 2 <= end && ptr[0] && ptr + ptr[0] <= end; ptr += ptr[0]) {
 | |
| 		if (ptr[1] == DT_INTF || ptr[1] == DT_CFG ||
 | |
| 				dev->num_endp >= ARRAY_SIZE(dev->endpoints))
 | |
| 			break;
 | |
| 		if (ptr[1] != DT_ENDP)
 | |
| 			continue;
 | |
| 
 | |
| 		endpoint_descriptor_t *desc = (void *)ptr;
 | |
| 		static const char *transfertypes[4] = {
 | |
| 			"control", "isochronous", "bulk", "interrupt"
 | |
| 		};
 | |
| 		usb_debug (" #Endpoint %d (%s), max packet size %x, type %s\n",
 | |
| 			desc->bEndpointAddress & 0x7f,
 | |
| 			(desc->bEndpointAddress & 0x80) ? "in" : "out",
 | |
| 			desc->wMaxPacketSize,
 | |
| 			transfertypes[desc->bmAttributes & 0x3]);
 | |
| 
 | |
| 		endpoint_t *ep = &dev->endpoints[dev->num_endp++];
 | |
| 		ep->dev = dev;
 | |
| 		ep->endpoint = desc->bEndpointAddress;
 | |
| 		ep->toggle = 0;
 | |
| 		ep->maxpacketsize = desc->wMaxPacketSize;
 | |
| 		ep->direction = (desc->bEndpointAddress & 0x80) ? IN : OUT;
 | |
| 		ep->type = desc->bmAttributes & 0x3;
 | |
| 		ep->interval = usb_decode_interval (dev->speed, ep->type,
 | |
| 						    desc->bInterval);
 | |
| 	}
 | |
| 
 | |
| 	if ((controller->finish_device_config &&
 | |
| 			controller->finish_device_config(dev)) ||
 | |
| 			set_configuration(dev) < 0) {
 | |
| 		usb_debug ("Could not finalize device configuration\n");
 | |
| 		usb_detach_device (controller, dev->address);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	int class = dev->descriptor->bDeviceClass;
 | |
| 	if (class == 0)
 | |
| 		class = intf->bInterfaceClass;
 | |
| 
 | |
| 	enum {
 | |
| 		audio_device      = 0x01,
 | |
| 		comm_device       = 0x02,
 | |
| 		hid_device        = 0x03,
 | |
| 		physical_device   = 0x05,
 | |
| 		imaging_device    = 0x06,
 | |
| 		printer_device    = 0x07,
 | |
| 		msc_device        = 0x08,
 | |
| 		hub_device        = 0x09,
 | |
| 		cdc_device        = 0x0a,
 | |
| 		ccid_device       = 0x0b,
 | |
| 		security_device   = 0x0d,
 | |
| 		video_device      = 0x0e,
 | |
| 		healthcare_device = 0x0f,
 | |
| 		diagnostic_device = 0xdc,
 | |
| 		wireless_device   = 0xe0,
 | |
| 		misc_device       = 0xef,
 | |
| 	};
 | |
| 	usb_debug("Class: ");
 | |
| 	switch (class) {
 | |
| 	case audio_device:
 | |
| 		usb_debug("audio\n");
 | |
| 		break;
 | |
| 	case comm_device:
 | |
| 		usb_debug("communication\n");
 | |
| 		break;
 | |
| 	case hid_device:
 | |
| 		usb_debug ("HID\n");
 | |
| #if IS_ENABLED(CONFIG_LP_USB_HID)
 | |
| 		dev->init = usb_hid_init;
 | |
| 		return dev->address;
 | |
| #else
 | |
| 		usb_debug ("NOTICE: USB HID support not compiled in\n");
 | |
| #endif
 | |
| 		break;
 | |
| 	case physical_device:
 | |
| 		usb_debug("physical\n");
 | |
| 		break;
 | |
| 	case imaging_device:
 | |
| 		usb_debug("camera\n");
 | |
| 		break;
 | |
| 	case printer_device:
 | |
| 		usb_debug("printer\n");
 | |
| 		break;
 | |
| 	case msc_device:
 | |
| 		usb_debug ("MSC\n");
 | |
| #if IS_ENABLED(CONFIG_LP_USB_MSC)
 | |
| 		dev->init = usb_msc_init;
 | |
| 		return dev->address;
 | |
| #else
 | |
| 		usb_debug ("NOTICE: USB MSC support not compiled in\n");
 | |
| #endif
 | |
| 		break;
 | |
| 	case hub_device:
 | |
| 		if (speed < SUPER_SPEED) {
 | |
| 			usb_debug ("hub (2.0)\n");
 | |
| #if IS_ENABLED(CONFIG_LP_USB_HUB)
 | |
| 			dev->init = usb_hub_init;
 | |
| 			return dev->address;
 | |
| #else
 | |
| 			usb_debug ("NOTICE: USB hub support not compiled in\n");
 | |
| #endif
 | |
| 		} else {
 | |
| 			usb_debug ("hub (3.0) - not yet supported!\n");
 | |
| 		}
 | |
| 		break;
 | |
| 	case cdc_device:
 | |
| 		usb_debug("CDC\n");
 | |
| 		break;
 | |
| 	case ccid_device:
 | |
| 		usb_debug("smartcard / CCID\n");
 | |
| 		break;
 | |
| 	case security_device:
 | |
| 		usb_debug("content security\n");
 | |
| 		break;
 | |
| 	case video_device:
 | |
| 		usb_debug("video\n");
 | |
| 		break;
 | |
| 	case healthcare_device:
 | |
| 		usb_debug("healthcare\n");
 | |
| 		break;
 | |
| 	case diagnostic_device:
 | |
| 		usb_debug("diagnostic\n");
 | |
| 		break;
 | |
| 	case wireless_device:
 | |
| 		usb_debug("wireless\n");
 | |
| 		break;
 | |
| 	default:
 | |
| 		usb_debug("unsupported class %x\n", class);
 | |
| 		break;
 | |
| 	}
 | |
| 	dev->init = usb_generic_init;
 | |
| 	return dev->address;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Should be called by the hub drivers whenever a physical detach occurs
 | |
|  * and can be called by usb class drivers if they are unsatisfied with a
 | |
|  * malfunctioning device.
 | |
|  */
 | |
| void
 | |
| usb_detach_device(hci_t *controller, int devno)
 | |
| {
 | |
| 	/* check if device exists, as we may have
 | |
| 	   been called yet by the usb class driver */
 | |
| 	if (controller->devices[devno]) {
 | |
| 		controller->devices[devno]->destroy (controller->devices[devno]);
 | |
| 		free(controller->devices[devno]);
 | |
| 		controller->devices[devno] = NULL;
 | |
| 		if (controller->destroy_device)
 | |
| 			controller->destroy_device(controller, devno);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int
 | |
| usb_attach_device(hci_t *controller, int hubaddress, int port, usb_speed speed)
 | |
| {
 | |
| 	static const char* speeds[] = { "full", "low", "high", "super" };
 | |
| 	usb_debug ("%sspeed device\n", (speed < sizeof(speeds) / sizeof(char*))
 | |
| 		? speeds[speed] : "invalid value - no");
 | |
| 	int newdev = set_address (controller, speed, port, hubaddress);
 | |
| 	if (newdev == -1)
 | |
| 		return -1;
 | |
| 	usbdev_t *newdev_t = controller->devices[newdev];
 | |
| 	// determine responsible driver - current done in set_address
 | |
| 	newdev_t->init (newdev_t);
 | |
| 	/* init() may have called usb_detach_device() yet, so check */
 | |
| 	return controller->devices[newdev] ? newdev : -1;
 | |
| }
 | |
| 
 | |
| static void
 | |
| usb_generic_destroy (usbdev_t *dev)
 | |
| {
 | |
| 	if (usb_generic_remove)
 | |
| 		usb_generic_remove(dev);
 | |
| }
 | |
| 
 | |
| void
 | |
| usb_generic_init (usbdev_t *dev)
 | |
| {
 | |
| 	dev->data = NULL;
 | |
| 	dev->destroy = usb_generic_destroy;
 | |
| 
 | |
| 	if (usb_generic_create)
 | |
| 		usb_generic_create(dev);
 | |
| 
 | |
| 	if (dev->data == NULL) {
 | |
| 		usb_debug("Detaching device not used by payload\n");
 | |
| 		usb_detach_device(dev->controller, dev->address);
 | |
| 	}
 | |
| }
 |