From a1a769dfe82d9b87ef90bdb1e2966b2173068ec5 Mon Sep 17 00:00:00 2001 From: Wojciech Zajac Date: Tue, 5 Aug 2014 16:02:37 +0200 Subject: [PATCH] hcd & usb hub changes . HCD can now enumerate devices at hub ports . New devices are now dynamically allocated/freed . Some HCD cleanup was performed . Unified hub driver messages . HCD URB scheduler improvements . USB device threads with higher priority and better names . Some other fixes . Reorganized URB completion Change-Id: I83af772de049ab9354e9edc4aa14eedc10e99836 --- minix/drivers/usb/usb_hub/usb_hub.c | 125 +++++++-- minix/drivers/usb/usbd/base/usbd.c | 2 +- minix/drivers/usb/usbd/hcd/hcd.c | 245 +++++++++++++----- minix/drivers/usb/usbd/hcd/hcd_common.c | 139 +++++++++- minix/drivers/usb/usbd/hcd/hcd_ddekit.c | 16 +- minix/drivers/usb/usbd/hcd/hcd_schedule.c | 170 ++++++++++-- .../usb/usbd/include/usbd/hcd_common.h | 36 ++- .../usb/usbd/include/usbd/hcd_interface.h | 13 +- minix/include/ddekit/usb.h | 6 +- 9 files changed, 597 insertions(+), 155 deletions(-) diff --git a/minix/drivers/usb/usb_hub/usb_hub.c b/minix/drivers/usb/usb_hub/usb_hub.c index 519935a6f..186bc8721 100644 --- a/minix/drivers/usb/usb_hub/usb_hub.c +++ b/minix/drivers/usb/usb_hub/usb_hub.c @@ -2,7 +2,6 @@ * Minix3 USB hub driver implementation */ -#include /* assert */ #include /* memset */ #include @@ -49,6 +48,9 @@ static void hub_task(void *); /* Max number of hub ports */ #define USB_HUB_PORT_LIMIT 8 +/* Limits number of communication retries (when needed) */ +#define USB_HUB_MAX_TRIES 3 + /* Hub descriptor type */ #define USB_HUB_DESCRIPTOR_TYPE 0x29 @@ -164,7 +166,7 @@ static int hub_get_port_status(int, hub_port_status *); static port_change hub_handle_change(int, hub_port_status *); /* Handle port connection */ -static int hub_handle_connection(int); +static int hub_handle_connection(int, hub_port_status *); /* Handle port disconnection */ static int hub_handle_disconnection(int); @@ -281,10 +283,10 @@ hub_sef_hdlr(int type, sef_init_info_t * UNUSED(info)) return EXIT_SUCCESS; case SEF_INIT_LU: case SEF_INIT_RESTART: - HUB_MSG("Only 'fresh' SEF initialization supported\n"); + HUB_MSG("Only 'fresh' SEF initialization supported"); break; default: - HUB_MSG("illegal SEF type\n"); + HUB_MSG("Illegal SEF type"); break; } @@ -423,17 +425,9 @@ hub_task(void * UNUSED(arg)) * from 1, as defined by USB 2.0 document */ for (port = 1; port <= s->num_ports; port++) { if (hub_port_feature(port, SET_FEATURE, PORT_POWER)) { - HUB_MSG("Powering port %d failed", port); + HUB_MSG("Powering port%d failed", port); goto HUB_ERROR; } - - /* TODO: Will be needed later with HUB IPC */ -#if 0 - if (hub_port_feature(port, SET_FEATURE, PORT_RESET)) { - HUB_MSG("Resetting port %d failed", port); - goto HUB_ERROR; - } -#endif } /* @@ -450,7 +444,7 @@ hub_task(void * UNUSED(arg)) /* Get port status */ if (hub_get_port_status(port, &port_status)) { - HUB_MSG("Reading port %d status failed", port); + HUB_MSG("Reading port%d status failed", port); goto HUB_ERROR; } @@ -473,7 +467,7 @@ hub_task(void * UNUSED(arg)) if (hub_port_feature(port, CLEAR_FEATURE, PORT_POWER)) { - HUB_MSG("Halting port %d " + HUB_MSG("Halting port%d " "failed", port); goto HUB_ERROR; } @@ -488,7 +482,7 @@ hub_task(void * UNUSED(arg)) case HUB_CHANGE_COM_ERR: /* Serious error, hang */ - HUB_MSG("Handling port %d " + HUB_MSG("Handling port%d " "change failed", port); goto HUB_ERROR; } @@ -588,7 +582,7 @@ hub_port_feature(int port_num, class_code code, class_feature feature) return EXIT_FAILURE; if (!((feature == PORT_RESET) || (feature == PORT_POWER) || - (feature == C_PORT_CONNECTION))) + (feature == C_PORT_CONNECTION) || (feature == C_PORT_RESET))) return EXIT_FAILURE; /* Initialize EP configuration */ @@ -724,7 +718,7 @@ hub_handle_change(int port_num, hub_port_status * status) * to allow further polling */ if (hub_port_feature(port_num, CLEAR_FEATURE, C_PORT_CONNECTION)) { - HUB_MSG("Clearing port %d change bit failed", port_num); + HUB_MSG("Clearing port%d change bit failed", port_num); return HUB_CHANGE_COM_ERR; } @@ -736,7 +730,7 @@ hub_handle_change(int port_num, hub_port_status * status) */ /* Make hub disconnect and connect again */ if (hub_handle_disconnection(port_num) || - hub_handle_connection(port_num)) + hub_handle_connection(port_num, status)) return HUB_CHANGE_STATUS_ERR; else return HUB_CHANGE_CONN; @@ -760,7 +754,7 @@ hub_handle_change(int port_num, hub_port_status * status) * 3 */ /* Handle connection */ - if (hub_handle_connection(port_num)) + if (hub_handle_connection(port_num, status)) return HUB_CHANGE_STATUS_ERR; else return HUB_CHANGE_CONN; @@ -821,19 +815,98 @@ hub_handle_change(int port_num, hub_port_status * status) } -/* TODO: Add real connection/disconnection HCD IPC below */ /*===========================================================================* * hub_handle_connection * *===========================================================================*/ static int -hub_handle_connection(int port_num) +hub_handle_connection(int port_num, hub_port_status * status) { + int reset_tries; + long port_speed; + HUB_DEBUG_DUMP; - HUB_MSG("Device connected to port %d", port_num); + HUB_MSG("Device connected to port%d", port_num); - return ddekit_usb_info(driver_state.dev, (long)DDEKIT_HUB_PORT_CONN, - (long)port_num); + /* This should never happen if power-off works as intended */ + if (status->C_PORT_RESET) { + HUB_MSG("Unexpected reset state for port%d", port_num); + return EXIT_FAILURE; + } + + /* Start reset signaling for this port */ + if (hub_port_feature(port_num, SET_FEATURE, PORT_RESET)) { + HUB_MSG("Resetting port%d failed", port_num); + return EXIT_FAILURE; + } + + reset_tries = 0; + + /* Wait for reset completion */ + while (!status->C_PORT_RESET) { + /* To avoid endless loop */ + if (reset_tries >= USB_HUB_MAX_TRIES) { + HUB_MSG("Port%d reset took too long", port_num); + return EXIT_FAILURE; + } + + /* Get port status again */ + if (hub_get_port_status(port_num, status)) { + HUB_MSG("Reading port%d status failed", port_num); + return EXIT_FAILURE; + } + + reset_tries++; + } + + /* Reset completed */ + HUB_DEBUG_MSG("Port%d reset complete", port_num); + + /* Dump full status for analysis (high-speed, ...) */ + HUB_DEBUG_MSG("C_PORT_CONNECTION %1X", status->C_PORT_CONNECTION ); + HUB_DEBUG_MSG("C_PORT_ENABLE %1X", status->C_PORT_ENABLE ); + HUB_DEBUG_MSG("C_PORT_OVER_CURRENT %1X", status->C_PORT_OVER_CURRENT); + HUB_DEBUG_MSG("C_PORT_RESET %1X", status->C_PORT_RESET ); + HUB_DEBUG_MSG("C_PORT_SUSPEND %1X", status->C_PORT_SUSPEND ); + HUB_DEBUG_MSG("PORT_CONNECTION %1X", status->PORT_CONNECTION ); + HUB_DEBUG_MSG("PORT_ENABLE %1X", status->PORT_ENABLE ); + HUB_DEBUG_MSG("PORT_HIGH_SPEED %1X", status->PORT_HIGH_SPEED ); + HUB_DEBUG_MSG("PORT_INDICATOR %1X", status->PORT_INDICATOR ); + HUB_DEBUG_MSG("PORT_LOW_SPEED %1X", status->PORT_LOW_SPEED ); + HUB_DEBUG_MSG("PORT_OVER_CURRENT %1X", status->PORT_OVER_CURRENT ); + HUB_DEBUG_MSG("PORT_POWER %1X", status->PORT_POWER ); + HUB_DEBUG_MSG("PORT_RESET %1X", status->PORT_RESET ); + HUB_DEBUG_MSG("PORT_SUSPEND %1X", status->PORT_SUSPEND ); + HUB_DEBUG_MSG("PORT_TEST %1X", status->PORT_TEST ); + + /* Clear reset change bit for further devices */ + if (hub_port_feature(port_num, CLEAR_FEATURE, C_PORT_RESET)) { + HUB_MSG("Clearing port%d reset bit failed", port_num); + return EXIT_FAILURE; + } + + /* Should never happen */ + if (!status->PORT_CONNECTION || !status->PORT_ENABLE) { + HUB_MSG("Port%d unexpectedly unavailable", port_num); + return EXIT_FAILURE; + } + + /* Determine port speed from status bits */ + if (status->PORT_LOW_SPEED) { + if (status->PORT_HIGH_SPEED) { + HUB_MSG("Port%d has invalid speed flags", port_num); + return EXIT_FAILURE; + } else + port_speed = (long)DDEKIT_HUB_PORT_LS_CONN; + } else { + if (status->PORT_HIGH_SPEED) + port_speed = (long)DDEKIT_HUB_PORT_HS_CONN; + else + port_speed = (long)DDEKIT_HUB_PORT_FS_CONN; + } + + /* Signal to HCD that port has device connected at given speed */ + return ddekit_usb_info(driver_state.dev, port_speed, (long)port_num); } @@ -845,7 +918,7 @@ hub_handle_disconnection(int port_num) { HUB_DEBUG_DUMP; - HUB_MSG("Device disconnected from port %d", port_num); + HUB_MSG("Device disconnected from port%d", port_num); return ddekit_usb_info(driver_state.dev, (long)DDEKIT_HUB_PORT_DISCONN, (long)port_num); diff --git a/minix/drivers/usb/usbd/base/usbd.c b/minix/drivers/usb/usbd/base/usbd.c index 2d90b972d..ee7299bc9 100644 --- a/minix/drivers/usb/usbd/base/usbd.c +++ b/minix/drivers/usb/usbd/base/usbd.c @@ -126,7 +126,7 @@ usbd_start(void) DEBUG_DUMP; /* Driver's "main loop" is within DDEKit server thread */ - usbd_th = ddekit_thread_create(usbd_server_thread, NULL, "USBD"); + usbd_th = ddekit_thread_create(usbd_server_thread, NULL, "usbd"); /* After spawning, allow server thread to work */ if (NULL != usbd_th) { diff --git a/minix/drivers/usb/usbd/hcd/hcd.c b/minix/drivers/usb/usbd/hcd/hcd.c index 002c7e1d2..ad6ad6451 100644 --- a/minix/drivers/usb/usbd/hcd/hcd.c +++ b/minix/drivers/usb/usbd/hcd/hcd.c @@ -24,13 +24,17 @@ static void hcd_device_finish(hcd_device_state *, const char *); /* Procedure that finds device, waiting for given EP interrupt */ static hcd_device_state * hcd_get_child_for_ep(hcd_device_state *, hcd_reg1); +/* For HCD level, hub handling */ +static void hcd_add_child(hcd_device_state *, hcd_reg1, hcd_speed); +static void hcd_delete_child(hcd_device_state *, hcd_reg1); + /* Typical USD device communication procedures */ static int hcd_enumerate(hcd_device_state *); static int hcd_get_device_descriptor(hcd_device_state *); -static int hcd_set_address(hcd_device_state *, hcd_reg1); +static int hcd_set_address(hcd_device_state *); static int hcd_get_descriptor_tree(hcd_device_state *); static int hcd_set_configuration(hcd_device_state *, hcd_reg1); -static void hcd_handle_urb(hcd_device_state *, hcd_urb *); +static void hcd_handle_urb(hcd_device_state *); static int hcd_control_urb(hcd_device_state *, hcd_urb *); static int hcd_non_control_urb(hcd_device_state *, hcd_urb *); @@ -39,13 +43,14 @@ static int hcd_setup_packet(hcd_device_state *, hcd_ctrlrequest *, hcd_reg1); static int hcd_finish_setup(hcd_device_state *, void *, hcd_reg4); static int hcd_data_transfer(hcd_device_state *, hcd_datarequest *); +/* TODO: This is not meant to be explicitly visible outside DDEKit library + * but there is no other way to set thread priority for now */ +extern void _ddekit_thread_set_myprio(int); + /*===========================================================================* * Local definitions * *===========================================================================*/ -/* Array of available devices */ -static hcd_device_state hcd_device[HCD_NUM_MAX_DEVICES]; - /* TODO: This was added for compatibility with DDELinux drivers that * allow receiving less data than expected in URB, without error */ #define HCD_ANY_LENGTH 0xFFFFFFFFu @@ -73,9 +78,6 @@ hcd_handle_event(hcd_device_state * device, hcd_event event, hcd_reg1 val) "Device not marked as 'disconnected' " "for 'connection' event"); - /* Mark as 'plugged in' first */ - device->state = HCD_STATE_CONNECTION_PENDING; - /* Try creating new thread for device */ if (hcd_connect_device(device, hcd_device_thread)) USB_MSG("Device creation failed, nothing more " @@ -93,23 +95,53 @@ hcd_handle_event(hcd_device_state * device, hcd_event event, hcd_reg1 val) if (HCD_STATE_CONNECTED == device->state) hcd_disconnect_cb(device); + /* Handle device disconnection (freeing memory etc.) */ hcd_disconnect_device(device); - device->state = HCD_STATE_DISCONNECTED; - - /* Finally, zero everything to allow - * further connections with this object */ - memset(device, 0x00, sizeof(*device)); break; - case HCD_EVENT_PORT_CONNECTED: - /* TODO: Actual port handling */ - USB_MSG("Device connected to hub's port"); + case HCD_EVENT_PORT_LS_CONNECTED: + USB_ASSERT((HCD_STATE_DISCONNECTED != device->state), + "Device is marked as 'disconnected' " + "for 'hub port LS attach' event"); + + USB_MSG("Low speed device connected at " + "hub 0x%08X, port %u", device, val); + + hcd_add_child(device, val, HCD_SPEED_LOW); + break; + + case HCD_EVENT_PORT_FS_CONNECTED: + USB_ASSERT((HCD_STATE_DISCONNECTED != device->state), + "Device is marked as 'disconnected' " + "for 'hub port FS attach' event"); + + USB_MSG("Full speed device connected at " + "hub 0x%08X, port %u", device, val); + + hcd_add_child(device, val, HCD_SPEED_FULL); + break; + + case HCD_EVENT_PORT_HS_CONNECTED: + USB_ASSERT((HCD_STATE_DISCONNECTED != device->state), + "Device is marked as 'disconnected' " + "for 'hub port HS attach' event"); + + USB_MSG("High speed device connected at " + "hub 0x%08X, port %u", device, val); + + hcd_add_child(device, val, HCD_SPEED_HIGH); break; case HCD_EVENT_PORT_DISCONNECTED: - /* TODO: Actual port handling */ - USB_MSG("Device disconnected from hub's port"); + USB_ASSERT((HCD_STATE_DISCONNECTED != device->state), + "Device is marked as 'disconnected' " + "for 'hub port detach' event"); + + USB_MSG("Device disconnected from " + "hub 0x%08X, port %u", device, val); + + hcd_delete_child(device, val); break; case HCD_EVENT_ENDPOINT: @@ -154,11 +186,6 @@ hcd_handle_event(hcd_device_state * device, hcd_event event, hcd_reg1 val) void hcd_update_port(hcd_driver_state * driver, hcd_event event) { - hcd_device_state * d; - int devnum; - - const int maxdevs = (sizeof(hcd_device) / sizeof(hcd_device[0])); - DEBUG_DUMP; switch (event) { @@ -168,25 +195,11 @@ hcd_update_port(hcd_driver_state * driver, hcd_event event) "Device was already connected before " "receiving 'connection' event"); - /* For short */ - d = hcd_device; - - /* Find first unused device */ - for (devnum = 0; devnum < maxdevs; devnum++) { - if (HCD_STATE_DISCONNECTED == d[devnum].state) { - /* Assign free device */ - driver->port_device = &(d[devnum]); - /* Associate this device with driver */ - driver->port_device->driver = driver; - /* Device found, exit */ - return; - } - } - - /* Nothing found */ - USB_MSG("Device limit reached, no more " - "free device structures to use"); + /* Assign new blank device */ + driver->port_device = hcd_new_device(); + /* Associate this device with driver */ + driver->port_device->driver = driver; break; case HCD_EVENT_DISCONNECTED: @@ -195,7 +208,10 @@ hcd_update_port(hcd_driver_state * driver, hcd_event event) "Device was already disconnected before " "receiving 'disconnection' event"); - /* Clear port device */ + /* Release device */ + hcd_delete_device(driver->port_device); + + /* Clear port device pointer */ driver->port_device = NULL; break; @@ -215,6 +231,10 @@ hcd_device_thread(void * thread_args) DEBUG_DUMP; + /* Set device thread priority higher so it + * won't change context unless explicitly locked */ + _ddekit_thread_set_myprio(2); + /* Retrieve structures from generic data */ this_device = (hcd_device_state *)thread_args; @@ -234,7 +254,7 @@ hcd_device_thread(void * thread_args) for(;;) { /* Block and wait for something like 'submit URB' */ hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL); - hcd_handle_urb(this_device, this_device->urb); + hcd_handle_urb(this_device); } /* Finish device handling to avoid leaving thread */ @@ -266,13 +286,95 @@ hcd_device_finish(hcd_device_state * this_device, const char * finish_msg) static hcd_device_state * hcd_get_child_for_ep(hcd_device_state * device, hcd_reg1 ep) { + hcd_device_state * d; + hcd_device_state * found; + int child_num; + DEBUG_DUMP; - /* TODO: When device has multiple children (hub), - * this will allow routing interrupt login downstream */ - ((void)ep); + /* Start with parent */ + d = device; + child_num = 0; - return device; + /* Nothing found yet */ + found = NULL; + + /* When device has multiple children (hub), this allows + * routing interrupt login downstream */ + do { + /* Must exist and match requirements */ + if ((NULL != d) && + (HCD_EVENT_ENDPOINT == d->wait_event) && + (ep == d->wait_ep)) { + + /* Waiting for EP by multiple devices is mutually + * exclusive, as long as scheduler works correctly */ + USB_ASSERT(NULL == found, "More than one device waits " + "for given endpoint interrupt"); + found = d; + } + + /* Check next child */ + d = device->child[child_num++]; + + } while(child_num <= HCD_CHILDREN); + + return found; +} + + +/*===========================================================================* + * hcd_add_child * + *===========================================================================*/ +static void +hcd_add_child(hcd_device_state * parent, hcd_reg1 port, hcd_speed speed) +{ + DEBUG_DUMP; + + USB_ASSERT(port < HCD_CHILDREN, "Port number too high"); + USB_ASSERT(NULL == parent->child[port], "Child device already exists"); + + /* Basic addition */ + parent->child[port] = hcd_new_device(); + parent->child[port]->parent = parent; + + /* Inherit parent's driver */ + parent->child[port]->driver = parent->driver; + + /* Remember speed, determined by hub driver */ + parent->child[port]->speed = speed; + + /* Try creating new thread for device */ + if (hcd_connect_device(parent->child[port], hcd_device_thread)) + USB_MSG("Device creation failed, nothing more " + "will happen until disconnected"); +} + + +/*===========================================================================* + * hcd_delete_child * + *===========================================================================*/ +static void +hcd_delete_child(hcd_device_state * parent, hcd_reg1 port) +{ + DEBUG_DUMP; + + USB_ASSERT(port < HCD_CHILDREN, "Port number too high"); + USB_ASSERT(NULL != parent->child[port], "Child device does not exist"); + + /* If connect callback was used before, call + * it's equivalent to signal disconnection */ + if (HCD_STATE_CONNECTED == parent->child[port]->state) + hcd_disconnect_cb(parent->child[port]); + + /* Disconnect to release fields */ + hcd_disconnect_device(parent->child[port]); + + /* Delete to release device itself */ + hcd_delete_device(parent->child[port]); + + /* Mark as released */ + parent->child[port] = NULL; } @@ -288,11 +390,15 @@ hcd_enumerate(hcd_device_state * this_device) d = this_device->driver; - /* First let driver reset device */ - if (EXIT_SUCCESS != d->reset_device(d->private_data, - &(this_device->speed))) { - USB_MSG("Failed to reset device"); - return EXIT_FAILURE; + /* Having a parent device also means being reseted by it + * so only reset devices that have no parents */ + if (NULL == this_device->parent) { + /* First let driver reset device */ + if (EXIT_SUCCESS != d->reset_device(d->private_data, + &(this_device->speed))) { + USB_MSG("Failed to reset device"); + return EXIT_FAILURE; + } } /* Default MaxPacketSize, based on speed */ @@ -307,11 +413,8 @@ hcd_enumerate(hcd_device_state * this_device) return EXIT_FAILURE; } - /* TODO: Dynamic device addressing should be added here, when more - * than one device can be handled at a time */ - - /* Set address */ - if (EXIT_SUCCESS != hcd_set_address(this_device, HCD_ATTACHED_ADDR)) { + /* Set reserved address */ + if (EXIT_SUCCESS != hcd_set_address(this_device)) { USB_MSG("Failed to set device address"); return EXIT_FAILURE; } @@ -405,20 +508,21 @@ hcd_get_device_descriptor(hcd_device_state * this_device) * hcd_set_address * *===========================================================================*/ static int -hcd_set_address(hcd_device_state * this_device, hcd_reg1 address) +hcd_set_address(hcd_device_state * this_device) { hcd_ctrlrequest setup; DEBUG_DUMP; /* Check for legal USB device address (must be non-zero as well) */ - USB_ASSERT((address > HCD_DEFAULT_ADDR) && (address <= HCD_LAST_ADDR), + USB_ASSERT((this_device->reserved_address > HCD_DEFAULT_ADDR) && + (this_device->reserved_address <= HCD_LAST_ADDR), "Illegal device address supplied"); /* TODO: magic numbers, no header for these */ setup.bRequestType = 0x00; /* OUT */ setup.bRequest = 0x05; /* Set address */ - setup.wValue = address; + setup.wValue = this_device->reserved_address; setup.wIndex = 0x0000; setup.wLength = 0x0000; @@ -433,7 +537,7 @@ hcd_set_address(hcd_device_state * this_device, hcd_reg1 address) hcd_os_nanosleep(HCD_NANOSLEEP_MSEC(5)); /* Remember what was assigned in hardware */ - this_device->address = address; + this_device->current_address = this_device->reserved_address; return EXIT_SUCCESS; } @@ -556,12 +660,17 @@ hcd_set_configuration(hcd_device_state * this_device, hcd_reg1 configuration) * hcd_handle_urb * *===========================================================================*/ static void -hcd_handle_urb(hcd_device_state * this_device, hcd_urb * urb) +hcd_handle_urb(hcd_device_state * this_device) { + hcd_urb * urb; int transfer_status; DEBUG_DUMP; + /* Retrieve URB */ + urb = this_device->urb; + + USB_ASSERT(NULL != urb, "No URB supplied"); USB_ASSERT(this_device == urb->target_device, "Unknown device for URB"); /* Only if URB parsing was completed... */ @@ -596,7 +705,7 @@ hcd_handle_urb(hcd_device_state * this_device, hcd_urb * urb) USB_MSG("Invalid URB supplied"); /* Signal scheduler that URB was handled */ - urb->handled(); + urb->handled(urb); } @@ -750,7 +859,7 @@ hcd_setup_packet(hcd_device_state * this_device, hcd_ctrlrequest * setup, USB_ASSERT(this_device->max_packet_size >= HCD_LS_MAXPACKETSIZE, "Illegal MaxPacketSize"); USB_ASSERT(ep <= HCD_LAST_EP, "Invalid EP number"); - USB_ASSERT(this_device->address <= HCD_LAST_ADDR, + USB_ASSERT(this_device->current_address <= HCD_LAST_ADDR, "Invalid device address"); /* Initially... */ @@ -759,7 +868,7 @@ hcd_setup_packet(hcd_device_state * this_device, hcd_ctrlrequest * setup, this_device->control_len = 0; /* Nothing read yet */ /* Set parameters for further communication */ - d->setup_device(d->private_data, ep, this_device->address); + d->setup_device(d->private_data, ep, this_device->current_address); /* Send setup packet */ d->setup_stage(d->private_data, setup); @@ -929,8 +1038,8 @@ hcd_data_transfer(hcd_device_state * this_device, hcd_datarequest * request) USB_ASSERT((request->endpoint <= HCD_LAST_EP) && (request->endpoint > HCD_DEFAULT_EP), "Invalid EP number"); - USB_ASSERT((this_device->address <= HCD_LAST_ADDR) && - (this_device->address > HCD_DEFAULT_ADDR), + USB_ASSERT((this_device->current_address <= HCD_LAST_ADDR) && + (this_device->current_address > HCD_DEFAULT_ADDR), "Invalid device address"); /* Initially... */ @@ -938,7 +1047,7 @@ hcd_data_transfer(hcd_device_state * this_device, hcd_datarequest * request) /* Set parameters for further communication */ d->setup_device(d->private_data, request->endpoint, - this_device->address); + this_device->current_address); /* Check transfer direction first */ if (HCD_DIRECTION_IN == request->direction) { diff --git a/minix/drivers/usb/usbd/hcd/hcd_common.c b/minix/drivers/usb/usbd/hcd/hcd_common.c index 23f3f660d..59301fc09 100644 --- a/minix/drivers/usb/usbd/hcd/hcd_common.c +++ b/minix/drivers/usb/usbd/hcd/hcd_common.c @@ -21,10 +21,15 @@ /*===========================================================================* * Local prototypes * *===========================================================================*/ +/* Descriptor related operations */ static int hcd_fill_configuration(hcd_reg1 *, int, hcd_configuration *, int); static int hcd_fill_interface(hcd_reg1 *, int, hcd_interface *, int); static int hcd_fill_endpoint(hcd_reg1 *, int, hcd_endpoint *); +/* Handling free USB device addresses */ +static hcd_reg1 hcd_reserve_addr(hcd_driver_state *); +static void hcd_release_addr(hcd_driver_state *, hcd_reg1); + /*===========================================================================* * hcd_os_interrupt_attach * @@ -188,46 +193,84 @@ hcd_os_nanosleep(int nanosec) /*===========================================================================* - * hcd_init_device * + * hcd_connect_device * *===========================================================================*/ int hcd_connect_device(hcd_device_state * this_device, hcd_thread_function funct) { DEBUG_DUMP; - if ((NULL != this_device->lock) || (NULL != this_device->thread)) { - USB_MSG("Device data already allocated"); + /* This is meant to allow thread name distinction + * and should not be used for anything else */ + static unsigned int devnum = 0; + + /* Should be able to hold device prefix and some number */ + char devname[] = "dev.........."; + + USB_ASSERT((NULL == this_device->lock) && + (NULL == this_device->thread) && + (HCD_DEFAULT_ADDR == this_device->reserved_address) && + (HCD_STATE_DISCONNECTED == this_device->state), + "Device structure not clean"); + + /* Mark as 'plugged in' to avoid treating device + * as 'disconnected' in case of errors below */ + this_device->state = HCD_STATE_CONNECTION_PENDING; + + /* Reserve device address for further use if available */ + if (HCD_DEFAULT_ADDR == (this_device->reserved_address = + hcd_reserve_addr(this_device->driver))) { + USB_MSG("No free device addresses"); return EXIT_FAILURE; } - if (NULL == (this_device->lock = ddekit_sem_init(0))) + /* Get 'lock' that makes device thread wait for events to occur */ + if (NULL == (this_device->lock = ddekit_sem_init(0))) { + USB_MSG("Failed to initialize thread lock"); return EXIT_FAILURE; + } + /* Prepare name */ + snprintf(devname, sizeof(devname), "dev%u", devnum++); + + /* Get thread itself */ if (NULL == (this_device->thread = ddekit_thread_create(funct, - this_device, - "Device"))) { - ddekit_sem_deinit(this_device->lock); + this_device, + (const char *)devname))) { + USB_MSG("Failed to initialize USB device thread"); return EXIT_FAILURE; } return EXIT_SUCCESS; } + /*===========================================================================* - * hcd_deinit_device * + * hcd_disconnect_device * *===========================================================================*/ void hcd_disconnect_device(hcd_device_state * this_device) { DEBUG_DUMP; + /* TODO: This should disconnect all the children if they exist */ + + /* Clean configuration tree in case it was acquired */ hcd_tree_cleanup(&(this_device->config_tree)); - ddekit_thread_terminate(this_device->thread); - ddekit_sem_deinit(this_device->lock); + /* Release allocated resources */ + if (NULL != this_device->thread) + ddekit_thread_terminate(this_device->thread); + if (NULL != this_device->lock) + ddekit_sem_deinit(this_device->lock); - this_device->thread = NULL; - this_device->lock = NULL; + /* Release reserved address */ + if (HCD_DEFAULT_ADDR != this_device->reserved_address) + hcd_release_addr(this_device->driver, + this_device->reserved_address); + + /* Mark as disconnected */ + this_device->state = HCD_STATE_DISCONNECTED; } @@ -265,6 +308,37 @@ hcd_device_continue(hcd_device_state * device, hcd_event event, hcd_reg1 ep) } +/*===========================================================================* + * hcd_new_device * + *===========================================================================*/ +hcd_device_state * +hcd_new_device(void) +{ + hcd_device_state * d; + + DEBUG_DUMP; + + /* One new blank device */ + d = calloc(1, sizeof(*d)); + + USB_ASSERT(NULL != d, "Failed to allocate device"); + + return d; +} + + +/*===========================================================================* + * hcd_delete_device * + *===========================================================================*/ +void +hcd_delete_device(hcd_device_state * d) +{ + DEBUG_DUMP; + + free(d); +} + + /*===========================================================================* * hcd_buffer_to_tree * *===========================================================================*/ @@ -555,3 +629,44 @@ hcd_fill_endpoint(hcd_reg1 * buf, int len, hcd_endpoint * e) return EXIT_SUCCESS; } + + +/*===========================================================================* + * hcd_reserve_addr * + *===========================================================================*/ +static hcd_reg1 +hcd_reserve_addr(hcd_driver_state * driver) +{ + hcd_reg1 addr; + + DEBUG_DUMP; + + for (addr = HCD_FIRST_ADDR; addr <= HCD_LAST_ADDR; addr++) { + if (HCD_ADDR_AVAILABLE == driver->dev_addr[addr]) { + USB_DBG("Reserved address: %u", addr); + driver->dev_addr[addr] = HCD_ADDR_USED; + return addr; + } + } + + /* This means error */ + return HCD_DEFAULT_ADDR; +} + + +/*===========================================================================* + * hcd_release_addr * + *===========================================================================*/ +static void +hcd_release_addr(hcd_driver_state * driver, hcd_reg1 addr) +{ + DEBUG_DUMP; + + USB_ASSERT((addr > HCD_DEFAULT_ADDR) && (addr <= HCD_LAST_ADDR), + "Invalid device address to be released"); + USB_ASSERT(HCD_ADDR_USED == driver->dev_addr[addr], + "Attempted to release unused address"); + + USB_DBG("Released address: %u", addr); + driver->dev_addr[addr] = HCD_ADDR_AVAILABLE; +} diff --git a/minix/drivers/usb/usbd/hcd/hcd_ddekit.c b/minix/drivers/usb/usbd/hcd/hcd_ddekit.c index b982ff1dd..dbbf6e120 100644 --- a/minix/drivers/usb/usbd/hcd/hcd_ddekit.c +++ b/minix/drivers/usb/usbd/hcd/hcd_ddekit.c @@ -184,14 +184,10 @@ ddekit_usb_get_device_id(struct ddekit_usb_dev * dev, int ddekit_usb_submit_urb(struct ddekit_usb_urb * d_urb) { - hcd_device_state * dev; hcd_urb * urb; DEBUG_DUMP; - /* Retrieve info on device state from DDEKit's URB */ - dev = (hcd_device_state *)(d_urb->dev); - /* Get new URB */ urb = hcd_new_urb(); @@ -213,6 +209,8 @@ ddekit_usb_cancle_urb(struct ddekit_usb_urb * d_urb) DEBUG_DUMP; /* TODO: UNUSED for argument won't work */ ((void)d_urb); + /* TODO: No driver will require this any time soon */ + USB_ASSERT(0, "URB cancellation not supported"); return EXIT_SUCCESS; } @@ -464,8 +462,14 @@ hcd_decode_info(long type, long invalue, hcd_event * event, hcd_reg1 * outvalue) "Illegal USB info value received"); switch ((ddekit_msg_type_t)type) { - case DDEKIT_HUB_PORT_CONN: - *event = HCD_EVENT_PORT_CONNECTED; + case DDEKIT_HUB_PORT_LS_CONN: + *event = HCD_EVENT_PORT_LS_CONNECTED; + break; + case DDEKIT_HUB_PORT_FS_CONN: + *event = HCD_EVENT_PORT_FS_CONNECTED; + break; + case DDEKIT_HUB_PORT_HS_CONN: + *event = HCD_EVENT_PORT_HS_CONNECTED; break; case DDEKIT_HUB_PORT_DISCONN: *event = HCD_EVENT_PORT_DISCONNECTED; diff --git a/minix/drivers/usb/usbd/hcd/hcd_schedule.c b/minix/drivers/usb/usbd/hcd/hcd_schedule.c index 50a9ff32f..5da6bcb11 100644 --- a/minix/drivers/usb/usbd/hcd/hcd_schedule.c +++ b/minix/drivers/usb/usbd/hcd/hcd_schedule.c @@ -2,6 +2,8 @@ * Implementation of HCD URB scheduler */ +#include /* memset */ + #include #include #include @@ -13,11 +15,21 @@ /*===========================================================================* * Required for scheduling * *===========================================================================*/ +/* TODO: Like in DDEKit but power of 2 */ +#define HCD_MAX_URBS 16 + +/* TODO: Structure to hold URBs in DDEKit is limited so this is no better + * (but because of that, there is no need for another malloc) */ +static hcd_urb * stored_urb[HCD_MAX_URBS]; + +/* Number of URBs stored during operation */ +static int num_stored_urbs; + /* Scheduler thread */ static hcd_thread * urb_thread; -/* TODO: This will soon become URB list */ -static hcd_urb * current_urb; +/* This allows waiting for URB */ +static hcd_lock * urb_lock; /* This allows waiting for completion */ static hcd_lock * handled_lock; @@ -26,7 +38,16 @@ static hcd_lock * handled_lock; static void hcd_urb_scheduler_task(void *); /* Completion callback */ -static void hcd_urb_handled(void); +static void hcd_urb_handled(hcd_urb *); + +/* Stores URB to be handled */ +static int hcd_store_urb(hcd_urb *); + +/* Removes stored URB */ +static void hcd_remove_urb(hcd_urb *); + +/* Gets URB to be handled next (based on priority) */ +static hcd_urb * hcd_get_urb(void); /*===========================================================================* @@ -37,16 +58,26 @@ usbd_init_scheduler(void) { DEBUG_DUMP; - urb_thread = ddekit_thread_create(hcd_urb_scheduler_task, NULL, "URB"); + /* Reset everything */ + num_stored_urbs = 0; + memset(stored_urb, 0, sizeof(stored_urb)); + + urb_thread = ddekit_thread_create(hcd_urb_scheduler_task, NULL, "urb"); if (NULL == urb_thread) goto ERR1; + urb_lock = ddekit_sem_init(0); + if (NULL == urb_lock) + goto ERR2; + handled_lock = ddekit_sem_init(0); if (NULL == handled_lock) - goto ERR2; + goto ERR3; return EXIT_SUCCESS; + ERR3: + ddekit_sem_deinit(urb_lock); ERR2: ddekit_thread_terminate(urb_thread); ERR1: @@ -64,6 +95,8 @@ usbd_deinit_scheduler(void) ddekit_sem_deinit(handled_lock); + ddekit_sem_deinit(urb_lock); + ddekit_thread_terminate(urb_thread); } @@ -79,10 +112,18 @@ hcd_schedule_urb(hcd_urb * urb) /* Tell URB what to call on completion */ urb->handled = hcd_urb_handled; - /* TODO: Proper list handling */ - current_urb = urb; + /* Store and check if scheduler should be unlocked */ + if (EXIT_SUCCESS == hcd_store_urb(urb)) { - return EXIT_SUCCESS; + /* This is the first stored URB (possibly in a row) + * so unlock scheduler */ + if (1 == num_stored_urbs) + ddekit_sem_up(urb_lock); + + return EXIT_SUCCESS; + } + + return EXIT_FAILURE; } @@ -94,9 +135,7 @@ hcd_unschedule_urb(hcd_urb * urb) { DEBUG_DUMP; - /* TODO: Proper list handling */ - ((void)urb); - current_urb = NULL; + hcd_remove_urb(urb); } @@ -106,26 +145,33 @@ hcd_unschedule_urb(hcd_urb * urb) static void hcd_urb_scheduler_task(void * UNUSED(arg)) { + hcd_urb * current_urb; + DEBUG_DUMP; for (;;) { - if (NULL != current_urb) { - /* Tell device that this is it's URB */ - current_urb->target_device->urb = current_urb; + /* Wait for scheduler to unlock on first URB submit */ + if (0 == num_stored_urbs) + ddekit_sem_down(urb_lock); - /* Start handling URB event */ - hcd_handle_event(current_urb->target_device, - HCD_EVENT_URB, HCD_UNUSED_VAL); + /* Get URB */ + current_urb = hcd_get_urb(); - /* Wait for completion */ - ddekit_sem_down(handled_lock); + /* Check for mismatch */ + USB_ASSERT(NULL != current_urb, "URB missing after URB unlock"); - /* Call completion regardless of status */ - hcd_completion_cb(current_urb); - } + /* Tell device that this is its URB */ + current_urb->target_device->urb = current_urb; - /* TODO: Temporary, poor scheduling with forced sleep */ - ddekit_thread_msleep(50); + /* Start handling URB event */ + hcd_handle_event(current_urb->target_device, + HCD_EVENT_URB, HCD_UNUSED_VAL); + + /* Wait for completion */ + ddekit_sem_down(handled_lock); + + /* Handled, forget about it */ + current_urb->target_device->urb = NULL; } } @@ -134,10 +180,84 @@ hcd_urb_scheduler_task(void * UNUSED(arg)) * hcd_urb_handled * *===========================================================================*/ static void -hcd_urb_handled(void) +hcd_urb_handled(hcd_urb * urb) { DEBUG_DUMP; + /* Call completion regardless of status */ + hcd_completion_cb(urb); + /* Handling completed */ ddekit_sem_up(handled_lock); } + + +/*===========================================================================* + * hcd_store_urb * + *===========================================================================*/ +static int +hcd_store_urb(hcd_urb * urb) +{ + int i; + + DEBUG_DUMP; + + for (i = 0; i < HCD_MAX_URBS; i++) { + if (NULL == stored_urb[i]) { + stored_urb[i] = urb; + num_stored_urbs++; + return EXIT_SUCCESS; + } + } + + USB_MSG("No more free URBs"); + + return EXIT_FAILURE; +} + +/*===========================================================================* + * hcd_remove_urb * + *===========================================================================*/ +static void +hcd_remove_urb(hcd_urb * urb) +{ + int i; + + DEBUG_DUMP; + + for (i = 0; i < HCD_MAX_URBS; i++) { + if (urb == stored_urb[i]) { + stored_urb[i] = NULL; + num_stored_urbs--; + return; + } + } + + USB_ASSERT(0, "URB to be removed, was never stored"); +} + +/*===========================================================================* + * hcd_get_urb * + *===========================================================================*/ +static hcd_urb * +hcd_get_urb(void) +{ + static int i = 0; + int checked; + + DEBUG_DUMP; + + /* TODO: Some priority checking may be here */ + for (checked = 0; checked < HCD_MAX_URBS; checked++) { + /* To avoid starting from 0 every + * time (potential starvation) */ + i = (i + 1) % HCD_MAX_URBS; + + /* When found */ + if (NULL != stored_urb[i]) + return stored_urb[i]; + } + + /* Nothing submitted yet */ + return NULL; +} diff --git a/minix/drivers/usb/usbd/include/usbd/hcd_common.h b/minix/drivers/usb/usbd/include/usbd/hcd_common.h index 133003492..5feeef6a1 100644 --- a/minix/drivers/usb/usbd/include/usbd/hcd_common.h +++ b/minix/drivers/usb/usbd/include/usbd/hcd_common.h @@ -124,12 +124,14 @@ hcd_direction; /* Possible asynchronous HCD events */ typedef enum { - HCD_EVENT_CONNECTED = 0, - HCD_EVENT_DISCONNECTED, - HCD_EVENT_PORT_CONNECTED, - HCD_EVENT_PORT_DISCONNECTED, - HCD_EVENT_ENDPOINT, - HCD_EVENT_URB, + HCD_EVENT_CONNECTED = 0, /* Device connected directly to root */ + HCD_EVENT_DISCONNECTED, /* Directly connected device removed */ + HCD_EVENT_PORT_LS_CONNECTED, /* Low speed device connected to hub */ + HCD_EVENT_PORT_FS_CONNECTED, /* Full speed device connected to hub */ + HCD_EVENT_PORT_HS_CONNECTED, /* High speed device connected to hub */ + HCD_EVENT_PORT_DISCONNECTED, /* Device disconnected from hub */ + HCD_EVENT_ENDPOINT, /* Something happened at endpoint */ + HCD_EVENT_URB, /* URB was submitted by device driver */ HCD_EVENT_INVALID = 0xFF } hcd_event; @@ -166,6 +168,10 @@ typedef struct usb_ctrlrequest hcd_ctrlrequest; * see MAXPAYLOAD in TXMAXP/RXMAXP */ #define MAX_WTOTALLENGTH 1024 +/* TODO: This has corresponding redefinition in hub driver */ +/* Limit of child devices for each parent */ +#define HCD_CHILDREN 8u + /* Forward declarations */ typedef struct hcd_datarequest hcd_datarequest; typedef struct hcd_urb hcd_urb; @@ -192,7 +198,7 @@ struct hcd_urb { /* Basic */ void * original_urb; hcd_device_state * target_device; - void (*handled)(void); /* URB handled callback */ + void (*handled)(hcd_urb *); /* URB handled callback */ /* Transfer (in/out signifies what may be overwritten by HCD) */ hcd_ctrlrequest * in_setup; @@ -212,6 +218,7 @@ struct hcd_urb { struct hcd_device_state { hcd_device_state * parent; /* In case of hub attachment */ + hcd_device_state * child[HCD_CHILDREN]; /* In case of being hub */ hcd_driver_state * driver; /* Specific HCD driver object */ hcd_thread * thread; hcd_lock * lock; @@ -225,7 +232,8 @@ struct hcd_device_state { hcd_reg1 max_packet_size; hcd_speed speed; hcd_state state; - hcd_reg1 address; + hcd_reg1 reserved_address; + hcd_reg1 current_address; /* * Control transfer's local data: @@ -254,7 +262,9 @@ struct hcd_device_state { #define HCD_DEFAULT_EP 0x00u #define HCD_DEFAULT_ADDR 0x00u #define HCD_DEFAULT_CONFIG 0x00u +#define HCD_FIRST_ADDR 0x01u #define HCD_LAST_ADDR 0x7Fu +#define HCD_TOTAL_ADDR 0x80u #define HCD_LAST_EP 0x0Fu #define HCD_TOTAL_EP 0x10u #define HCD_UNUSED_VAL 0xFFu /* When number not needed */ @@ -263,12 +273,6 @@ struct hcd_device_state { #define HCD_LOWEST_INTERVAL 0x00u #define HCD_HIGHEST_INTERVAL 0xFFu -/* Max number of supported devices */ -#define HCD_NUM_MAX_DEVICES 0x08u - -/* TODO: One device only */ -#define HCD_ATTACHED_ADDR 0x01u - /* Translates configuration number for 'set configuration' */ #define HCD_SET_CONFIG_NUM(num) ((num)+0x01u) @@ -325,6 +329,10 @@ void hcd_device_wait(hcd_device_state *, hcd_event, hcd_reg1); /* Unlocks device thread halted by 'hcd_device_wait' */ void hcd_device_continue(hcd_device_state *, hcd_event, hcd_reg1); +/* Allocation/deallocation of device structures */ +hcd_device_state * hcd_new_device(void); +void hcd_delete_device(hcd_device_state *); + /*===========================================================================* * Descriptor tree calls * diff --git a/minix/drivers/usb/usbd/include/usbd/hcd_interface.h b/minix/drivers/usb/usbd/include/usbd/hcd_interface.h index 8ab31f486..ed7486808 100644 --- a/minix/drivers/usb/usbd/include/usbd/hcd_interface.h +++ b/minix/drivers/usb/usbd/include/usbd/hcd_interface.h @@ -18,9 +18,17 @@ /* Can be returned by 'read_data' to indicate error */ #define HCD_READ_ERR -1 +/* Possible states of USB device address */ +typedef enum { + + HCD_ADDR_AVAILABLE = 0, /* Default for reset */ + HCD_ADDR_USED +} +hcd_addr_state; + /*===========================================================================* - * HCD driver structure to be filled + * HCD driver structure to be filled * *===========================================================================*/ struct hcd_driver_state { /* Standard USB controller procedures */ @@ -43,6 +51,9 @@ struct hcd_driver_state { /* TODO: Only one port for each driver */ /* Represents device attached to USB port handled by this driver */ hcd_device_state * port_device; + + /* Array to hold information of unused device addresses */ + hcd_addr_state dev_addr[HCD_TOTAL_ADDR]; }; diff --git a/minix/include/ddekit/usb.h b/minix/include/ddekit/usb.h index 1d3fc7438..e6bb81066 100644 --- a/minix/include/ddekit/usb.h +++ b/minix/include/ddekit/usb.h @@ -81,8 +81,10 @@ struct ddekit_usb_urb { /* USB message types */ typedef enum { - DDEKIT_HUB_PORT_CONN, - DDEKIT_HUB_PORT_DISCONN + DDEKIT_HUB_PORT_LS_CONN, /* Low speed device connected */ + DDEKIT_HUB_PORT_FS_CONN, /* Full speed device connected */ + DDEKIT_HUB_PORT_HS_CONN, /* High speed device connected */ + DDEKIT_HUB_PORT_DISCONN /* Device disconnected */ } ddekit_msg_type_t;