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
This commit is contained in:
Wojciech Zajac 2014-08-05 16:02:37 +02:00 committed by Ben Gras
parent d2032a25e2
commit a1a769dfe8
9 changed files with 597 additions and 155 deletions

View File

@ -2,7 +2,6 @@
* Minix3 USB hub driver implementation
*/
#include <assert.h> /* assert */
#include <string.h> /* memset */
#include <stdint.h>
@ -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);

View File

@ -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) {

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;

View File

@ -2,6 +2,8 @@
* Implementation of HCD URB scheduler
*/
#include <string.h> /* memset */
#include <usbd/hcd_common.h>
#include <usbd/hcd_ddekit.h>
#include <usbd/hcd_interface.h>
@ -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;
}

View File

@ -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 *

View File

@ -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];
};

View File

@ -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;