 6631803105
			
		
	
	
		6631803105
		
	
	
	
	
		
			
			They can be deactivated with -DNDEBUG, which is why they can't contain code which should always be run. It seems the local implementation of USB_ASSERT doesn't honor the CPP flags NDEBUG, this will be corrected in a later patch. Change-Id: Iac56b09fd563f1b8c4c7be15a468a05b9cc86a18
		
			
				
	
	
		
			762 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			762 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Implementation of commonly used procedures for HCD handling/initialization
 | |
|  * If possible, everything OS specific should be here
 | |
|  */
 | |
| 
 | |
| #include <string.h>			/* memset... */
 | |
| #include <time.h>			/* nanosleep */
 | |
| 
 | |
| #include <sys/mman.h>			/* Physical to virtual memory mapping */
 | |
| 
 | |
| #include <ddekit/interrupt.h>		/* DDEKit based interrupt handling */
 | |
| 
 | |
| #include <minix/clkconf.h>		/* clkconf_* */
 | |
| #include <minix/syslib.h>		/* sys_privctl */
 | |
| 
 | |
| #include <usbd/hcd_common.h>
 | |
| #include <usbd/hcd_interface.h>
 | |
| #include <usbd/usbd_common.h>
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    Local prototypes                                                       *
 | |
|  *===========================================================================*/
 | |
| /* Descriptor related operations */
 | |
| static int hcd_fill_configuration(hcd_reg1 *, int, hcd_configuration *, int);
 | |
| static int hcd_fill_interface(hcd_reg1 *, int, hcd_interface *, int);
 | |
| static int hcd_fill_endpoint(hcd_reg1 *, int, hcd_endpoint *);
 | |
| 
 | |
| /* Handling free USB device addresses */
 | |
| static hcd_reg1 hcd_reserve_addr(hcd_driver_state *);
 | |
| static void hcd_release_addr(hcd_driver_state *, hcd_reg1);
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    Local definitions                                                      *
 | |
|  *===========================================================================*/
 | |
| /* List of all allocated devices */
 | |
| static hcd_device_state * dev_list = NULL;
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_os_interrupt_attach                                                *
 | |
|  *===========================================================================*/
 | |
| int
 | |
| hcd_os_interrupt_attach(int irq, void (*init)(void *),
 | |
| 			void (*isr)(void *), void *priv)
 | |
| {
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	if (NULL == ddekit_interrupt_attach(irq, 0, init, isr, priv)) {
 | |
| 		USB_MSG("Attaching interrupt %d failed", irq);
 | |
| 		return EXIT_FAILURE;
 | |
| 	}
 | |
| 
 | |
| 	return EXIT_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_os_interrupt_detach                                                *
 | |
|  *===========================================================================*/
 | |
| void
 | |
| hcd_os_interrupt_detach(int irq)
 | |
| {
 | |
| 	DEBUG_DUMP;
 | |
| 	ddekit_interrupt_detach(irq);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_os_interrupt_enable                                                *
 | |
|  *===========================================================================*/
 | |
| void
 | |
| hcd_os_interrupt_enable(int irq)
 | |
| {
 | |
| 	DEBUG_DUMP;
 | |
| 	ddekit_interrupt_enable(irq);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_os_interrupt_disable                                               *
 | |
|  *===========================================================================*/
 | |
| void
 | |
| hcd_os_interrupt_disable(int irq)
 | |
| {
 | |
| 	DEBUG_DUMP;
 | |
| 	ddekit_interrupt_disable(irq);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_os_regs_init                                                       *
 | |
|  *===========================================================================*/
 | |
| void *
 | |
| hcd_os_regs_init(hcd_addr phys_addr, unsigned long addr_len)
 | |
| {
 | |
| 	/* Memory range where we need privileged access */
 | |
| 	struct minix_mem_range mr;
 | |
| 
 | |
| 	/* NULL unless initialization was fully completed */
 | |
| 	void * virt_reg_base;
 | |
| 
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	virt_reg_base = NULL;
 | |
| 
 | |
| 	/* Must have been set before */
 | |
| 	USB_ASSERT(0 != phys_addr, "Invalid base address!");
 | |
| 	USB_ASSERT(0 != addr_len, "Invalid base length!");
 | |
| 
 | |
| 	/* Set memory range for peripheral */
 | |
| 	mr.mr_base = phys_addr;
 | |
| 	mr.mr_limit = phys_addr + addr_len;
 | |
| 
 | |
| 	/* Try getting access to memory range */
 | |
| 	if (EXIT_SUCCESS == sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr)) {
 | |
| 
 | |
| 		/* And map it where we want it */
 | |
| 		virt_reg_base = vm_map_phys(SELF, (void *)phys_addr, addr_len);
 | |
| 
 | |
| 		/* Check for mapping errors to allow us returning NULL */
 | |
| 		if (MAP_FAILED == virt_reg_base) {
 | |
| 			USB_MSG("Mapping memory with vm_map_phys() failed");
 | |
| 			virt_reg_base = NULL;
 | |
| 		}
 | |
| 
 | |
| 	} else
 | |
| 		USB_MSG("Acquiring memory with sys_privctl() failed");
 | |
| 
 | |
| 	return virt_reg_base;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_os_regs_deinit                                                     *
 | |
|  *===========================================================================*/
 | |
| int
 | |
| hcd_os_regs_deinit(hcd_addr virt_addr, unsigned long addr_len)
 | |
| {
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	/* To keep USBD return value convention */
 | |
| 	return (0 == vm_unmap_phys(SELF, (void*)virt_addr, addr_len)) ?
 | |
| 		EXIT_SUCCESS : EXIT_FAILURE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_os_clkconf                                                         *
 | |
|  *===========================================================================*/
 | |
| int
 | |
| hcd_os_clkconf(unsigned long clk, unsigned long mask, unsigned long value)
 | |
| {
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	/* Apparently clkconf_init may be called more than once anyway */
 | |
| 	if ((0 == clkconf_init()) && (0 == clkconf_set(clk, mask, value)))
 | |
| 		return EXIT_SUCCESS;
 | |
| 	else
 | |
| 		return EXIT_FAILURE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_os_clkconf_release                                                 *
 | |
|  *===========================================================================*/
 | |
| int
 | |
| hcd_os_clkconf_release(void)
 | |
| {
 | |
| 	DEBUG_DUMP;
 | |
| 	return clkconf_release();
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_os_nanosleep                                                       *
 | |
|  *===========================================================================*/
 | |
| void
 | |
| hcd_os_nanosleep(int nanosec)
 | |
| {
 | |
| 	struct timespec nanotm;
 | |
| 	int r;
 | |
| 
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	if (nanosec >= HCD_NANO) {
 | |
| 		nanotm.tv_sec = nanosec / HCD_NANO;
 | |
| 		nanotm.tv_nsec = nanosec % HCD_NANO;
 | |
| 	} else {
 | |
| 		nanotm.tv_sec = 0;
 | |
| 		nanotm.tv_nsec = nanosec;
 | |
| 	}
 | |
| 
 | |
| 	/* TODO: Since it is not likely to be ever interrupted, we do not try
 | |
| 	 * to sleep for a remaining time in case of signal handling */
 | |
| 	/* Signal handling will most likely end up with termination anyway */
 | |
| 	r = nanosleep(&nanotm, NULL);
 | |
| 	USB_ASSERT(EXIT_SUCCESS == r, "Calling nanosleep() failed");
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_connect_device                                                     *
 | |
|  *===========================================================================*/
 | |
| int
 | |
| hcd_connect_device(hcd_device_state * this_device, hcd_thread_function funct)
 | |
| {
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	/* This is meant to allow thread name distinction
 | |
| 	 * and should not be used for anything else */
 | |
| 	static unsigned int devnum = 0;
 | |
| 
 | |
| 	/* Should be able to hold device prefix and some number */
 | |
| 	char devname[] = "dev..........";
 | |
| 
 | |
| 	USB_ASSERT((NULL == this_device->lock) &&
 | |
| 		(NULL == this_device->thread) &&
 | |
| 		(HCD_DEFAULT_ADDR == this_device->reserved_address) &&
 | |
| 		(HCD_STATE_DISCONNECTED == this_device->state),
 | |
| 		"Device structure not clean");
 | |
| 
 | |
| 	/* Mark as 'plugged in' to avoid treating device
 | |
| 	 * as 'disconnected' in case of errors below */
 | |
| 	this_device->state = HCD_STATE_CONNECTION_PENDING;
 | |
| 
 | |
| 	/* Reserve device address for further use if available */
 | |
| 	if (HCD_DEFAULT_ADDR == (this_device->reserved_address =
 | |
| 				hcd_reserve_addr(this_device->driver))) {
 | |
| 		USB_MSG("No free device addresses");
 | |
| 		return EXIT_FAILURE;
 | |
| 	}
 | |
| 
 | |
| 	/* Get 'lock' that makes device thread wait for events to occur */
 | |
| 	if (NULL == (this_device->lock = ddekit_sem_init(0))) {
 | |
| 		USB_MSG("Failed to initialize thread lock");
 | |
| 		return EXIT_FAILURE;
 | |
| 	}
 | |
| 
 | |
| 	/* Prepare name */
 | |
| 	snprintf(devname, sizeof(devname), "dev%u", devnum++);
 | |
| 
 | |
| 	/* Get thread itself */
 | |
| 	if (NULL == (this_device->thread = ddekit_thread_create(funct,
 | |
| 						this_device,
 | |
| 						(const char *)devname))) {
 | |
| 		USB_MSG("Failed to initialize USB device thread");
 | |
| 		return EXIT_FAILURE;
 | |
| 	}
 | |
| 
 | |
| 	return EXIT_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_disconnect_device                                                  *
 | |
|  *===========================================================================*/
 | |
| void
 | |
| hcd_disconnect_device(hcd_device_state * this_device)
 | |
| {
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	/* TODO: This should disconnect all the children if they exist */
 | |
| 
 | |
| 	/* Clean configuration tree in case it was acquired */
 | |
| 	hcd_tree_cleanup(&(this_device->config_tree));
 | |
| 
 | |
| 	/* Release allocated resources */
 | |
| 	if (NULL != this_device->thread)
 | |
| 		ddekit_thread_terminate(this_device->thread);
 | |
| 	if (NULL != this_device->lock)
 | |
| 		ddekit_sem_deinit(this_device->lock);
 | |
| 
 | |
| 	/* Release reserved address */
 | |
| 	if (HCD_DEFAULT_ADDR != this_device->reserved_address)
 | |
| 		hcd_release_addr(this_device->driver,
 | |
| 				this_device->reserved_address);
 | |
| 
 | |
| 	/* Mark as disconnected */
 | |
| 	this_device->state = HCD_STATE_DISCONNECTED;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_device_wait                                                        *
 | |
|  *===========================================================================*/
 | |
| void
 | |
| hcd_device_wait(hcd_device_state * device, hcd_event event, hcd_reg1 ep)
 | |
| {
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	USB_DBG("0x%08X wait (0x%02X, 0x%02X)", device, event, ep);
 | |
| 
 | |
| 	device->wait_event = event;
 | |
| 	device->wait_ep = ep;
 | |
| 
 | |
| 	ddekit_sem_down(device->lock);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_device_continue                                                    *
 | |
|  *===========================================================================*/
 | |
| void
 | |
| hcd_device_continue(hcd_device_state * device, hcd_event event, hcd_reg1 ep)
 | |
| {
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	USB_DBG("0x%08X continue (0x%02X, 0x%02X)", device, event, ep);
 | |
| 
 | |
| 	USB_ASSERT(device->wait_event == event, "Unexpected event");
 | |
| 	USB_ASSERT(device->wait_ep == ep, "Unexpected endpoint");
 | |
| 
 | |
| 	ddekit_sem_up(device->lock);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_new_device                                                         *
 | |
|  *===========================================================================*/
 | |
| hcd_device_state *
 | |
| hcd_new_device(void)
 | |
| {
 | |
| 	hcd_device_state * d;
 | |
| 
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	/* One new blank device */
 | |
| 	d = calloc(1, sizeof(*d));
 | |
| 
 | |
| 	USB_ASSERT(NULL != d, "Failed to allocate device");
 | |
| 
 | |
| 	if (NULL == dev_list) {
 | |
| 		dev_list = d;
 | |
| 	} else {
 | |
| 		d->_next = dev_list;
 | |
| 		dev_list = d;
 | |
| 	}
 | |
| 
 | |
| #ifdef HCD_DUMP_DEVICE_LIST
 | |
| 	/* Dump updated state of device list */
 | |
| 	hcd_dump_devices();
 | |
| #endif
 | |
| 
 | |
| 	return d;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_delete_device                                                      *
 | |
|  *===========================================================================*/
 | |
| void
 | |
| hcd_delete_device(hcd_device_state * d)
 | |
| {
 | |
| 	hcd_device_state * temp;
 | |
| 
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	if (d == dev_list) {
 | |
| 		dev_list = dev_list->_next;
 | |
| 	} else {
 | |
| 		temp = dev_list;
 | |
| 
 | |
| 		/* Find the device and ... */
 | |
| 		while (temp->_next != d) {
 | |
| 			USB_ASSERT(NULL != temp->_next,
 | |
| 				"Invalid state of device list");
 | |
| 			temp = temp->_next;
 | |
| 		}
 | |
| 
 | |
| 		/* ...make device list forget about it */
 | |
| 		temp->_next = temp->_next->_next;
 | |
| 	}
 | |
| 
 | |
| 	free(d);
 | |
| 
 | |
| #ifdef HCD_DUMP_DEVICE_LIST
 | |
| 	/* Dump updated state of device list */
 | |
| 	hcd_dump_devices();
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_dump_devices                                                       *
 | |
|  *===========================================================================*/
 | |
| void
 | |
| hcd_dump_devices(void)
 | |
| {
 | |
| 	hcd_device_state * temp;
 | |
| 
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	temp = dev_list;
 | |
| 
 | |
| 	USB_MSG("Allocated devices:");
 | |
| 
 | |
| 	while (NULL != temp) {
 | |
| 		USB_MSG("0x%08X", (int)temp);
 | |
| 		temp = temp->_next;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_check_device                                                       *
 | |
|  *===========================================================================*/
 | |
| int
 | |
| hcd_check_device(hcd_device_state * d)
 | |
| {
 | |
| 	hcd_device_state * temp;
 | |
| 
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	temp = dev_list;
 | |
| 
 | |
| 	/* Traverse the list of allocated devices
 | |
| 	 * to determine validity of this one */
 | |
| 	while (NULL != temp) {
 | |
| 		if (temp == d)
 | |
| 			return EXIT_SUCCESS; /* Device found within the list */
 | |
| 		temp = temp->_next;
 | |
| 	}
 | |
| 
 | |
| 	/* Device was not found, may have been removed earlier */
 | |
| 	return EXIT_FAILURE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_buffer_to_tree                                                     *
 | |
|  *===========================================================================*/
 | |
| int
 | |
| hcd_buffer_to_tree(hcd_reg1 * buf, int len, hcd_configuration * c)
 | |
| {
 | |
| 	hcd_interface * i;
 | |
| 	hcd_endpoint * e;
 | |
| 	hcd_descriptor * desc;
 | |
| 	int cfg_num;
 | |
| 	int if_num;
 | |
| 	int ep_num;
 | |
| 
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	cfg_num = 0;
 | |
| 	if_num = 0;
 | |
| 	ep_num = 0;
 | |
| 
 | |
| 	i = NULL;
 | |
| 	e = NULL;
 | |
| 
 | |
| 	/* Cleanup initially to NULL pointers before any allocation */
 | |
| 	memset(c, 0, sizeof(*c));
 | |
| 
 | |
| 	while (len > (int)sizeof(*desc)) {
 | |
| 		/* Check descriptor type */
 | |
| 		desc = (hcd_descriptor *)buf;
 | |
| 
 | |
| 		if (0 == desc->bLength) {
 | |
| 			USB_MSG("Zero length descriptor");
 | |
| 			goto PARSE_ERROR;
 | |
| 		}
 | |
| 
 | |
| 		if (UDESC_CONFIG == desc->bDescriptorType) {
 | |
| 			if (EXIT_SUCCESS != hcd_fill_configuration(buf, len,
 | |
| 								c, cfg_num++))
 | |
| 				goto PARSE_ERROR;
 | |
| 
 | |
| 			if_num = 0;
 | |
| 		}
 | |
| 		else if (UDESC_INTERFACE == desc->bDescriptorType) {
 | |
| 			if (NULL == c->interface)
 | |
| 				goto PARSE_ERROR;
 | |
| 
 | |
| 			i = &(c->interface[if_num]);
 | |
| 
 | |
| 			if (EXIT_SUCCESS != hcd_fill_interface(buf, len,
 | |
| 								i, if_num++))
 | |
| 				goto PARSE_ERROR;
 | |
| 
 | |
| 			ep_num = 0;
 | |
| 		}
 | |
| 		else if (UDESC_ENDPOINT == desc->bDescriptorType) {
 | |
| 			if (NULL == c->interface)
 | |
| 				goto PARSE_ERROR;
 | |
| 
 | |
| 			if (NULL == i)
 | |
| 				goto PARSE_ERROR;
 | |
| 
 | |
| 			e = &(i->endpoint[ep_num++]);
 | |
| 
 | |
| 			if (EXIT_SUCCESS != hcd_fill_endpoint(buf, len, e))
 | |
| 				goto PARSE_ERROR;
 | |
| 		} else
 | |
| 			USB_DBG("Unhandled descriptor type 0x%02X",
 | |
| 				desc->bDescriptorType);
 | |
| 
 | |
| 		len -= desc->bLength;
 | |
| 		buf += desc->bLength;
 | |
| 	}
 | |
| 
 | |
| 	if (0 != len) {
 | |
| 		USB_MSG("After parsing, some descriptor data remains");
 | |
| 		goto PARSE_ERROR;
 | |
| 	}
 | |
| 
 | |
| 	return EXIT_SUCCESS;
 | |
| 
 | |
| 	PARSE_ERROR:
 | |
| 	hcd_tree_cleanup(c);
 | |
| 	return EXIT_FAILURE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_tree_cleanup                                                       *
 | |
|  *===========================================================================*/
 | |
| void
 | |
| hcd_tree_cleanup(hcd_configuration * c)
 | |
| {
 | |
| 	int if_idx;
 | |
| 
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	/* Free if anything was allocated */
 | |
| 	if (NULL != c->interface) {
 | |
| 
 | |
| 		USB_ASSERT(c->num_interfaces > 0, "Interface number error");
 | |
| 
 | |
| 		for (if_idx = 0; if_idx < c->num_interfaces; if_idx++) {
 | |
| 			if (NULL != c->interface[if_idx].endpoint) {
 | |
| 				USB_DBG("Freeing ep for interface #%d", if_idx);
 | |
| 				free(c->interface[if_idx].endpoint);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		USB_DBG("Freeing interfaces");
 | |
| 		free(c->interface);
 | |
| 		c->interface = NULL;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_tree_find_ep                                                       *
 | |
|  *===========================================================================*/
 | |
| hcd_endpoint *
 | |
| hcd_tree_find_ep(hcd_configuration * c, hcd_reg1 ep)
 | |
| {
 | |
| 	hcd_interface * i;
 | |
| 	hcd_endpoint * e;
 | |
| 	int if_idx;
 | |
| 	int ep_idx;
 | |
| 
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	/* Free if anything was allocated */
 | |
| 	USB_ASSERT(NULL != c->interface, "No interfaces available");
 | |
| 	USB_ASSERT(c->num_interfaces > 0, "Interface number error");
 | |
| 
 | |
| 	for (if_idx = 0; if_idx < c->num_interfaces; if_idx++) {
 | |
| 		i = &(c->interface[if_idx]);
 | |
| 		for (ep_idx = 0; ep_idx < i->num_endpoints; ep_idx++) {
 | |
| 			e = &(i->endpoint[ep_idx]);
 | |
| 			if (UE_GET_ADDR(e->descriptor.bEndpointAddress) == ep)
 | |
| 				return e;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_fill_configuration                                                 *
 | |
|  *===========================================================================*/
 | |
| static int
 | |
| hcd_fill_configuration(hcd_reg1 * buf, int len, hcd_configuration * c, int num)
 | |
| {
 | |
| 	hcd_config_descriptor * desc;
 | |
| 	int interfaces_size;
 | |
| 
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	desc = (hcd_config_descriptor *)buf;
 | |
| 
 | |
| 	USB_DBG("Configuration #%d", num);
 | |
| 
 | |
| 	if (num > 0) {
 | |
| 		USB_DBG("Only one configuration possible");
 | |
| 		return EXIT_SUCCESS;
 | |
| 	}
 | |
| 
 | |
| 	if (UDESC_CONFIG != desc->bDescriptorType)
 | |
| 		return EXIT_FAILURE;
 | |
| 
 | |
| 	if (desc->bLength > len)
 | |
| 		return EXIT_FAILURE;
 | |
| 
 | |
| 	if (sizeof(*desc) != desc->bLength)
 | |
| 		return EXIT_FAILURE;
 | |
| 
 | |
| 	memcpy(&(c->descriptor), buf, sizeof(c->descriptor));
 | |
| 
 | |
| 	c->num_interfaces = c->descriptor.bNumInterface;
 | |
| 
 | |
| 	interfaces_size = c->num_interfaces * sizeof(*(c->interface));
 | |
| 
 | |
| 	USB_DBG("Allocating interfaces, %dB", interfaces_size);
 | |
| 	c->interface = malloc(interfaces_size);
 | |
| 
 | |
| 	memset(c->interface, 0, interfaces_size);
 | |
| 
 | |
| 	/* Dump configuration in debug mode */
 | |
| 	USB_DBG("<<CONFIGURATION>>");
 | |
| 	USB_DBG("bLength %02X",			desc->bLength);
 | |
| 	USB_DBG("bDescriptorType %02X",		desc->bDescriptorType);
 | |
| 	USB_DBG("wTotalLength %04X",		UGETW(desc->wTotalLength));
 | |
| 	USB_DBG("bNumInterface %02X",		desc->bNumInterface);
 | |
| 	USB_DBG("bConfigurationValue %02X",	desc->bConfigurationValue);
 | |
| 	USB_DBG("iConfiguration %02X",		desc->iConfiguration);
 | |
| 	USB_DBG("bmAttributes %02X",		desc->bmAttributes);
 | |
| 	USB_DBG("bMaxPower %02X",		desc->bMaxPower);
 | |
| 
 | |
| 	return EXIT_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_fill_interface                                                     *
 | |
|  *===========================================================================*/
 | |
| static int
 | |
| hcd_fill_interface(hcd_reg1 * buf, int len, hcd_interface * i, int num)
 | |
| {
 | |
| 	hcd_interface_descriptor * desc;
 | |
| 	int endpoints_size;
 | |
| 
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	desc = (hcd_interface_descriptor *)buf;
 | |
| 
 | |
| 	USB_DBG("Interface #%d", num);
 | |
| 
 | |
| 	if (UDESC_INTERFACE != desc->bDescriptorType)
 | |
| 		return EXIT_FAILURE;
 | |
| 
 | |
| 	if (desc->bLength > len)
 | |
| 		return EXIT_FAILURE;
 | |
| 
 | |
| 	if (sizeof(*desc) != desc->bLength)
 | |
| 		return EXIT_FAILURE;
 | |
| 
 | |
| 	/* It is mandatory to supply interfaces in correct order */
 | |
| 	if (desc->bInterfaceNumber != num)
 | |
| 		return EXIT_FAILURE;
 | |
| 
 | |
| 	memcpy(&(i->descriptor), buf, sizeof(i->descriptor));
 | |
| 
 | |
| 	i->num_endpoints = i->descriptor.bNumEndpoints;
 | |
| 
 | |
| 	endpoints_size = i->num_endpoints * sizeof(*(i->endpoint));
 | |
| 
 | |
| 	USB_DBG("Allocating endpoints, %dB", endpoints_size);
 | |
| 	i->endpoint = malloc(endpoints_size);
 | |
| 
 | |
| 	memset(i->endpoint, 0, endpoints_size);
 | |
| 
 | |
| 	/* Dump interface in debug mode */
 | |
| 	USB_DBG("<<INTERFACE>>");
 | |
| 	USB_DBG("bLength %02X",			desc->bLength);
 | |
| 	USB_DBG("bDescriptorType %02X",		desc->bDescriptorType);
 | |
| 	USB_DBG("bInterfaceNumber %02X",	desc->bInterfaceNumber);
 | |
| 	USB_DBG("bAlternateSetting %02X",	desc->bAlternateSetting);
 | |
| 	USB_DBG("bNumEndpoints %02X",		desc->bNumEndpoints);
 | |
| 	USB_DBG("bInterfaceClass %02X",		desc->bInterfaceClass);
 | |
| 	USB_DBG("bInterfaceSubClass %02X",	desc->bInterfaceSubClass);
 | |
| 	USB_DBG("bInterfaceProtocol %02X",	desc->bInterfaceProtocol);
 | |
| 	USB_DBG("iInterface %02X",		desc->iInterface);
 | |
| 
 | |
| 	return EXIT_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_fill_endpoint                                                      *
 | |
|  *===========================================================================*/
 | |
| static int
 | |
| hcd_fill_endpoint(hcd_reg1 * buf, int len, hcd_endpoint * e)
 | |
| {
 | |
| 	hcd_endpoint_descriptor * desc;
 | |
| 
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	desc = (hcd_endpoint_descriptor *)buf;
 | |
| 
 | |
| 	USB_DBG("Endpoint #%d", UE_GET_ADDR(desc->bEndpointAddress));
 | |
| 
 | |
| 	if (UDESC_ENDPOINT != desc->bDescriptorType)
 | |
| 		return EXIT_FAILURE;
 | |
| 
 | |
| 	if (desc->bLength > len)
 | |
| 		return EXIT_FAILURE;
 | |
| 
 | |
| 	if (sizeof(*desc) != desc->bLength)
 | |
| 		return EXIT_FAILURE;
 | |
| 
 | |
| 	memcpy(&(e->descriptor), buf, sizeof(e->descriptor));
 | |
| 
 | |
| 	/* Dump endpoint in debug mode */
 | |
| 	USB_DBG("<<ENDPOINT>>");
 | |
| 	USB_DBG("bLength %02X",			desc->bLength);
 | |
| 	USB_DBG("bDescriptorType %02X",		desc->bDescriptorType);
 | |
| 	USB_DBG("bEndpointAddress %02X",	desc->bEndpointAddress);
 | |
| 	USB_DBG("bmAttributes %02X",		desc->bmAttributes);
 | |
| 	USB_DBG("wMaxPacketSize %04X",		UGETW(desc->wMaxPacketSize));
 | |
| 	USB_DBG("bInterval %02X",		desc->bInterval);
 | |
| 
 | |
| 	return EXIT_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_reserve_addr                                                       *
 | |
|  *===========================================================================*/
 | |
| static hcd_reg1
 | |
| hcd_reserve_addr(hcd_driver_state * driver)
 | |
| {
 | |
| 	hcd_reg1 addr;
 | |
| 
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	for (addr = HCD_FIRST_ADDR; addr <= HCD_LAST_ADDR; addr++) {
 | |
| 		if (HCD_ADDR_AVAILABLE == driver->dev_addr[addr]) {
 | |
| 			USB_DBG("Reserved address: %u", addr);
 | |
| 			driver->dev_addr[addr] = HCD_ADDR_USED;
 | |
| 			return addr;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* This means error */
 | |
| 	return HCD_DEFAULT_ADDR;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *    hcd_release_addr                                                       *
 | |
|  *===========================================================================*/
 | |
| static void
 | |
| hcd_release_addr(hcd_driver_state * driver, hcd_reg1 addr)
 | |
| {
 | |
| 	DEBUG_DUMP;
 | |
| 
 | |
| 	USB_ASSERT((addr > HCD_DEFAULT_ADDR) && (addr <= HCD_LAST_ADDR),
 | |
| 		"Invalid device address to be released");
 | |
| 	USB_ASSERT(HCD_ADDR_USED == driver->dev_addr[addr],
 | |
| 		"Attempted to release unused address");
 | |
| 
 | |
| 	USB_DBG("Released address: %u", addr);
 | |
| 	driver->dev_addr[addr] = HCD_ADDR_AVAILABLE;
 | |
| }
 |