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