xref: /minix3/minix/drivers/usb/usbd/hcd/hcd.c (revision 3c8950cce94bbea7236b2d07ae8e59f07e4432c5)
1433d6423SLionel Sambuc /*
2433d6423SLionel Sambuc  * Implementation of generic HCD
3433d6423SLionel Sambuc  */
4433d6423SLionel Sambuc 
5433d6423SLionel Sambuc #include <string.h>				/* memcpy */
6433d6423SLionel Sambuc 
7433d6423SLionel Sambuc #include <minix/drivers.h>			/* errno with sign */
8433d6423SLionel Sambuc 
92d64210cSWojciech Zajac #include <usbd/hcd_common.h>
102d64210cSWojciech Zajac #include <usbd/hcd_ddekit.h>
112d64210cSWojciech Zajac #include <usbd/hcd_interface.h>
122d64210cSWojciech Zajac #include <usbd/hcd_schedule.h>
132d64210cSWojciech Zajac #include <usbd/usbd_common.h>
14433d6423SLionel Sambuc 
15433d6423SLionel Sambuc 
16433d6423SLionel Sambuc /*===========================================================================*
17433d6423SLionel Sambuc  *    Local declarations                                                     *
18433d6423SLionel Sambuc  *===========================================================================*/
19433d6423SLionel Sambuc /* Thread to handle device logic */
20433d6423SLionel Sambuc static void hcd_device_thread(void *);
21433d6423SLionel Sambuc 
22433d6423SLionel Sambuc /* Procedure that locks device thread forever in case of error/completion */
23433d6423SLionel Sambuc static void hcd_device_finish(hcd_device_state *, const char *);
24433d6423SLionel Sambuc 
252d64210cSWojciech Zajac /* Procedure that finds device, waiting for given EP interrupt */
262d64210cSWojciech Zajac static hcd_device_state * hcd_get_child_for_ep(hcd_device_state *, hcd_reg1);
272d64210cSWojciech Zajac 
282d64210cSWojciech Zajac /* For HCD level, hub handling */
292d64210cSWojciech Zajac static void hcd_add_child(hcd_device_state *, hcd_reg1, hcd_speed);
302d64210cSWojciech Zajac static void hcd_delete_child(hcd_device_state *, hcd_reg1);
312d64210cSWojciech Zajac static void hcd_disconnect_tree(hcd_device_state *);
322d64210cSWojciech Zajac static void hcd_dump_tree(hcd_device_state *, hcd_reg1);
332d64210cSWojciech Zajac 
34433d6423SLionel Sambuc /* Typical USD device communication procedures */
35433d6423SLionel Sambuc static int hcd_enumerate(hcd_device_state *);
36433d6423SLionel Sambuc static int hcd_get_device_descriptor(hcd_device_state *);
372d64210cSWojciech Zajac static int hcd_set_address(hcd_device_state *);
38433d6423SLionel Sambuc static int hcd_get_descriptor_tree(hcd_device_state *);
39433d6423SLionel Sambuc static int hcd_set_configuration(hcd_device_state *, hcd_reg1);
402d64210cSWojciech Zajac static void hcd_handle_urb(hcd_device_state *);
412d64210cSWojciech Zajac static void hcd_complete_urb(hcd_device_state *);
42433d6423SLionel Sambuc static int hcd_control_urb(hcd_device_state *, hcd_urb *);
43433d6423SLionel Sambuc static int hcd_non_control_urb(hcd_device_state *, hcd_urb *);
44433d6423SLionel Sambuc 
45433d6423SLionel Sambuc /* For internal use by more general methods */
46433d6423SLionel Sambuc static int hcd_setup_packet(hcd_device_state *, hcd_ctrlrequest *, hcd_reg1);
472d64210cSWojciech Zajac static int hcd_finish_setup(hcd_device_state *, void *);
48433d6423SLionel Sambuc static int hcd_data_transfer(hcd_device_state *, hcd_datarequest *);
49433d6423SLionel Sambuc 
502d64210cSWojciech Zajac /* TODO: This is not meant to be explicitly visible outside DDEKit library
512d64210cSWojciech Zajac  * but there is no other way to set thread priority for now */
522d64210cSWojciech Zajac extern void _ddekit_thread_set_myprio(int);
532d64210cSWojciech Zajac 
54433d6423SLionel Sambuc 
55433d6423SLionel Sambuc /*===========================================================================*
56433d6423SLionel Sambuc  *    Local definitions                                                      *
57433d6423SLionel Sambuc  *===========================================================================*/
58433d6423SLionel Sambuc /* TODO: This was added for compatibility with DDELinux drivers that
59433d6423SLionel Sambuc  * allow receiving less data than expected in URB, without error */
60433d6423SLionel Sambuc #define HCD_ANY_LENGTH 0xFFFFFFFFu
61433d6423SLionel Sambuc 
622d64210cSWojciech Zajac /* This doesn't seem to be specified in standard but abnormal values
632d64210cSWojciech Zajac  * are unlikely so check for this was added below */
642d64210cSWojciech Zajac #define HCD_SANE_DESCRIPTOR_LENGTH 2048
652d64210cSWojciech Zajac 
66433d6423SLionel Sambuc 
67433d6423SLionel Sambuc /*===========================================================================*
68433d6423SLionel Sambuc  *    hcd_handle_event                                                       *
69433d6423SLionel Sambuc  *===========================================================================*/
70433d6423SLionel Sambuc void
hcd_handle_event(hcd_device_state * device,hcd_event event,hcd_reg1 val)712d64210cSWojciech Zajac hcd_handle_event(hcd_device_state * device, hcd_event event, hcd_reg1 val)
72433d6423SLionel Sambuc {
73433d6423SLionel Sambuc 	DEBUG_DUMP;
74433d6423SLionel Sambuc 
752d64210cSWojciech Zajac 	/* Invalid device may be supplied */
762d64210cSWojciech Zajac 	if (EXIT_SUCCESS != hcd_check_device(device)) {
772d64210cSWojciech Zajac 		USB_MSG("No device available for event: 0x%02X, value: 0x%02X",
782d64210cSWojciech Zajac 			event, val);
792d64210cSWojciech Zajac 		return;
802d64210cSWojciech Zajac 	}
81433d6423SLionel Sambuc 
822d64210cSWojciech Zajac #ifdef HCD_DUMP_DEVICE_TREE
832d64210cSWojciech Zajac 	/* This can be unlocked to dump current USB device tree on event */
842d64210cSWojciech Zajac 	{
852d64210cSWojciech Zajac 		/* Go to the base of USB device tree and
862d64210cSWojciech Zajac 		 * print the current state of it */
872d64210cSWojciech Zajac 		hcd_device_state * base;
88433d6423SLionel Sambuc 
892d64210cSWojciech Zajac 		base = device;
902d64210cSWojciech Zajac 
912d64210cSWojciech Zajac 		while (NULL != base->parent)
922d64210cSWojciech Zajac 			base = base->parent;
932d64210cSWojciech Zajac 
942d64210cSWojciech Zajac 		USB_MSG("Current state of USB device tree:");
952d64210cSWojciech Zajac 		hcd_dump_tree(base, 0);
962d64210cSWojciech Zajac 	}
972d64210cSWojciech Zajac #endif
98433d6423SLionel Sambuc 
99433d6423SLionel Sambuc 	/* Handle event and forward control to device thread when required */
1002d64210cSWojciech Zajac 	switch (event) {
101433d6423SLionel Sambuc 		case HCD_EVENT_CONNECTED:
1022d64210cSWojciech Zajac 			USB_ASSERT((HCD_STATE_DISCONNECTED == device->state),
1032d64210cSWojciech Zajac 				"Device not marked as 'disconnected' "
104433d6423SLionel Sambuc 				"for 'connection' event");
105433d6423SLionel Sambuc 
1062d64210cSWojciech Zajac 			/* Try creating new thread for device */
1072d64210cSWojciech Zajac 			if (hcd_connect_device(device, hcd_device_thread))
1082d64210cSWojciech Zajac 				USB_MSG("Device creation failed, nothing more "
1092d64210cSWojciech Zajac 					"will happen until disconnected");
1102d64210cSWojciech Zajac 
111433d6423SLionel Sambuc 			break;
112433d6423SLionel Sambuc 
113433d6423SLionel Sambuc 		case HCD_EVENT_DISCONNECTED:
1142d64210cSWojciech Zajac 			USB_ASSERT((HCD_STATE_DISCONNECTED != device->state),
1152d64210cSWojciech Zajac 				"Device is marked as 'disconnected' "
116433d6423SLionel Sambuc 				"for 'disconnection' event");
117433d6423SLionel Sambuc 
1182d64210cSWojciech Zajac 			/* Make this device and all attached children
1192d64210cSWojciech Zajac 			 * disconnect recursively */
1202d64210cSWojciech Zajac 			hcd_disconnect_tree(device);
1212d64210cSWojciech Zajac 
1222d64210cSWojciech Zajac 			break;
1232d64210cSWojciech Zajac 
1242d64210cSWojciech Zajac 		case HCD_EVENT_PORT_LS_CONNECTED:
1252d64210cSWojciech Zajac 			USB_ASSERT((HCD_STATE_DISCONNECTED != device->state),
1262d64210cSWojciech Zajac 				"Device is marked as 'disconnected' "
1272d64210cSWojciech Zajac 				"for 'hub port LS attach' event");
1282d64210cSWojciech Zajac 
1292d64210cSWojciech Zajac 			USB_MSG("Low speed device connected at "
130*3c8950ccSBen Gras 				"hub 0x%p, port %u", device, val);
1312d64210cSWojciech Zajac 
1322d64210cSWojciech Zajac 			hcd_add_child(device, val, HCD_SPEED_LOW);
1332d64210cSWojciech Zajac 			break;
1342d64210cSWojciech Zajac 
1352d64210cSWojciech Zajac 		case HCD_EVENT_PORT_FS_CONNECTED:
1362d64210cSWojciech Zajac 			USB_ASSERT((HCD_STATE_DISCONNECTED != device->state),
1372d64210cSWojciech Zajac 				"Device is marked as 'disconnected' "
1382d64210cSWojciech Zajac 				"for 'hub port FS attach' event");
1392d64210cSWojciech Zajac 
1402d64210cSWojciech Zajac 			USB_MSG("Full speed device connected at "
141*3c8950ccSBen Gras 				"hub 0x%p, port %u", device, val);
1422d64210cSWojciech Zajac 
1432d64210cSWojciech Zajac 			hcd_add_child(device, val, HCD_SPEED_FULL);
1442d64210cSWojciech Zajac 			break;
1452d64210cSWojciech Zajac 
1462d64210cSWojciech Zajac 		case HCD_EVENT_PORT_HS_CONNECTED:
1472d64210cSWojciech Zajac 			USB_ASSERT((HCD_STATE_DISCONNECTED != device->state),
1482d64210cSWojciech Zajac 				"Device is marked as 'disconnected' "
1492d64210cSWojciech Zajac 				"for 'hub port HS attach' event");
1502d64210cSWojciech Zajac 
1512d64210cSWojciech Zajac 			USB_MSG("High speed device connected at "
152*3c8950ccSBen Gras 				"hub 0x%p, port %u", device, val);
1532d64210cSWojciech Zajac 
1542d64210cSWojciech Zajac 			hcd_add_child(device, val, HCD_SPEED_HIGH);
1552d64210cSWojciech Zajac 			break;
1562d64210cSWojciech Zajac 
1572d64210cSWojciech Zajac 		case HCD_EVENT_PORT_DISCONNECTED:
1582d64210cSWojciech Zajac 			USB_ASSERT((HCD_STATE_DISCONNECTED != device->state),
1592d64210cSWojciech Zajac 				"Device is marked as 'disconnected' "
1602d64210cSWojciech Zajac 				"for 'hub port detach' event");
1612d64210cSWojciech Zajac 
1622d64210cSWojciech Zajac 			hcd_delete_child(device, val);
1632d64210cSWojciech Zajac 
1642d64210cSWojciech Zajac 			USB_MSG("Device disconnected from "
165*3c8950ccSBen Gras 				"hub 0x%p, port %u", device, val);
1662d64210cSWojciech Zajac 
167433d6423SLionel Sambuc 			break;
168433d6423SLionel Sambuc 
169433d6423SLionel Sambuc 		case HCD_EVENT_ENDPOINT:
1702d64210cSWojciech Zajac 			USB_ASSERT((HCD_STATE_DISCONNECTED != device->state),
1712d64210cSWojciech Zajac 				"Parent device is marked as 'disconnected' "
1722d64210cSWojciech Zajac 				"for 'endpoint' event");
1732d64210cSWojciech Zajac 
1742d64210cSWojciech Zajac 			/* Alters 'device' when endpoint is allocated to
1752d64210cSWojciech Zajac 			 * child rather than parent (hub), which allows
1762d64210cSWojciech Zajac 			 * proper thread to continue */
1772d64210cSWojciech Zajac 			device = hcd_get_child_for_ep(device, val);
1782d64210cSWojciech Zajac 
1792d64210cSWojciech Zajac 			/* Check if anything at all, waits for such endpoint */
1802d64210cSWojciech Zajac 			if (device)
1812d64210cSWojciech Zajac 				/* Allow device thread, waiting for endpoint
1822d64210cSWojciech Zajac 				 * event, to continue with its logic */
1832d64210cSWojciech Zajac 				hcd_device_continue(device, event, val);
184433d6423SLionel Sambuc 			else
1852d64210cSWojciech Zajac 				USB_MSG("No device waits for endpoint %u", val);
1862d64210cSWojciech Zajac 
1872d64210cSWojciech Zajac 			break;
1882d64210cSWojciech Zajac 
1892d64210cSWojciech Zajac 		case HCD_EVENT_URB:
1902d64210cSWojciech Zajac 			USB_ASSERT((HCD_STATE_DISCONNECTED != device->state),
1912d64210cSWojciech Zajac 				"Device is marked as 'disconnected' "
1922d64210cSWojciech Zajac 				"for 'URB' event");
1932d64210cSWojciech Zajac 
1942d64210cSWojciech Zajac 			/* Allow device thread to continue with it's logic */
1952d64210cSWojciech Zajac 			hcd_device_continue(device, event, val);
196433d6423SLionel Sambuc 
197433d6423SLionel Sambuc 			break;
198433d6423SLionel Sambuc 
199433d6423SLionel Sambuc 		default:
200433d6423SLionel Sambuc 			USB_ASSERT(0, "Illegal HCD event");
2012d64210cSWojciech Zajac 	}
2022d64210cSWojciech Zajac }
2032d64210cSWojciech Zajac 
2042d64210cSWojciech Zajac 
2052d64210cSWojciech Zajac /*===========================================================================*
2062d64210cSWojciech Zajac  *    hcd_update_port                                                        *
2072d64210cSWojciech Zajac  *===========================================================================*/
2082d64210cSWojciech Zajac void
hcd_update_port(hcd_driver_state * driver,hcd_event event)2092d64210cSWojciech Zajac hcd_update_port(hcd_driver_state * driver, hcd_event event)
2102d64210cSWojciech Zajac {
2112d64210cSWojciech Zajac 	DEBUG_DUMP;
2122d64210cSWojciech Zajac 
2132d64210cSWojciech Zajac 	switch (event) {
2142d64210cSWojciech Zajac 		case HCD_EVENT_CONNECTED:
2152d64210cSWojciech Zajac 			/* Check if already assigned */
2162d64210cSWojciech Zajac 			USB_ASSERT(NULL == driver->port_device,
2172d64210cSWojciech Zajac 				"Device was already connected before "
2182d64210cSWojciech Zajac 				"receiving 'connection' event");
2192d64210cSWojciech Zajac 
2202d64210cSWojciech Zajac 			/* Assign new blank device */
2212d64210cSWojciech Zajac 			driver->port_device = hcd_new_device();
2222d64210cSWojciech Zajac 
2232d64210cSWojciech Zajac 			/* Associate this device with driver */
2242d64210cSWojciech Zajac 			driver->port_device->driver = driver;
225433d6423SLionel Sambuc 			break;
2262d64210cSWojciech Zajac 
2272d64210cSWojciech Zajac 		case HCD_EVENT_DISCONNECTED:
2282d64210cSWojciech Zajac 			/* Check if already released */
2292d64210cSWojciech Zajac 			USB_ASSERT(NULL != driver->port_device,
2302d64210cSWojciech Zajac 				"Device was already disconnected before "
2312d64210cSWojciech Zajac 				"receiving 'disconnection' event");
2322d64210cSWojciech Zajac 
2332d64210cSWojciech Zajac 			/* Release device */
2342d64210cSWojciech Zajac 			hcd_delete_device(driver->port_device);
2352d64210cSWojciech Zajac 
2362d64210cSWojciech Zajac 			/* Clear port device pointer */
2372d64210cSWojciech Zajac 			driver->port_device = NULL;
2382d64210cSWojciech Zajac 			break;
2392d64210cSWojciech Zajac 
2402d64210cSWojciech Zajac 		default:
2412d64210cSWojciech Zajac 			USB_ASSERT(0, "Illegal port update event");
242433d6423SLionel Sambuc 	}
243433d6423SLionel Sambuc }
244433d6423SLionel Sambuc 
245433d6423SLionel Sambuc 
246433d6423SLionel Sambuc /*===========================================================================*
247433d6423SLionel Sambuc  *    hcd_device_thread                                                      *
248433d6423SLionel Sambuc  *===========================================================================*/
249433d6423SLionel Sambuc static void
hcd_device_thread(void * thread_args)250433d6423SLionel Sambuc hcd_device_thread(void * thread_args)
251433d6423SLionel Sambuc {
252433d6423SLionel Sambuc 	hcd_device_state * this_device;
253433d6423SLionel Sambuc 
254433d6423SLionel Sambuc 	DEBUG_DUMP;
255433d6423SLionel Sambuc 
2562d64210cSWojciech Zajac 	/* Set device thread priority higher so it
2572d64210cSWojciech Zajac 	 * won't change context unless explicitly locked */
2582d64210cSWojciech Zajac 	_ddekit_thread_set_myprio(2);
2592d64210cSWojciech Zajac 
260433d6423SLionel Sambuc 	/* Retrieve structures from generic data */
261433d6423SLionel Sambuc 	this_device = (hcd_device_state *)thread_args;
262433d6423SLionel Sambuc 
263433d6423SLionel Sambuc 	/* Enumeration sequence */
264433d6423SLionel Sambuc 	if (EXIT_SUCCESS != hcd_enumerate(this_device))
265433d6423SLionel Sambuc 		hcd_device_finish(this_device, "USB device enumeration failed");
266433d6423SLionel Sambuc 
267433d6423SLionel Sambuc 	/* Tell everyone that device was connected */
268433d6423SLionel Sambuc 	hcd_connect_cb(this_device);
269433d6423SLionel Sambuc 
270433d6423SLionel Sambuc 	/* Fully configured */
271433d6423SLionel Sambuc 	this_device->state = HCD_STATE_CONNECTED;
272433d6423SLionel Sambuc 
273433d6423SLionel Sambuc 	USB_DBG("Waiting for URBs");
274433d6423SLionel Sambuc 
275433d6423SLionel Sambuc 	/* Start handling URB's */
276433d6423SLionel Sambuc 	for(;;) {
277433d6423SLionel Sambuc 		/* Block and wait for something like 'submit URB' */
2782d64210cSWojciech Zajac 		hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL);
2792d64210cSWojciech Zajac 		hcd_handle_urb(this_device);
280433d6423SLionel Sambuc 	}
281433d6423SLionel Sambuc 
282433d6423SLionel Sambuc 	/* Finish device handling to avoid leaving thread */
283433d6423SLionel Sambuc 	hcd_device_finish(this_device, "USB device handling completed");
284433d6423SLionel Sambuc }
285433d6423SLionel Sambuc 
286433d6423SLionel Sambuc 
287433d6423SLionel Sambuc /*===========================================================================*
288433d6423SLionel Sambuc  *    hcd_device_finish                                                      *
289433d6423SLionel Sambuc  *===========================================================================*/
290433d6423SLionel Sambuc static void
hcd_device_finish(hcd_device_state * this_device,const char * finish_msg)291433d6423SLionel Sambuc hcd_device_finish(hcd_device_state * this_device, const char * finish_msg)
292433d6423SLionel Sambuc {
293433d6423SLionel Sambuc 	DEBUG_DUMP;
294433d6423SLionel Sambuc 
295433d6423SLionel Sambuc 	USB_MSG("USB device handling finished with message: '%s'", finish_msg);
296433d6423SLionel Sambuc 
297433d6423SLionel Sambuc 	/* Lock forever */
298433d6423SLionel Sambuc 	for (;;) {
2992d64210cSWojciech Zajac 		hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL);
300433d6423SLionel Sambuc 		USB_MSG("Failed attempt to continue finished thread");
301433d6423SLionel Sambuc 	}
302433d6423SLionel Sambuc }
303433d6423SLionel Sambuc 
304433d6423SLionel Sambuc 
305433d6423SLionel Sambuc /*===========================================================================*
3062d64210cSWojciech Zajac  *    hcd_get_child_for_ep                                                   *
3072d64210cSWojciech Zajac  *===========================================================================*/
3082d64210cSWojciech Zajac static hcd_device_state *
hcd_get_child_for_ep(hcd_device_state * device,hcd_reg1 ep)3092d64210cSWojciech Zajac hcd_get_child_for_ep(hcd_device_state * device, hcd_reg1 ep)
3102d64210cSWojciech Zajac {
3112d64210cSWojciech Zajac 	hcd_device_state * child_found;
3122d64210cSWojciech Zajac 	hcd_device_state * final_found;
3132d64210cSWojciech Zajac 	hcd_device_state * child;
3142d64210cSWojciech Zajac 	hcd_reg1 child_num;
3152d64210cSWojciech Zajac 
3162d64210cSWojciech Zajac 	DEBUG_DUMP;
3172d64210cSWojciech Zajac 
3182d64210cSWojciech Zajac 	/* Nothing yet */
3192d64210cSWojciech Zajac 	final_found = NULL;
3202d64210cSWojciech Zajac 
3212d64210cSWojciech Zajac 	/* Check if any children (and their children) wait for EP event */
3222d64210cSWojciech Zajac 	/* Every device in tree is checked every time so errors can be found */
3232d64210cSWojciech Zajac 	for (child_num = 0; child_num < HCD_CHILDREN; child_num++) {
3242d64210cSWojciech Zajac 		/* Device, to be checked for EP event recursively... */
3252d64210cSWojciech Zajac 		child = device->child[child_num];
3262d64210cSWojciech Zajac 
3272d64210cSWojciech Zajac 		/* ...but only if attached */
3282d64210cSWojciech Zajac 		if (NULL != child) {
3292d64210cSWojciech Zajac 			/* Look deeper first */
3302d64210cSWojciech Zajac 			child_found = hcd_get_child_for_ep(child, ep);
3312d64210cSWojciech Zajac 
3322d64210cSWojciech Zajac 			if (NULL != child_found) {
3332d64210cSWojciech Zajac 				/* Only one device can wait for EP event */
3342d64210cSWojciech Zajac 				USB_ASSERT((NULL == final_found),
3352d64210cSWojciech Zajac 					"More than one device waits for EP");
3362d64210cSWojciech Zajac 				/* Remember what was found */
3372d64210cSWojciech Zajac 				final_found = child_found;
3382d64210cSWojciech Zajac 			}
3392d64210cSWojciech Zajac 		}
3402d64210cSWojciech Zajac 	}
3412d64210cSWojciech Zajac 
3422d64210cSWojciech Zajac 	/* Check this device last */
3432d64210cSWojciech Zajac 	if ((HCD_EVENT_ENDPOINT == device->wait_event) &&
3442d64210cSWojciech Zajac 	    (ep == device->wait_ep)) {
3452d64210cSWojciech Zajac 		/* Only one device can wait for EP event */
3462d64210cSWojciech Zajac 		USB_ASSERT((NULL == final_found),
3472d64210cSWojciech Zajac 			"More than one device waits for EP");
3482d64210cSWojciech Zajac 		/* Remember what was found */
3492d64210cSWojciech Zajac 		final_found = device;
3502d64210cSWojciech Zajac 	}
3512d64210cSWojciech Zajac 
3522d64210cSWojciech Zajac 	return final_found;
3532d64210cSWojciech Zajac }
3542d64210cSWojciech Zajac 
3552d64210cSWojciech Zajac 
3562d64210cSWojciech Zajac /*===========================================================================*
3572d64210cSWojciech Zajac  *    hcd_add_child                                                          *
3582d64210cSWojciech Zajac  *===========================================================================*/
3592d64210cSWojciech Zajac static void
hcd_add_child(hcd_device_state * parent,hcd_reg1 port,hcd_speed speed)3602d64210cSWojciech Zajac hcd_add_child(hcd_device_state * parent, hcd_reg1 port, hcd_speed speed)
3612d64210cSWojciech Zajac {
3622d64210cSWojciech Zajac 	DEBUG_DUMP;
3632d64210cSWojciech Zajac 
3642d64210cSWojciech Zajac 	USB_ASSERT(port < HCD_CHILDREN, "Port number too high");
3652d64210cSWojciech Zajac 	USB_ASSERT(NULL == parent->child[port], "Child device already exists");
3662d64210cSWojciech Zajac 
3672d64210cSWojciech Zajac 	/* Basic addition */
3682d64210cSWojciech Zajac 	parent->child[port] = hcd_new_device();
3692d64210cSWojciech Zajac 	parent->child[port]->parent = parent;
3702d64210cSWojciech Zajac 
3712d64210cSWojciech Zajac 	/* Inherit parent's driver */
3722d64210cSWojciech Zajac 	parent->child[port]->driver = parent->driver;
3732d64210cSWojciech Zajac 
3742d64210cSWojciech Zajac 	/* Remember speed, determined by hub driver */
3752d64210cSWojciech Zajac 	parent->child[port]->speed = speed;
3762d64210cSWojciech Zajac 
3772d64210cSWojciech Zajac 	/* Try creating new thread for device */
3782d64210cSWojciech Zajac 	if (hcd_connect_device(parent->child[port], hcd_device_thread))
3792d64210cSWojciech Zajac 		USB_MSG("Device creation failed, nothing more "
3802d64210cSWojciech Zajac 			"will happen until disconnected");
3812d64210cSWojciech Zajac }
3822d64210cSWojciech Zajac 
3832d64210cSWojciech Zajac 
3842d64210cSWojciech Zajac /*===========================================================================*
3852d64210cSWojciech Zajac  *    hcd_delete_child                                                       *
3862d64210cSWojciech Zajac  *===========================================================================*/
3872d64210cSWojciech Zajac static void
hcd_delete_child(hcd_device_state * parent,hcd_reg1 port)3882d64210cSWojciech Zajac hcd_delete_child(hcd_device_state * parent, hcd_reg1 port)
3892d64210cSWojciech Zajac {
3902d64210cSWojciech Zajac 	hcd_device_state * child;
3912d64210cSWojciech Zajac 
3922d64210cSWojciech Zajac 	DEBUG_DUMP;
3932d64210cSWojciech Zajac 
3942d64210cSWojciech Zajac 	USB_ASSERT(port < HCD_CHILDREN, "Port number too high");
3952d64210cSWojciech Zajac 
3962d64210cSWojciech Zajac 	child = parent->child[port]; /* Child to be detached */
3972d64210cSWojciech Zajac 
3982d64210cSWojciech Zajac 	USB_ASSERT(NULL != child, "Child device does not exist");
3992d64210cSWojciech Zajac 
4002d64210cSWojciech Zajac 	/* Make this child device and all its attached children
4012d64210cSWojciech Zajac 	 * disconnect recursively */
4022d64210cSWojciech Zajac 	hcd_disconnect_tree(child);
4032d64210cSWojciech Zajac 
4042d64210cSWojciech Zajac 	/* Delete to release device itself */
4052d64210cSWojciech Zajac 	hcd_delete_device(child);
4062d64210cSWojciech Zajac 
4072d64210cSWojciech Zajac 	/* Mark as released */
4082d64210cSWojciech Zajac 	parent->child[port] = NULL;
4092d64210cSWojciech Zajac }
4102d64210cSWojciech Zajac 
4112d64210cSWojciech Zajac 
4122d64210cSWojciech Zajac /*===========================================================================*
4132d64210cSWojciech Zajac  *    hcd_disconnect_tree                                                    *
4142d64210cSWojciech Zajac  *===========================================================================*/
4152d64210cSWojciech Zajac static void
hcd_disconnect_tree(hcd_device_state * device)4162d64210cSWojciech Zajac hcd_disconnect_tree(hcd_device_state * device)
4172d64210cSWojciech Zajac {
4182d64210cSWojciech Zajac 	hcd_reg1 child_num;
4192d64210cSWojciech Zajac 
4202d64210cSWojciech Zajac 	DEBUG_DUMP;
4212d64210cSWojciech Zajac 
4222d64210cSWojciech Zajac 	/* Generate disconnect event for all children */
4232d64210cSWojciech Zajac 	for (child_num = 0; child_num < HCD_CHILDREN; child_num++) {
4242d64210cSWojciech Zajac 		if (NULL != device->child[child_num])
4252d64210cSWojciech Zajac 			hcd_handle_event(device, HCD_EVENT_PORT_DISCONNECTED,
4262d64210cSWojciech Zajac 					child_num);
4272d64210cSWojciech Zajac 	}
4282d64210cSWojciech Zajac 
4292d64210cSWojciech Zajac 	/* If this device was detached during URB handling, some steps must be
4302d64210cSWojciech Zajac 	 * taken to ensure that no process/thread is waiting for completion */
4312d64210cSWojciech Zajac 	if (NULL != device->urb) {
4322d64210cSWojciech Zajac 		USB_MSG("Unplugged device had unhandled URB");
4332d64210cSWojciech Zajac 		/* Tell device driver that device was detached */
4342d64210cSWojciech Zajac 		/* TODO: ENODEV selected for that */
4352d64210cSWojciech Zajac 		device->urb->inout_status = ENODEV;
4362d64210cSWojciech Zajac 		hcd_complete_urb(device);
4372d64210cSWojciech Zajac 	}
4382d64210cSWojciech Zajac 
4392d64210cSWojciech Zajac 	/* If connect callback was used before, call
4402d64210cSWojciech Zajac 	 * it's equivalent to signal disconnection */
4412d64210cSWojciech Zajac 	if (HCD_STATE_CONNECTED == device->state)
4422d64210cSWojciech Zajac 		hcd_disconnect_cb(device);
4432d64210cSWojciech Zajac 
4442d64210cSWojciech Zajac 	/* Handle device disconnection (freeing memory etc.) */
4452d64210cSWojciech Zajac 	hcd_disconnect_device(device);
4462d64210cSWojciech Zajac }
4472d64210cSWojciech Zajac 
4482d64210cSWojciech Zajac 
4492d64210cSWojciech Zajac /*===========================================================================*
4502d64210cSWojciech Zajac  *    hcd_dump_tree                                                          *
4512d64210cSWojciech Zajac  *===========================================================================*/
4522d64210cSWojciech Zajac static void
hcd_dump_tree(hcd_device_state * device,hcd_reg1 level)4532d64210cSWojciech Zajac hcd_dump_tree(hcd_device_state * device, hcd_reg1 level)
4542d64210cSWojciech Zajac {
4552d64210cSWojciech Zajac 	hcd_reg1 child_num;
4562d64210cSWojciech Zajac 
4572d64210cSWojciech Zajac 	/* DEBUG_DUMP; */ /* Let's keep tree output cleaner */
4582d64210cSWojciech Zajac 
459*3c8950ccSBen Gras 	USB_MSG("Device on level %03u: 0x%p", level, device);
4602d64210cSWojciech Zajac 
4612d64210cSWojciech Zajac 	/* Traverse device tree recursively */
4622d64210cSWojciech Zajac 	for (child_num = 0; child_num < HCD_CHILDREN; child_num++) {
4632d64210cSWojciech Zajac 		if (NULL != device->child[child_num])
4642d64210cSWojciech Zajac 			hcd_dump_tree(device->child[child_num], level + 1);
4652d64210cSWojciech Zajac 	}
4662d64210cSWojciech Zajac }
4672d64210cSWojciech Zajac 
4682d64210cSWojciech Zajac 
4692d64210cSWojciech Zajac /*===========================================================================*
470433d6423SLionel Sambuc  *    hcd_enumerate                                                          *
471433d6423SLionel Sambuc  *===========================================================================*/
472433d6423SLionel Sambuc static int
hcd_enumerate(hcd_device_state * this_device)473433d6423SLionel Sambuc hcd_enumerate(hcd_device_state * this_device)
474433d6423SLionel Sambuc {
475433d6423SLionel Sambuc 	hcd_driver_state * d;
476433d6423SLionel Sambuc 
477433d6423SLionel Sambuc 	DEBUG_DUMP;
478433d6423SLionel Sambuc 
479433d6423SLionel Sambuc 	d = this_device->driver;
480433d6423SLionel Sambuc 
4812d64210cSWojciech Zajac 	/* Having a parent device also means being reseted by it
4822d64210cSWojciech Zajac 	 * so only reset devices that have no parents */
4832d64210cSWojciech Zajac 	if (NULL == this_device->parent) {
484433d6423SLionel Sambuc 		/* First let driver reset device */
485433d6423SLionel Sambuc 		if (EXIT_SUCCESS != d->reset_device(d->private_data,
486433d6423SLionel Sambuc 						&(this_device->speed))) {
487433d6423SLionel Sambuc 			USB_MSG("Failed to reset device");
488433d6423SLionel Sambuc 			return EXIT_FAILURE;
489433d6423SLionel Sambuc 		}
4902d64210cSWojciech Zajac 	}
491433d6423SLionel Sambuc 
492433d6423SLionel Sambuc 	/* Default MaxPacketSize, based on speed */
4932d64210cSWojciech Zajac 	if (HCD_SPEED_HIGH == this_device->speed)
494433d6423SLionel Sambuc 		this_device->max_packet_size = HCD_HS_MAXPACKETSIZE;
4952d64210cSWojciech Zajac 	else
4962d64210cSWojciech Zajac 		this_device->max_packet_size = HCD_LS_MAXPACKETSIZE;
497433d6423SLionel Sambuc 
498433d6423SLionel Sambuc 	/* Get device descriptor */
499433d6423SLionel Sambuc 	if (EXIT_SUCCESS != hcd_get_device_descriptor(this_device)) {
500433d6423SLionel Sambuc 		USB_MSG("Failed to get device descriptor");
501433d6423SLionel Sambuc 		return EXIT_FAILURE;
502433d6423SLionel Sambuc 	}
503433d6423SLionel Sambuc 
5042d64210cSWojciech Zajac 	/* Remember max packet size from device descriptor */
5052d64210cSWojciech Zajac 	this_device->max_packet_size = this_device->device_desc.bMaxPacketSize;
506433d6423SLionel Sambuc 
5072d64210cSWojciech Zajac 	/* Dump device descriptor in debug mode */
5082d64210cSWojciech Zajac #ifdef DEBUG
5092d64210cSWojciech Zajac 	{
5102d64210cSWojciech Zajac 		hcd_device_descriptor * d;
5112d64210cSWojciech Zajac 		d = &(this_device->device_desc);
5122d64210cSWojciech Zajac 
5132d64210cSWojciech Zajac 		USB_DBG("<<DEVICE>>");
5142d64210cSWojciech Zajac 		USB_DBG("bLength %02X",			d->bLength);
5152d64210cSWojciech Zajac 		USB_DBG("bDescriptorType %02X",		d->bDescriptorType);
5162d64210cSWojciech Zajac 		USB_DBG("bcdUSB %04X",			UGETW(d->bcdUSB));
5172d64210cSWojciech Zajac 		USB_DBG("bDeviceClass %02X",		d->bDeviceClass);
5182d64210cSWojciech Zajac 		USB_DBG("bDeviceSubClass %02X",		d->bDeviceSubClass);
5192d64210cSWojciech Zajac 		USB_DBG("bDeviceProtocol %02X",		d->bDeviceProtocol);
5202d64210cSWojciech Zajac 		USB_DBG("bMaxPacketSize %02X",		d->bMaxPacketSize);
5212d64210cSWojciech Zajac 		USB_DBG("idVendor %04X",		UGETW(d->idVendor));
5222d64210cSWojciech Zajac 		USB_DBG("idProduct %04X",		UGETW(d->idProduct));
5232d64210cSWojciech Zajac 		USB_DBG("bcdDevice %04X",		UGETW(d->bcdDevice));
5242d64210cSWojciech Zajac 		USB_DBG("iManufacturer %02X",		d->iManufacturer);
5252d64210cSWojciech Zajac 		USB_DBG("iProduct %02X",		d->iProduct);
5262d64210cSWojciech Zajac 		USB_DBG("iSerialNumber %02X",		d->iSerialNumber);
5272d64210cSWojciech Zajac 		USB_DBG("bNumConfigurations %02X",	d->bNumConfigurations);
5282d64210cSWojciech Zajac 	}
5292d64210cSWojciech Zajac #endif
5302d64210cSWojciech Zajac 
5312d64210cSWojciech Zajac 	/* Set reserved address */
5322d64210cSWojciech Zajac 	if (EXIT_SUCCESS != hcd_set_address(this_device)) {
533433d6423SLionel Sambuc 		USB_MSG("Failed to set device address");
534433d6423SLionel Sambuc 		return EXIT_FAILURE;
535433d6423SLionel Sambuc 	}
536433d6423SLionel Sambuc 
5372d64210cSWojciech Zajac 	/* Sleep 5msec to allow addressing */
5382d64210cSWojciech Zajac 	hcd_os_nanosleep(HCD_NANOSLEEP_MSEC(5));
5392d64210cSWojciech Zajac 
5402d64210cSWojciech Zajac 	/* Remember what was assigned in hardware */
5412d64210cSWojciech Zajac 	this_device->current_address = this_device->reserved_address;
5422d64210cSWojciech Zajac 
543433d6423SLionel Sambuc 	/* Get other descriptors */
544433d6423SLionel Sambuc 	if (EXIT_SUCCESS != hcd_get_descriptor_tree(this_device)) {
545433d6423SLionel Sambuc 		USB_MSG("Failed to get configuration descriptor tree");
546433d6423SLionel Sambuc 		return EXIT_FAILURE;
547433d6423SLionel Sambuc 	}
548433d6423SLionel Sambuc 
549433d6423SLionel Sambuc 	/* TODO: Always use first configuration, as there is no support for
550433d6423SLionel Sambuc 	 * multiple configurations in DDEKit/devman and devices rarely have
551433d6423SLionel Sambuc 	 * more than one anyway */
552433d6423SLionel Sambuc 	/* Set configuration */
553433d6423SLionel Sambuc 	if (EXIT_SUCCESS != hcd_set_configuration(this_device,
554433d6423SLionel Sambuc 				HCD_SET_CONFIG_NUM(HCD_DEFAULT_CONFIG))) {
555433d6423SLionel Sambuc 		USB_MSG("Failed to set configuration");
556433d6423SLionel Sambuc 		return EXIT_FAILURE;
557433d6423SLionel Sambuc 	}
558433d6423SLionel Sambuc 
559433d6423SLionel Sambuc 	USB_DBG("Enumeration completed");
560433d6423SLionel Sambuc 
561433d6423SLionel Sambuc 	return EXIT_SUCCESS;
562433d6423SLionel Sambuc }
563433d6423SLionel Sambuc 
564433d6423SLionel Sambuc 
565433d6423SLionel Sambuc /*===========================================================================*
566433d6423SLionel Sambuc  *    hcd_get_device_descriptor                                              *
567433d6423SLionel Sambuc  *===========================================================================*/
568433d6423SLionel Sambuc static int
hcd_get_device_descriptor(hcd_device_state * this_device)569433d6423SLionel Sambuc hcd_get_device_descriptor(hcd_device_state * this_device)
570433d6423SLionel Sambuc {
571433d6423SLionel Sambuc 	hcd_ctrlrequest setup;
5722d64210cSWojciech Zajac 	hcd_urb urb;
573433d6423SLionel Sambuc 
574433d6423SLionel Sambuc 	DEBUG_DUMP;
575433d6423SLionel Sambuc 
576433d6423SLionel Sambuc 	/* TODO: magic numbers, no header for these */
577433d6423SLionel Sambuc 	/* Format setup packet */
578433d6423SLionel Sambuc 	setup.bRequestType	= 0x80;			/* IN */
579433d6423SLionel Sambuc 	setup.bRequest		= 0x06;			/* Get descriptor */
580433d6423SLionel Sambuc 	setup.wValue		= 0x0100;		/* Device */
581433d6423SLionel Sambuc 	setup.wIndex		= 0x0000;
582433d6423SLionel Sambuc 	setup.wLength		= sizeof(this_device->device_desc);
583433d6423SLionel Sambuc 
5842d64210cSWojciech Zajac 	/* Prepare self-URB */
5852d64210cSWojciech Zajac 	memset(&urb, 0, sizeof(urb));
5862d64210cSWojciech Zajac 	urb.direction = HCD_DIRECTION_IN;
5872d64210cSWojciech Zajac 	urb.endpoint = HCD_DEFAULT_EP;
5882d64210cSWojciech Zajac 	urb.in_setup = &setup;
5892d64210cSWojciech Zajac 	urb.inout_data = (hcd_reg1 *)(&(this_device->device_desc));
5902d64210cSWojciech Zajac 	urb.target_device = this_device;
5912d64210cSWojciech Zajac 	urb.type = HCD_TRANSFER_CONTROL;
5922d64210cSWojciech Zajac 
5932d64210cSWojciech Zajac 	/* Put it to be scheduled and wait for control to get back */
5942d64210cSWojciech Zajac 	hcd_schedule_internal_urb(&urb);
5952d64210cSWojciech Zajac 	hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL);
5962d64210cSWojciech Zajac 	hcd_handle_urb(this_device);
5972d64210cSWojciech Zajac 
5982d64210cSWojciech Zajac 	/* Check if URB submission completed successfully */
5992d64210cSWojciech Zajac 	if (urb.inout_status) {
6002d64210cSWojciech Zajac 		USB_MSG("URB submission failed");
601433d6423SLionel Sambuc 		return EXIT_FAILURE;
602433d6423SLionel Sambuc 	}
603433d6423SLionel Sambuc 
6042d64210cSWojciech Zajac 	/* Check if expected size was received */
6052d64210cSWojciech Zajac 	if (urb.out_size != setup.wLength) {
6062d64210cSWojciech Zajac 		USB_MSG("URB submission returned invalid amount of data");
607433d6423SLionel Sambuc 		return EXIT_FAILURE;
608433d6423SLionel Sambuc 	}
609433d6423SLionel Sambuc 
610433d6423SLionel Sambuc 	return EXIT_SUCCESS;
611433d6423SLionel Sambuc }
612433d6423SLionel Sambuc 
613433d6423SLionel Sambuc 
614433d6423SLionel Sambuc /*===========================================================================*
615433d6423SLionel Sambuc  *    hcd_set_address                                                        *
616433d6423SLionel Sambuc  *===========================================================================*/
617433d6423SLionel Sambuc static int
hcd_set_address(hcd_device_state * this_device)6182d64210cSWojciech Zajac hcd_set_address(hcd_device_state * this_device)
619433d6423SLionel Sambuc {
620433d6423SLionel Sambuc 	hcd_ctrlrequest setup;
6212d64210cSWojciech Zajac 	hcd_urb urb;
622433d6423SLionel Sambuc 
623433d6423SLionel Sambuc 	DEBUG_DUMP;
624433d6423SLionel Sambuc 
625433d6423SLionel Sambuc 	/* Check for legal USB device address (must be non-zero as well) */
6262d64210cSWojciech Zajac 	USB_ASSERT((this_device->reserved_address > HCD_DEFAULT_ADDR) &&
6272d64210cSWojciech Zajac 		(this_device->reserved_address <= HCD_LAST_ADDR),
628433d6423SLionel Sambuc 		"Illegal device address supplied");
629433d6423SLionel Sambuc 
630433d6423SLionel Sambuc 	/* TODO: magic numbers, no header for these */
631433d6423SLionel Sambuc 	setup.bRequestType	= 0x00;			/* OUT */
632433d6423SLionel Sambuc 	setup.bRequest		= 0x05;			/* Set address */
6332d64210cSWojciech Zajac 	setup.wValue		= this_device->reserved_address;
634433d6423SLionel Sambuc 	setup.wIndex		= 0x0000;
635433d6423SLionel Sambuc 	setup.wLength		= 0x0000;
636433d6423SLionel Sambuc 
6372d64210cSWojciech Zajac 	/* Prepare self-URB */
6382d64210cSWojciech Zajac 	memset(&urb, 0, sizeof(urb));
6392d64210cSWojciech Zajac 	urb.direction = HCD_DIRECTION_OUT;
6402d64210cSWojciech Zajac 	urb.endpoint = HCD_DEFAULT_EP;
6412d64210cSWojciech Zajac 	urb.in_setup = &setup;
6422d64210cSWojciech Zajac 	urb.inout_data = NULL;
6432d64210cSWojciech Zajac 	urb.target_device = this_device;
6442d64210cSWojciech Zajac 	urb.type = HCD_TRANSFER_CONTROL;
6452d64210cSWojciech Zajac 
6462d64210cSWojciech Zajac 	/* Put it to be scheduled and wait for control to get back */
6472d64210cSWojciech Zajac 	hcd_schedule_internal_urb(&urb);
6482d64210cSWojciech Zajac 	hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL);
6492d64210cSWojciech Zajac 	hcd_handle_urb(this_device);
6502d64210cSWojciech Zajac 
6512d64210cSWojciech Zajac 	/* Check if URB submission completed successfully */
6522d64210cSWojciech Zajac 	if (urb.inout_status) {
6532d64210cSWojciech Zajac 		USB_MSG("URB submission failed");
654433d6423SLionel Sambuc 		return EXIT_FAILURE;
655433d6423SLionel Sambuc 	}
656433d6423SLionel Sambuc 
6572d64210cSWojciech Zajac 	/* Check if expected size was received */
6582d64210cSWojciech Zajac 	if (urb.out_size != setup.wLength) {
6592d64210cSWojciech Zajac 		USB_MSG("URB submission returned invalid amount of data");
6602d64210cSWojciech Zajac 		return EXIT_FAILURE;
6612d64210cSWojciech Zajac 	}
662433d6423SLionel Sambuc 
663433d6423SLionel Sambuc 	return EXIT_SUCCESS;
664433d6423SLionel Sambuc }
665433d6423SLionel Sambuc 
666433d6423SLionel Sambuc 
667433d6423SLionel Sambuc /*===========================================================================*
668433d6423SLionel Sambuc  *    hcd_get_descriptor_tree                                                *
669433d6423SLionel Sambuc  *===========================================================================*/
670433d6423SLionel Sambuc static int
hcd_get_descriptor_tree(hcd_device_state * this_device)671433d6423SLionel Sambuc hcd_get_descriptor_tree(hcd_device_state * this_device)
672433d6423SLionel Sambuc {
6732d64210cSWojciech Zajac 	hcd_config_descriptor temp_config_descriptor;
674433d6423SLionel Sambuc 	hcd_ctrlrequest setup;
6752d64210cSWojciech Zajac 	hcd_urb urb;
6762d64210cSWojciech Zajac 
6772d64210cSWojciech Zajac 	/* To receive data */
6782d64210cSWojciech Zajac 	hcd_reg4 expected_length;
6792d64210cSWojciech Zajac 	hcd_reg1 * expected_buffer;
6802d64210cSWojciech Zajac 
6812d64210cSWojciech Zajac 	int retval;
682433d6423SLionel Sambuc 
683433d6423SLionel Sambuc 	DEBUG_DUMP;
684433d6423SLionel Sambuc 
6852d64210cSWojciech Zajac 	/* Initially */
6862d64210cSWojciech Zajac 	retval = EXIT_FAILURE;
6872d64210cSWojciech Zajac 	expected_buffer = NULL;
688433d6423SLionel Sambuc 
6892d64210cSWojciech Zajac 	/* First part gets only configuration to find out total length */
6902d64210cSWojciech Zajac 	{
691433d6423SLionel Sambuc 		/* TODO: Default configuration is hard-coded
692433d6423SLionel Sambuc 		 * but others are rarely used anyway */
693433d6423SLionel Sambuc 		/* TODO: magic numbers, no header for these */
694433d6423SLionel Sambuc 		setup.bRequestType	= 0x80;		/* IN */
695433d6423SLionel Sambuc 		setup.bRequest		= 0x06;		/* Get descriptor */
696433d6423SLionel Sambuc 		setup.wValue		= 0x0200 | HCD_DEFAULT_CONFIG;
697433d6423SLionel Sambuc 		setup.wIndex		= 0x0000;
6982d64210cSWojciech Zajac 		setup.wLength		= sizeof(temp_config_descriptor);
699433d6423SLionel Sambuc 
7002d64210cSWojciech Zajac 		/* Prepare self-URB */
7012d64210cSWojciech Zajac 		memset(&urb, 0, sizeof(urb));
7022d64210cSWojciech Zajac 		urb.direction = HCD_DIRECTION_IN;
7032d64210cSWojciech Zajac 		urb.endpoint = HCD_DEFAULT_EP;
7042d64210cSWojciech Zajac 		urb.in_setup = &setup;
7052d64210cSWojciech Zajac 		urb.inout_data = (hcd_reg1 *)(&temp_config_descriptor);
7062d64210cSWojciech Zajac 		urb.target_device = this_device;
7072d64210cSWojciech Zajac 		urb.type = HCD_TRANSFER_CONTROL;
7082d64210cSWojciech Zajac 
7092d64210cSWojciech Zajac 		/* Put it to be scheduled and wait for control to get back */
7102d64210cSWojciech Zajac 		hcd_schedule_internal_urb(&urb);
7112d64210cSWojciech Zajac 		hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL);
7122d64210cSWojciech Zajac 		hcd_handle_urb(this_device);
7132d64210cSWojciech Zajac 
7142d64210cSWojciech Zajac 		/* Check if URB submission completed successfully */
7152d64210cSWojciech Zajac 		if (urb.inout_status) {
7162d64210cSWojciech Zajac 			USB_MSG("URB submission failed");
7172d64210cSWojciech Zajac 			goto FINISH;
718433d6423SLionel Sambuc 		}
719433d6423SLionel Sambuc 
7202d64210cSWojciech Zajac 		/* Check if expected size was received */
7212d64210cSWojciech Zajac 		if (urb.out_size != setup.wLength) {
7222d64210cSWojciech Zajac 			USB_MSG("URB submission returned "
7232d64210cSWojciech Zajac 				"invalid amount of data");
7242d64210cSWojciech Zajac 			goto FINISH;
725433d6423SLionel Sambuc 		}
726433d6423SLionel Sambuc 	}
727433d6423SLionel Sambuc 
7282d64210cSWojciech Zajac 	/* Get total expected length */
7292d64210cSWojciech Zajac 	expected_length = UGETW(temp_config_descriptor.wTotalLength);
730433d6423SLionel Sambuc 
7312d64210cSWojciech Zajac 	/* Check for abnormal value */
7322d64210cSWojciech Zajac 	if (expected_length > HCD_SANE_DESCRIPTOR_LENGTH) {
7332d64210cSWojciech Zajac 		USB_MSG("Total descriptor length declared is too high");
7342d64210cSWojciech Zajac 		goto FINISH;
7352d64210cSWojciech Zajac 	}
7362d64210cSWojciech Zajac 
7372d64210cSWojciech Zajac 	/* Get descriptor buffer to hold everything expected */
7382d64210cSWojciech Zajac 	if (NULL == (expected_buffer = malloc(expected_length))) {
7392d64210cSWojciech Zajac 		USB_MSG("Descriptor allocation failed");
7402d64210cSWojciech Zajac 		goto FINISH;
7412d64210cSWojciech Zajac 	}
7422d64210cSWojciech Zajac 
7432d64210cSWojciech Zajac 	/* Second part gets all available descriptors */
7442d64210cSWojciech Zajac 	{
7452d64210cSWojciech Zajac 		/* TODO: Default configuration is hard-coded
7462d64210cSWojciech Zajac 		 * but others are rarely used anyway */
7472d64210cSWojciech Zajac 		/* TODO: magic numbers, no header for these */
7482d64210cSWojciech Zajac 		setup.bRequestType	= 0x80;		/* IN */
7492d64210cSWojciech Zajac 		setup.bRequest		= 0x06;		/* Get descriptor */
7502d64210cSWojciech Zajac 		setup.wValue		= 0x0200 | HCD_DEFAULT_CONFIG;
7512d64210cSWojciech Zajac 		setup.wIndex		= 0x0000;
7522d64210cSWojciech Zajac 		setup.wLength		= expected_length;
7532d64210cSWojciech Zajac 
7542d64210cSWojciech Zajac 		/* Prepare self-URB */
7552d64210cSWojciech Zajac 		memset(&urb, 0, sizeof(urb));
7562d64210cSWojciech Zajac 		urb.direction = HCD_DIRECTION_IN;
7572d64210cSWojciech Zajac 		urb.endpoint = HCD_DEFAULT_EP;
7582d64210cSWojciech Zajac 		urb.in_setup = &setup;
7592d64210cSWojciech Zajac 		urb.inout_data = expected_buffer;
7602d64210cSWojciech Zajac 		urb.target_device = this_device;
7612d64210cSWojciech Zajac 		urb.type = HCD_TRANSFER_CONTROL;
7622d64210cSWojciech Zajac 
7632d64210cSWojciech Zajac 		/* Put it to be scheduled and wait for control to get back */
7642d64210cSWojciech Zajac 		hcd_schedule_internal_urb(&urb);
7652d64210cSWojciech Zajac 		hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL);
7662d64210cSWojciech Zajac 		hcd_handle_urb(this_device);
7672d64210cSWojciech Zajac 
7682d64210cSWojciech Zajac 		/* Check if URB submission completed successfully */
7692d64210cSWojciech Zajac 		if (urb.inout_status) {
7702d64210cSWojciech Zajac 			USB_MSG("URB submission failed");
7712d64210cSWojciech Zajac 			goto FINISH;
7722d64210cSWojciech Zajac 		}
7732d64210cSWojciech Zajac 
7742d64210cSWojciech Zajac 		/* Check if expected size was received */
7752d64210cSWojciech Zajac 		if (urb.out_size != setup.wLength) {
7762d64210cSWojciech Zajac 			USB_MSG("URB submission returned "
7772d64210cSWojciech Zajac 				"invalid amount of data");
7782d64210cSWojciech Zajac 			goto FINISH;
7792d64210cSWojciech Zajac 		}
7802d64210cSWojciech Zajac 	}
7812d64210cSWojciech Zajac 
7822d64210cSWojciech Zajac 	if (EXIT_SUCCESS != hcd_buffer_to_tree(expected_buffer,
7832d64210cSWojciech Zajac 						(int)expected_length,
784433d6423SLionel Sambuc 						&(this_device->config_tree))) {
7852d64210cSWojciech Zajac 		USB_MSG("Broken descriptor data");
7862d64210cSWojciech Zajac 		goto FINISH;
787433d6423SLionel Sambuc 	}
788433d6423SLionel Sambuc 
7892d64210cSWojciech Zajac 	/* No errors occurred */
7902d64210cSWojciech Zajac 	retval = EXIT_SUCCESS;
7912d64210cSWojciech Zajac 
7922d64210cSWojciech Zajac 	FINISH:
7932d64210cSWojciech Zajac 
7942d64210cSWojciech Zajac 	/* Release allocated buffer */
7952d64210cSWojciech Zajac 	if (expected_buffer)
7962d64210cSWojciech Zajac 		free(expected_buffer);
7972d64210cSWojciech Zajac 
7982d64210cSWojciech Zajac 	return retval;
799433d6423SLionel Sambuc }
800433d6423SLionel Sambuc 
801433d6423SLionel Sambuc 
802433d6423SLionel Sambuc /*===========================================================================*
803433d6423SLionel Sambuc  *    hcd_set_configuration                                                  *
804433d6423SLionel Sambuc  *===========================================================================*/
805433d6423SLionel Sambuc static int
hcd_set_configuration(hcd_device_state * this_device,hcd_reg1 configuration)806433d6423SLionel Sambuc hcd_set_configuration(hcd_device_state * this_device, hcd_reg1 configuration)
807433d6423SLionel Sambuc {
808433d6423SLionel Sambuc 	hcd_ctrlrequest setup;
8092d64210cSWojciech Zajac 	hcd_urb urb;
810433d6423SLionel Sambuc 
811433d6423SLionel Sambuc 	DEBUG_DUMP;
812433d6423SLionel Sambuc 
813433d6423SLionel Sambuc 	/* TODO: magic numbers, no header for these */
814433d6423SLionel Sambuc 	setup.bRequestType	= 0x00;		/* OUT */
815433d6423SLionel Sambuc 	setup.bRequest		= 0x09;		/* Set configuration */
816433d6423SLionel Sambuc 	setup.wValue		= configuration;
817433d6423SLionel Sambuc 	setup.wIndex		= 0x0000;
818433d6423SLionel Sambuc 	setup.wLength		= 0x0000;
819433d6423SLionel Sambuc 
8202d64210cSWojciech Zajac 	/* Prepare self-URB */
8212d64210cSWojciech Zajac 	memset(&urb, 0, sizeof(urb));
8222d64210cSWojciech Zajac 	urb.direction = HCD_DIRECTION_OUT;
8232d64210cSWojciech Zajac 	urb.endpoint = HCD_DEFAULT_EP;
8242d64210cSWojciech Zajac 	urb.in_setup = &setup;
8252d64210cSWojciech Zajac 	urb.inout_data = NULL;
8262d64210cSWojciech Zajac 	urb.target_device = this_device;
8272d64210cSWojciech Zajac 	urb.type = HCD_TRANSFER_CONTROL;
828433d6423SLionel Sambuc 
8292d64210cSWojciech Zajac 	/* Put it to be scheduled and wait for control to get back */
8302d64210cSWojciech Zajac 	hcd_schedule_internal_urb(&urb);
8312d64210cSWojciech Zajac 	hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL);
8322d64210cSWojciech Zajac 	hcd_handle_urb(this_device);
8332d64210cSWojciech Zajac 
8342d64210cSWojciech Zajac 	return urb.inout_status;
835433d6423SLionel Sambuc }
836433d6423SLionel Sambuc 
837433d6423SLionel Sambuc 
838433d6423SLionel Sambuc /*===========================================================================*
839433d6423SLionel Sambuc  *    hcd_handle_urb                                                         *
840433d6423SLionel Sambuc  *===========================================================================*/
8412d64210cSWojciech Zajac static void
hcd_handle_urb(hcd_device_state * this_device)8422d64210cSWojciech Zajac hcd_handle_urb(hcd_device_state * this_device)
843433d6423SLionel Sambuc {
8442d64210cSWojciech Zajac 	hcd_urb * urb;
845433d6423SLionel Sambuc 	int transfer_status;
846433d6423SLionel Sambuc 
847433d6423SLionel Sambuc 	DEBUG_DUMP;
848433d6423SLionel Sambuc 
8492d64210cSWojciech Zajac 	/* Retrieve URB */
8502d64210cSWojciech Zajac 	urb = this_device->urb;
8512d64210cSWojciech Zajac 
8522d64210cSWojciech Zajac 	USB_ASSERT(NULL != urb, "No URB supplied");
853433d6423SLionel Sambuc 	USB_ASSERT(this_device == urb->target_device, "Unknown device for URB");
854433d6423SLionel Sambuc 
855433d6423SLionel Sambuc 	/* Only if URB parsing was completed... */
856433d6423SLionel Sambuc 	if (EXIT_SUCCESS == urb->inout_status) {
857433d6423SLionel Sambuc 
858433d6423SLionel Sambuc 		transfer_status = EXIT_FAILURE;
859433d6423SLionel Sambuc 
860433d6423SLionel Sambuc 		/* ...check for URB to handle */
861433d6423SLionel Sambuc 		switch (urb->type) {
862433d6423SLionel Sambuc 			case HCD_TRANSFER_CONTROL:
863433d6423SLionel Sambuc 				transfer_status = hcd_control_urb(
864433d6423SLionel Sambuc 							this_device, urb);
865433d6423SLionel Sambuc 				break;
866433d6423SLionel Sambuc 
867433d6423SLionel Sambuc 			case HCD_TRANSFER_BULK:
868433d6423SLionel Sambuc 			case HCD_TRANSFER_INTERRUPT:
869433d6423SLionel Sambuc 				transfer_status = hcd_non_control_urb(
870433d6423SLionel Sambuc 							this_device, urb);
871433d6423SLionel Sambuc 				break;
872433d6423SLionel Sambuc 
873433d6423SLionel Sambuc 			default:
8742d64210cSWojciech Zajac 				USB_MSG("Unsupported transfer type 0x%02X",
875433d6423SLionel Sambuc 							(int)urb->type);
876433d6423SLionel Sambuc 				break;
877433d6423SLionel Sambuc 		}
878433d6423SLionel Sambuc 
879433d6423SLionel Sambuc 		/* In case of error, only dump message */
880433d6423SLionel Sambuc 		if (EXIT_SUCCESS != transfer_status)
881433d6423SLionel Sambuc 			USB_MSG("USB transfer failed");
882433d6423SLionel Sambuc 
883433d6423SLionel Sambuc 	} else
884433d6423SLionel Sambuc 		USB_MSG("Invalid URB supplied");
885433d6423SLionel Sambuc 
8862d64210cSWojciech Zajac 	/* Perform completion routine */
8872d64210cSWojciech Zajac 	hcd_complete_urb(this_device);
8882d64210cSWojciech Zajac }
889433d6423SLionel Sambuc 
8902d64210cSWojciech Zajac 
8912d64210cSWojciech Zajac /*===========================================================================*
8922d64210cSWojciech Zajac  *    hcd_complete_urb                                                       *
8932d64210cSWojciech Zajac  *===========================================================================*/
8942d64210cSWojciech Zajac static void
hcd_complete_urb(hcd_device_state * this_device)8952d64210cSWojciech Zajac hcd_complete_urb(hcd_device_state * this_device)
8962d64210cSWojciech Zajac {
8972d64210cSWojciech Zajac 	DEBUG_DUMP;
8982d64210cSWojciech Zajac 
8992d64210cSWojciech Zajac 	/* Signal scheduler that URB was handled */
9002d64210cSWojciech Zajac 	this_device->urb->handled(this_device->urb);
9012d64210cSWojciech Zajac 
9022d64210cSWojciech Zajac 	/* Use this callback in case it is an external URB */
9032d64210cSWojciech Zajac 	hcd_completion_cb(this_device->urb);
9042d64210cSWojciech Zajac 
9052d64210cSWojciech Zajac 	/* Make device forget about this URB */
9062d64210cSWojciech Zajac 	this_device->urb = NULL;
907433d6423SLionel Sambuc }
908433d6423SLionel Sambuc 
909433d6423SLionel Sambuc 
910433d6423SLionel Sambuc /*===========================================================================*
911433d6423SLionel Sambuc  *    hcd_control_urb                                                        *
912433d6423SLionel Sambuc  *===========================================================================*/
913433d6423SLionel Sambuc static int
hcd_control_urb(hcd_device_state * this_device,hcd_urb * urb)914433d6423SLionel Sambuc hcd_control_urb(hcd_device_state * this_device, hcd_urb * urb)
915433d6423SLionel Sambuc {
916433d6423SLionel Sambuc 	DEBUG_DUMP;
917433d6423SLionel Sambuc 
918433d6423SLionel Sambuc 	/* Assume bad values unless something different occurs later */
919433d6423SLionel Sambuc 	urb->inout_status = EINVAL;
920433d6423SLionel Sambuc 
921433d6423SLionel Sambuc 	/* Must have setup packet for control transfer */
922433d6423SLionel Sambuc 	if (NULL == urb->in_setup) {
923433d6423SLionel Sambuc 		USB_MSG("No setup packet in URB, for control transfer");
924433d6423SLionel Sambuc 		return EXIT_FAILURE;
925433d6423SLionel Sambuc 	}
926433d6423SLionel Sambuc 
927433d6423SLionel Sambuc 	/* TODO: Only EP0 can have control transfer */
928433d6423SLionel Sambuc 	if (HCD_DEFAULT_EP != urb->endpoint) {
929433d6423SLionel Sambuc 		USB_MSG("Control transfer for non zero EP");
930433d6423SLionel Sambuc 		return EXIT_FAILURE;
931433d6423SLionel Sambuc 	}
932433d6423SLionel Sambuc 
933433d6423SLionel Sambuc 	/* Setup and URB directions should match */
934433d6423SLionel Sambuc 	if (((urb->in_setup->bRequestType >> 7) & 0x01) != urb->direction) {
935433d6423SLionel Sambuc 		USB_MSG("URB Direction mismatch");
936433d6423SLionel Sambuc 		return EXIT_FAILURE;
937433d6423SLionel Sambuc 	}
938433d6423SLionel Sambuc 
939433d6423SLionel Sambuc 	/* Send setup packet */
940433d6423SLionel Sambuc 	if (EXIT_SUCCESS != hcd_setup_packet(this_device, urb->in_setup,
941433d6423SLionel Sambuc 						urb->endpoint)) {
942433d6423SLionel Sambuc 		USB_MSG("Sending URB setup packet, failed");
943433d6423SLionel Sambuc 		urb->inout_status = EPIPE;
944433d6423SLionel Sambuc 		return EXIT_FAILURE;
945433d6423SLionel Sambuc 	}
946433d6423SLionel Sambuc 
947433d6423SLionel Sambuc 	/* Put what was read back into URB */
9482d64210cSWojciech Zajac 	if (EXIT_SUCCESS != hcd_finish_setup(this_device, urb->inout_data))
949433d6423SLionel Sambuc 		return EXIT_FAILURE;
950433d6423SLionel Sambuc 
951433d6423SLionel Sambuc 	/* Write transfer output info to URB */
952433d6423SLionel Sambuc 	urb->out_size = (hcd_reg4)this_device->control_len;
953433d6423SLionel Sambuc 	urb->inout_status = EXIT_SUCCESS;
954433d6423SLionel Sambuc 
955433d6423SLionel Sambuc 	return EXIT_SUCCESS;
956433d6423SLionel Sambuc }
957433d6423SLionel Sambuc 
958433d6423SLionel Sambuc 
959433d6423SLionel Sambuc /*===========================================================================*
960433d6423SLionel Sambuc  *    hcd_non_control_urb                                                    *
961433d6423SLionel Sambuc  *===========================================================================*/
962433d6423SLionel Sambuc static int
hcd_non_control_urb(hcd_device_state * this_device,hcd_urb * urb)963433d6423SLionel Sambuc hcd_non_control_urb(hcd_device_state * this_device, hcd_urb * urb)
964433d6423SLionel Sambuc {
965433d6423SLionel Sambuc 	hcd_endpoint * e;
966433d6423SLionel Sambuc 	hcd_datarequest request;
967433d6423SLionel Sambuc 
968433d6423SLionel Sambuc 	DEBUG_DUMP;
969433d6423SLionel Sambuc 
970433d6423SLionel Sambuc 	/* Assume bad values unless something different occurs later */
971433d6423SLionel Sambuc 	urb->inout_status = EINVAL;
972433d6423SLionel Sambuc 
973433d6423SLionel Sambuc 	/* Must have data buffer to send/receive */
974433d6423SLionel Sambuc 	if (NULL == urb->inout_data) {
975433d6423SLionel Sambuc 		USB_MSG("No data packet in URB");
976433d6423SLionel Sambuc 		return EXIT_FAILURE;
977433d6423SLionel Sambuc 	}
978433d6423SLionel Sambuc 
979433d6423SLionel Sambuc 	if (HCD_DEFAULT_EP == urb->endpoint) {
980433d6423SLionel Sambuc 		USB_MSG("Non-control transfer for EP0");
981433d6423SLionel Sambuc 		return EXIT_FAILURE;
982433d6423SLionel Sambuc 	}
983433d6423SLionel Sambuc 
984433d6423SLionel Sambuc 	/* Check if EP number is valid within remembered descriptor tree */
985433d6423SLionel Sambuc 	e = hcd_tree_find_ep(&(this_device->config_tree), urb->endpoint);
986433d6423SLionel Sambuc 
987433d6423SLionel Sambuc 	if (NULL == e) {
988433d6423SLionel Sambuc 		USB_MSG("Invalid EP number for this device");
989433d6423SLionel Sambuc 		return EXIT_FAILURE;
990433d6423SLionel Sambuc 	}
991433d6423SLionel Sambuc 
992433d6423SLionel Sambuc 	/* Check if remembered descriptor direction, matches the one in URB */
993433d6423SLionel Sambuc 	if (((e->descriptor.bEndpointAddress >> 7) & 0x01) != urb->direction) {
994433d6423SLionel Sambuc 		USB_MSG("EP direction mismatch");
995433d6423SLionel Sambuc 		return EXIT_FAILURE;
996433d6423SLionel Sambuc 	}
997433d6423SLionel Sambuc 
998433d6423SLionel Sambuc 	/* Check if remembered type matches */
999433d6423SLionel Sambuc 	if (UE_GET_XFERTYPE(e->descriptor.bmAttributes) != urb->type) {
1000433d6423SLionel Sambuc 		USB_MSG("EP type mismatch");
1001433d6423SLionel Sambuc 		return EXIT_FAILURE;
1002433d6423SLionel Sambuc 	}
1003433d6423SLionel Sambuc 
1004433d6423SLionel Sambuc 	/* Check if remembered interval matches */
1005433d6423SLionel Sambuc 	if ((hcd_reg1)e->descriptor.bInterval != urb->interval) {
1006433d6423SLionel Sambuc 		USB_MSG("EP interval mismatch");
1007433d6423SLionel Sambuc 		return EXIT_FAILURE;
1008433d6423SLionel Sambuc 	}
1009433d6423SLionel Sambuc 
1010433d6423SLionel Sambuc 	/* Assign URB values to data request structure */
1011433d6423SLionel Sambuc 	request.type = urb->type;
1012433d6423SLionel Sambuc 	request.endpoint = urb->endpoint;
1013433d6423SLionel Sambuc 	request.direction = urb->direction;
1014433d6423SLionel Sambuc 	request.data_left = (int)urb->in_size;
1015433d6423SLionel Sambuc 	request.data = urb->inout_data;
10162d64210cSWojciech Zajac 	/* TODO: This was changed to allow software scheduler to work correctly
10172d64210cSWojciech Zajac 	 * by switching URBs when they NAK, rather than waiting forever if URB
10182d64210cSWojciech Zajac 	 * which requires such waiting, was issued */
10192d64210cSWojciech Zajac #if 0
1020433d6423SLionel Sambuc 	request.interval = urb->interval;
10212d64210cSWojciech Zajac #else
10222d64210cSWojciech Zajac 	request.interval = HCD_DEFAULT_NAKLIMIT;
10232d64210cSWojciech Zajac #endif
1024433d6423SLionel Sambuc 
1025433d6423SLionel Sambuc 	/* Assign to let know how much data can be transfered at a time */
1026433d6423SLionel Sambuc 	request.max_packet_size = UGETW(e->descriptor.wMaxPacketSize);
1027433d6423SLionel Sambuc 
1028433d6423SLionel Sambuc 	/* Let know how to configure EP for speed */
1029433d6423SLionel Sambuc 	request.speed = this_device->speed;
1030433d6423SLionel Sambuc 
1031433d6423SLionel Sambuc 	/* Start sending data */
1032433d6423SLionel Sambuc 	if (EXIT_SUCCESS != hcd_data_transfer(this_device, &request)) {
1033433d6423SLionel Sambuc 		USB_MSG("URB non-control transfer, failed");
1034433d6423SLionel Sambuc 		urb->inout_status = EPIPE;
1035433d6423SLionel Sambuc 		return EXIT_FAILURE;
1036433d6423SLionel Sambuc 	}
1037433d6423SLionel Sambuc 
1038433d6423SLionel Sambuc 	/* Transfer successfully completed update URB */
1039433d6423SLionel Sambuc 	USB_ASSERT(request.data_left >= 0,
1040433d6423SLionel Sambuc 		"Negative amount of transfer data remains");
1041433d6423SLionel Sambuc 	urb->out_size = urb->in_size - (hcd_reg4)request.data_left;
1042433d6423SLionel Sambuc 	urb->inout_status = EXIT_SUCCESS;
1043433d6423SLionel Sambuc 
1044433d6423SLionel Sambuc 	return EXIT_SUCCESS;
1045433d6423SLionel Sambuc }
1046433d6423SLionel Sambuc 
1047433d6423SLionel Sambuc 
1048433d6423SLionel Sambuc /*===========================================================================*
1049433d6423SLionel Sambuc  *    hcd_setup_packet                                                       *
1050433d6423SLionel Sambuc  *===========================================================================*/
1051433d6423SLionel Sambuc static int
hcd_setup_packet(hcd_device_state * this_device,hcd_ctrlrequest * setup,hcd_reg1 ep)1052433d6423SLionel Sambuc hcd_setup_packet(hcd_device_state * this_device, hcd_ctrlrequest * setup,
1053433d6423SLionel Sambuc 		hcd_reg1 ep)
1054433d6423SLionel Sambuc {
1055433d6423SLionel Sambuc 	hcd_driver_state * d;
1056433d6423SLionel Sambuc 	hcd_reg1 * current_byte;
1057433d6423SLionel Sambuc 	int rx_len;
1058433d6423SLionel Sambuc 
1059433d6423SLionel Sambuc 	DEBUG_DUMP;
1060433d6423SLionel Sambuc 
1061433d6423SLionel Sambuc 	/* Should have been set at enumeration or with default values */
1062433d6423SLionel Sambuc 	USB_ASSERT(this_device->max_packet_size >= HCD_LS_MAXPACKETSIZE,
1063433d6423SLionel Sambuc 		"Illegal MaxPacketSize");
1064433d6423SLionel Sambuc 	USB_ASSERT(ep <= HCD_LAST_EP, "Invalid EP number");
10652d64210cSWojciech Zajac 	USB_ASSERT(this_device->current_address <= HCD_LAST_ADDR,
1066433d6423SLionel Sambuc 		"Invalid device address");
1067433d6423SLionel Sambuc 
1068433d6423SLionel Sambuc 	/* Initially... */
1069433d6423SLionel Sambuc 	d = this_device->driver;
1070433d6423SLionel Sambuc 	current_byte = this_device->control_data;/* Start reading into this */
1071433d6423SLionel Sambuc 	this_device->control_len = 0;		/* Nothing read yet */
1072433d6423SLionel Sambuc 
1073433d6423SLionel Sambuc 	/* Set parameters for further communication */
10742d64210cSWojciech Zajac 	d->setup_device(d->private_data, ep, this_device->current_address,
10752d64210cSWojciech Zajac 			NULL, NULL);
1076433d6423SLionel Sambuc 
1077433d6423SLionel Sambuc 	/* Send setup packet */
1078433d6423SLionel Sambuc 	d->setup_stage(d->private_data, setup);
1079433d6423SLionel Sambuc 
1080433d6423SLionel Sambuc 	/* Wait for response */
1081433d6423SLionel Sambuc 	hcd_device_wait(this_device, HCD_EVENT_ENDPOINT, ep);
1082433d6423SLionel Sambuc 
1083433d6423SLionel Sambuc 	/* Check response */
1084433d6423SLionel Sambuc 	if (EXIT_SUCCESS != d->check_error(d->private_data,
1085433d6423SLionel Sambuc 					HCD_TRANSFER_CONTROL,
1086433d6423SLionel Sambuc 					ep,
1087433d6423SLionel Sambuc 					HCD_DIRECTION_UNUSED))
1088433d6423SLionel Sambuc 		return EXIT_FAILURE;
1089433d6423SLionel Sambuc 
1090433d6423SLionel Sambuc 	/* For data packets... */
1091433d6423SLionel Sambuc 	if (setup->wLength > 0) {
1092433d6423SLionel Sambuc 
1093433d6423SLionel Sambuc 		/* TODO: magic number */
1094433d6423SLionel Sambuc 		/* ...IN data packets */
1095433d6423SLionel Sambuc 		if (setup->bRequestType & 0x80) {
1096433d6423SLionel Sambuc 
1097433d6423SLionel Sambuc 			for(;;) {
1098433d6423SLionel Sambuc 
1099433d6423SLionel Sambuc 				/* Try getting data */
1100433d6423SLionel Sambuc 				d->in_data_stage(d->private_data);
1101433d6423SLionel Sambuc 
1102433d6423SLionel Sambuc 				/* Wait for response */
1103433d6423SLionel Sambuc 				hcd_device_wait(this_device,
1104433d6423SLionel Sambuc 						HCD_EVENT_ENDPOINT, ep);
1105433d6423SLionel Sambuc 
1106433d6423SLionel Sambuc 				/* Check response */
1107433d6423SLionel Sambuc 				if (EXIT_SUCCESS != d->check_error(
1108433d6423SLionel Sambuc 							d->private_data,
1109433d6423SLionel Sambuc 							HCD_TRANSFER_CONTROL,
1110433d6423SLionel Sambuc 							ep,
1111433d6423SLionel Sambuc 							HCD_DIRECTION_UNUSED))
1112433d6423SLionel Sambuc 					return EXIT_FAILURE;
1113433d6423SLionel Sambuc 
1114433d6423SLionel Sambuc 				/* Read data received as response */
1115433d6423SLionel Sambuc 				rx_len = d->read_data(d->private_data,
1116433d6423SLionel Sambuc 						current_byte, ep);
1117433d6423SLionel Sambuc 
1118433d6423SLionel Sambuc 				/* Increment */
1119433d6423SLionel Sambuc 				current_byte += rx_len;
1120433d6423SLionel Sambuc 				this_device->control_len += rx_len;
1121433d6423SLionel Sambuc 
11222d64210cSWojciech Zajac 				/* If max sized packet was read (or more)... */
11232d64210cSWojciech Zajac 				if (rx_len >= (int)this_device->max_packet_size)
1124433d6423SLionel Sambuc 					/* ...try reading next packet even if
1125433d6423SLionel Sambuc 					 * zero bytes may be received */
1126433d6423SLionel Sambuc 					continue;
1127433d6423SLionel Sambuc 
1128433d6423SLionel Sambuc 				/* If less than max data was read... */
1129433d6423SLionel Sambuc 				if (rx_len < (int)this_device->max_packet_size)
1130433d6423SLionel Sambuc 					/* ...it must have been
1131433d6423SLionel Sambuc 					 * the last packet */
1132433d6423SLionel Sambuc 					break;
1133433d6423SLionel Sambuc 
1134433d6423SLionel Sambuc 				/* Unreachable during normal operation */
1135433d6423SLionel Sambuc 				USB_MSG("rx_len: %d; max_packet_size: %d",
1136433d6423SLionel Sambuc 					rx_len, this_device->max_packet_size);
1137433d6423SLionel Sambuc 				USB_ASSERT(0, "Illegal state of data "
1138433d6423SLionel Sambuc 					"receive operation");
1139433d6423SLionel Sambuc 			}
1140433d6423SLionel Sambuc 
1141433d6423SLionel Sambuc 		} else {
1142433d6423SLionel Sambuc 			/* TODO: Unimplemented OUT DATA stage */
1143433d6423SLionel Sambuc 			d->out_data_stage(d->private_data);
1144433d6423SLionel Sambuc 
1145433d6423SLionel Sambuc 			return EXIT_FAILURE;
1146433d6423SLionel Sambuc 		}
1147433d6423SLionel Sambuc 	}
1148433d6423SLionel Sambuc 
1149433d6423SLionel Sambuc 	/* Status stages */
1150433d6423SLionel Sambuc 	if (setup->bRequestType & 0x80) {
1151433d6423SLionel Sambuc 
1152433d6423SLionel Sambuc 		/* Try confirming data receive */
1153433d6423SLionel Sambuc 		d->out_status_stage(d->private_data);
1154433d6423SLionel Sambuc 
1155433d6423SLionel Sambuc 		/* Wait for response */
1156433d6423SLionel Sambuc 		hcd_device_wait(this_device, HCD_EVENT_ENDPOINT, ep);
1157433d6423SLionel Sambuc 
1158433d6423SLionel Sambuc 		/* Check response */
1159433d6423SLionel Sambuc 		if (EXIT_SUCCESS != d->check_error(d->private_data,
1160433d6423SLionel Sambuc 						HCD_TRANSFER_CONTROL,
1161433d6423SLionel Sambuc 						ep,
1162433d6423SLionel Sambuc 						HCD_DIRECTION_UNUSED))
1163433d6423SLionel Sambuc 			return EXIT_FAILURE;
1164433d6423SLionel Sambuc 
1165433d6423SLionel Sambuc 	} else {
1166433d6423SLionel Sambuc 
1167433d6423SLionel Sambuc 		/* Try getting status confirmation */
1168433d6423SLionel Sambuc 		d->in_status_stage(d->private_data);
1169433d6423SLionel Sambuc 
1170433d6423SLionel Sambuc 		/* Wait for response */
1171433d6423SLionel Sambuc 		hcd_device_wait(this_device, HCD_EVENT_ENDPOINT, ep);
1172433d6423SLionel Sambuc 
1173433d6423SLionel Sambuc 		/* Check response */
1174433d6423SLionel Sambuc 		if (EXIT_SUCCESS != d->check_error(d->private_data,
1175433d6423SLionel Sambuc 						HCD_TRANSFER_CONTROL,
1176433d6423SLionel Sambuc 						ep,
1177433d6423SLionel Sambuc 						HCD_DIRECTION_UNUSED))
1178433d6423SLionel Sambuc 			return EXIT_FAILURE;
1179433d6423SLionel Sambuc 
1180433d6423SLionel Sambuc 		/* Read zero data from response to clear registers */
1181433d6423SLionel Sambuc 		if (0 != d->read_data(d->private_data, NULL, ep))
1182433d6423SLionel Sambuc 			return EXIT_FAILURE;
1183433d6423SLionel Sambuc 	}
1184433d6423SLionel Sambuc 
1185433d6423SLionel Sambuc 	return EXIT_SUCCESS;
1186433d6423SLionel Sambuc }
1187433d6423SLionel Sambuc 
1188433d6423SLionel Sambuc 
1189433d6423SLionel Sambuc /*===========================================================================*
1190433d6423SLionel Sambuc  *    hcd_finish_setup                                                       *
1191433d6423SLionel Sambuc  *===========================================================================*/
1192433d6423SLionel Sambuc static int
hcd_finish_setup(hcd_device_state * this_device,void * output)11932d64210cSWojciech Zajac hcd_finish_setup(hcd_device_state * this_device, void * output)
1194433d6423SLionel Sambuc {
1195433d6423SLionel Sambuc 	DEBUG_DUMP;
1196433d6423SLionel Sambuc 
1197433d6423SLionel Sambuc 	/* Validate setup transfer output length */
1198433d6423SLionel Sambuc 	if (this_device->control_len < 0) {
1199433d6423SLionel Sambuc 		USB_MSG("Negative control transfer output length");
1200433d6423SLionel Sambuc 		return EXIT_FAILURE;
1201433d6423SLionel Sambuc 	}
1202433d6423SLionel Sambuc 
1203433d6423SLionel Sambuc 	/* Length is valid but output not supplied */
1204433d6423SLionel Sambuc 	if (NULL == output)
1205433d6423SLionel Sambuc 		return EXIT_SUCCESS;
1206433d6423SLionel Sambuc 
1207433d6423SLionel Sambuc 	/* Finally, copy when needed */
1208433d6423SLionel Sambuc 	memcpy(output, this_device->control_data, this_device->control_len);
1209433d6423SLionel Sambuc 
1210433d6423SLionel Sambuc 	return EXIT_SUCCESS;
1211433d6423SLionel Sambuc }
1212433d6423SLionel Sambuc 
1213433d6423SLionel Sambuc 
1214433d6423SLionel Sambuc /*===========================================================================*
1215433d6423SLionel Sambuc  *    hcd_data_transfer                                                      *
1216433d6423SLionel Sambuc  *===========================================================================*/
1217433d6423SLionel Sambuc static int
hcd_data_transfer(hcd_device_state * this_device,hcd_datarequest * request)1218433d6423SLionel Sambuc hcd_data_transfer(hcd_device_state * this_device, hcd_datarequest * request)
1219433d6423SLionel Sambuc {
1220433d6423SLionel Sambuc 	hcd_driver_state * d;
1221433d6423SLionel Sambuc 	hcd_datarequest temp_req;
1222433d6423SLionel Sambuc 	int transfer_len;
1223433d6423SLionel Sambuc 
1224433d6423SLionel Sambuc 	DEBUG_DUMP;
1225433d6423SLionel Sambuc 
1226433d6423SLionel Sambuc 	USB_ASSERT((request->endpoint <= HCD_LAST_EP) &&
1227433d6423SLionel Sambuc 		(request->endpoint > HCD_DEFAULT_EP),
1228433d6423SLionel Sambuc 		"Invalid EP number");
12292d64210cSWojciech Zajac 	USB_ASSERT((this_device->current_address <= HCD_LAST_ADDR) &&
12302d64210cSWojciech Zajac 		(this_device->current_address > HCD_DEFAULT_ADDR),
1231433d6423SLionel Sambuc 		"Invalid device address");
1232433d6423SLionel Sambuc 
1233433d6423SLionel Sambuc 	/* Initially... */
1234433d6423SLionel Sambuc 	d = this_device->driver;
1235433d6423SLionel Sambuc 
1236433d6423SLionel Sambuc 	/* Set parameters for further communication */
1237433d6423SLionel Sambuc 	d->setup_device(d->private_data, request->endpoint,
12382d64210cSWojciech Zajac 			this_device->current_address,
12392d64210cSWojciech Zajac 			&(this_device->ep_tx_tog[request->endpoint]),
12402d64210cSWojciech Zajac 			&(this_device->ep_rx_tog[request->endpoint]));
1241433d6423SLionel Sambuc 
1242433d6423SLionel Sambuc 	/* Check transfer direction first */
1243433d6423SLionel Sambuc 	if (HCD_DIRECTION_IN == request->direction) {
1244433d6423SLionel Sambuc 
1245433d6423SLionel Sambuc 		do {
1246433d6423SLionel Sambuc 			/* Start actual data transfer */
1247433d6423SLionel Sambuc 			d->rx_stage(d->private_data, request);
1248433d6423SLionel Sambuc 
1249433d6423SLionel Sambuc 			/* Wait for response */
1250433d6423SLionel Sambuc 			hcd_device_wait(this_device, HCD_EVENT_ENDPOINT,
1251433d6423SLionel Sambuc 					request->endpoint);
1252433d6423SLionel Sambuc 
1253433d6423SLionel Sambuc 			/* Check response */
1254433d6423SLionel Sambuc 			if (EXIT_SUCCESS != d->check_error(d->private_data,
1255433d6423SLionel Sambuc 							request->type,
1256433d6423SLionel Sambuc 							request->endpoint,
1257433d6423SLionel Sambuc 							HCD_DIRECTION_IN))
1258433d6423SLionel Sambuc 				return EXIT_FAILURE;
1259433d6423SLionel Sambuc 
1260433d6423SLionel Sambuc 			/* Read data received as response */
1261433d6423SLionel Sambuc 			transfer_len = d->read_data(d->private_data,
1262433d6423SLionel Sambuc 						request->data,
1263433d6423SLionel Sambuc 						request->endpoint);
1264433d6423SLionel Sambuc 
1265433d6423SLionel Sambuc 			request->data_left -= transfer_len;
1266433d6423SLionel Sambuc 			request->data += transfer_len;
1267433d6423SLionel Sambuc 
1268433d6423SLionel Sambuc 			/* Total length shall not become negative */
1269433d6423SLionel Sambuc 			if (request->data_left < 0) {
1270433d6423SLionel Sambuc 				USB_MSG("Invalid amount of data received");
1271433d6423SLionel Sambuc 				return EXIT_FAILURE;
1272433d6423SLionel Sambuc 			}
1273433d6423SLionel Sambuc 
1274433d6423SLionel Sambuc 		} while (0 != request->data_left);
1275433d6423SLionel Sambuc 
1276433d6423SLionel Sambuc 	} else if (HCD_DIRECTION_OUT == request->direction) {
1277433d6423SLionel Sambuc 
1278433d6423SLionel Sambuc 		do {
1279433d6423SLionel Sambuc 			temp_req = *request;
1280433d6423SLionel Sambuc 
1281433d6423SLionel Sambuc 			/* Decide temporary transfer size */
1282433d6423SLionel Sambuc 			if (temp_req.data_left > (int)temp_req.max_packet_size)
1283433d6423SLionel Sambuc 				temp_req.data_left =
1284433d6423SLionel Sambuc 					(int)temp_req.max_packet_size;
1285433d6423SLionel Sambuc 
1286433d6423SLionel Sambuc 			/* Alter actual transfer size */
1287433d6423SLionel Sambuc 			request->data += temp_req.data_left;
1288433d6423SLionel Sambuc 			request->data_left -= temp_req.data_left;
1289433d6423SLionel Sambuc 
1290433d6423SLionel Sambuc 			/* Total length shall not become negative */
1291433d6423SLionel Sambuc 			USB_ASSERT(request->data_left >= 0,
1292433d6423SLionel Sambuc 				"Invalid amount of transfer data calculated");
1293433d6423SLionel Sambuc 
1294433d6423SLionel Sambuc 			/* Start actual data transfer */
1295433d6423SLionel Sambuc 			d->tx_stage(d->private_data, &temp_req);
1296433d6423SLionel Sambuc 
1297433d6423SLionel Sambuc 			/* Wait for response */
1298433d6423SLionel Sambuc 			hcd_device_wait(this_device, HCD_EVENT_ENDPOINT,
1299433d6423SLionel Sambuc 					request->endpoint);
1300433d6423SLionel Sambuc 
1301433d6423SLionel Sambuc 			/* Check response */
1302433d6423SLionel Sambuc 			if (EXIT_SUCCESS != d->check_error(d->private_data,
1303433d6423SLionel Sambuc 							request->type,
1304433d6423SLionel Sambuc 							request->endpoint,
1305433d6423SLionel Sambuc 							HCD_DIRECTION_OUT))
1306433d6423SLionel Sambuc 				return EXIT_FAILURE;
1307433d6423SLionel Sambuc 
1308433d6423SLionel Sambuc 		} while (0 != request->data_left);
1309433d6423SLionel Sambuc 
1310433d6423SLionel Sambuc 	} else
1311433d6423SLionel Sambuc 		USB_ASSERT(0, "Invalid transfer direction");
1312433d6423SLionel Sambuc 
1313433d6423SLionel Sambuc 	return EXIT_SUCCESS;
1314433d6423SLionel Sambuc }
1315