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:
parent
d2032a25e2
commit
a1a769dfe8
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user