HCD changes

. Verbose debug output in HCD
	. HCD now outputs threading information in debug build.
	. With this patch, multiple high speed usb sticks can work with
	  high speed usb hub.
	. Allow HCD to use a sequence of hubs
	. Make HCD ignore URBs for removed devices
	. HCD has list of allocated devices to determine validity of events
	. DDEKit debug build's error fixed
	. HCD handles pending URB on device detach
	. With this, URB scheduler is no longer locked on device detach
	  NAK limit fixed

Change-Id: I60036fa9902b98ac9090196dfe08a385155041c8
This commit is contained in:
Wojciech Zajac 2014-08-08 09:49:05 +02:00 committed by Ben Gras
parent d401ea8186
commit 0d6daaaebf
11 changed files with 394 additions and 129 deletions

View File

@ -38,7 +38,7 @@ main(int UNUSED(argc), char * UNUSED(argv[]))
int ret_val; int ret_val;
USB_MSG("Starting USBD"); USB_MSG("Starting USBD");
USB_DBG("Built: %s %s", __DATE__, __TIME__); USB_MSG("Built: %s %s", __DATE__, __TIME__);
/* Basic SEF,DDE,... initialization */ /* Basic SEF,DDE,... initialization */
usbd_init(); usbd_init();
@ -75,7 +75,7 @@ main(int UNUSED(argc), char * UNUSED(argv[]))
static int static int
usbd_sef_handler(int type, sef_init_info_t * UNUSED(info)) usbd_sef_handler(int type, sef_init_info_t * UNUSED(info))
{ {
DEBUG_DUMP; /* No DEBUG_DUMP, threading unavailable yet */
switch (type) { switch (type) {
case SEF_INIT_FRESH: case SEF_INIT_FRESH:
@ -126,7 +126,7 @@ usbd_start(void)
DEBUG_DUMP; DEBUG_DUMP;
/* Driver's "main loop" is within DDEKit server thread */ /* 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, "ddekit_usb");
/* After spawning, allow server thread to work */ /* After spawning, allow server thread to work */
if (NULL != usbd_th) { if (NULL != usbd_th) {
@ -157,16 +157,18 @@ usbd_start(void)
static void static void
usbd_init(void) usbd_init(void)
{ {
DEBUG_DUMP; /* No DEBUG_DUMP, threading unavailable yet */
/* Set one handler for all messages */ /* Set one handler for all messages */
sef_setcb_init_fresh(usbd_sef_handler); sef_setcb_init_fresh(usbd_sef_handler);
sef_setcb_init_lu(usbd_sef_handler); sef_setcb_init_lu(usbd_sef_handler);
sef_setcb_init_restart(usbd_sef_handler); sef_setcb_init_restart(usbd_sef_handler);
sef_setcb_signal_handler(usbd_signal_handler);
/* Initialize DDEkit (involves sef_startup()) */ /* Initialize DDEkit (involves sef_startup()) */
ddekit_init(); ddekit_init();
/* After threading initialization, add signal handler */
sef_setcb_signal_handler(usbd_signal_handler);
} }

View File

@ -28,6 +28,8 @@ static hcd_device_state * hcd_get_child_for_ep(hcd_device_state *, hcd_reg1);
/* For HCD level, hub handling */ /* For HCD level, hub handling */
static void hcd_add_child(hcd_device_state *, hcd_reg1, hcd_speed); static void hcd_add_child(hcd_device_state *, hcd_reg1, hcd_speed);
static void hcd_delete_child(hcd_device_state *, hcd_reg1); static void hcd_delete_child(hcd_device_state *, hcd_reg1);
static void hcd_disconnect_tree(hcd_device_state *);
static void hcd_dump_tree(hcd_device_state *, hcd_reg1);
/* Typical USD device communication procedures */ /* Typical USD device communication procedures */
static int hcd_enumerate(hcd_device_state *); static int hcd_enumerate(hcd_device_state *);
@ -36,6 +38,7 @@ static int hcd_set_address(hcd_device_state *);
static int hcd_get_descriptor_tree(hcd_device_state *); static int hcd_get_descriptor_tree(hcd_device_state *);
static int hcd_set_configuration(hcd_device_state *, hcd_reg1); static int hcd_set_configuration(hcd_device_state *, hcd_reg1);
static void hcd_handle_urb(hcd_device_state *); static void hcd_handle_urb(hcd_device_state *);
static void hcd_complete_urb(hcd_device_state *);
static int hcd_control_urb(hcd_device_state *, hcd_urb *); static int hcd_control_urb(hcd_device_state *, hcd_urb *);
static int hcd_non_control_urb(hcd_device_state *, hcd_urb *); static int hcd_non_control_urb(hcd_device_state *, hcd_urb *);
@ -69,13 +72,30 @@ hcd_handle_event(hcd_device_state * device, hcd_event event, hcd_reg1 val)
{ {
DEBUG_DUMP; DEBUG_DUMP;
/* No device may be supplied */ /* Invalid device may be supplied */
if (NULL == device) { if (EXIT_SUCCESS != hcd_check_device(device)) {
USB_MSG("No device available for event: 0x%02X, value: 0x%02X", USB_MSG("No device available for event: 0x%02X, value: 0x%02X",
event, val); event, val);
return; return;
} }
#ifdef HCD_DUMP_DEVICE_TREE
/* This can be unlocked to dump current USB device tree on event */
{
/* Go to the base of USB device tree and
* print the current state of it */
hcd_device_state * base;
base = device;
while (NULL != base->parent)
base = base->parent;
USB_MSG("Current state of USB device tree:");
hcd_dump_tree(base, 0);
}
#endif
/* Handle event and forward control to device thread when required */ /* Handle event and forward control to device thread when required */
switch (event) { switch (event) {
case HCD_EVENT_CONNECTED: case HCD_EVENT_CONNECTED:
@ -95,13 +115,9 @@ hcd_handle_event(hcd_device_state * device, hcd_event event, hcd_reg1 val)
"Device is marked as 'disconnected' " "Device is marked as 'disconnected' "
"for 'disconnection' event"); "for 'disconnection' event");
/* If connect callback was used before, call /* Make this device and all attached children
* it's equivalent to signal disconnection */ * disconnect recursively */
if (HCD_STATE_CONNECTED == device->state) hcd_disconnect_tree(device);
hcd_disconnect_cb(device);
/* Handle device disconnection (freeing memory etc.) */
hcd_disconnect_device(device);
break; break;
@ -143,10 +159,11 @@ hcd_handle_event(hcd_device_state * device, hcd_event event, hcd_reg1 val)
"Device is marked as 'disconnected' " "Device is marked as 'disconnected' "
"for 'hub port detach' event"); "for 'hub port detach' event");
hcd_delete_child(device, val);
USB_MSG("Device disconnected from " USB_MSG("Device disconnected from "
"hub 0x%08X, port %u", device, val); "hub 0x%08X, port %u", device, val);
hcd_delete_child(device, val);
break; break;
case HCD_EVENT_ENDPOINT: case HCD_EVENT_ENDPOINT:
@ -260,9 +277,6 @@ hcd_device_thread(void * thread_args)
/* Block and wait for something like 'submit URB' */ /* Block and wait for something like 'submit URB' */
hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL); hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL);
hcd_handle_urb(this_device); hcd_handle_urb(this_device);
/* Only external URBs should be submitted here */
hcd_completion_cb(this_device->urb);
} }
/* Finish device handling to avoid leaving thread */ /* Finish device handling to avoid leaving thread */
@ -294,40 +308,48 @@ hcd_device_finish(hcd_device_state * this_device, const char * finish_msg)
static hcd_device_state * static hcd_device_state *
hcd_get_child_for_ep(hcd_device_state * device, hcd_reg1 ep) hcd_get_child_for_ep(hcd_device_state * device, hcd_reg1 ep)
{ {
hcd_device_state * d; hcd_device_state * child_found;
hcd_device_state * found; hcd_device_state * final_found;
int child_num; hcd_device_state * child;
hcd_reg1 child_num;
DEBUG_DUMP; DEBUG_DUMP;
/* Start with parent */ /* Nothing yet */
d = device; final_found = NULL;
child_num = 0;
/* Nothing found yet */ /* Check if any children (and their children) wait for EP event */
found = NULL; /* Every device in tree is checked every time so errors can be found */
for (child_num = 0; child_num < HCD_CHILDREN; child_num++) {
/* Device, to be checked for EP event recursively... */
child = device->child[child_num];
/* When device has multiple children (hub), this allows /* ...but only if attached */
* routing interrupt login downstream */ if (NULL != child) {
do { /* Look deeper first */
/* Must exist and match requirements */ child_found = hcd_get_child_for_ep(child, ep);
if ((NULL != d) &&
(HCD_EVENT_ENDPOINT == d->wait_event) &&
(ep == d->wait_ep)) {
/* Waiting for EP by multiple devices is mutually if (NULL != child_found) {
* exclusive, as long as scheduler works correctly */ /* Only one device can wait for EP event */
USB_ASSERT(NULL == found, "More than one device waits " USB_ASSERT((NULL == final_found),
"for given endpoint interrupt"); "More than one device waits for EP");
found = d; /* Remember what was found */
final_found = child_found;
}
}
} }
/* Check next child */ /* Check this device last */
d = device->child[child_num++]; if ((HCD_EVENT_ENDPOINT == device->wait_event) &&
(ep == device->wait_ep)) {
/* Only one device can wait for EP event */
USB_ASSERT((NULL == final_found),
"More than one device waits for EP");
/* Remember what was found */
final_found = device;
}
} while(child_num <= HCD_CHILDREN); return final_found;
return found;
} }
@ -365,27 +387,85 @@ hcd_add_child(hcd_device_state * parent, hcd_reg1 port, hcd_speed speed)
static void static void
hcd_delete_child(hcd_device_state * parent, hcd_reg1 port) hcd_delete_child(hcd_device_state * parent, hcd_reg1 port)
{ {
hcd_device_state * child;
DEBUG_DUMP; DEBUG_DUMP;
USB_ASSERT(port < HCD_CHILDREN, "Port number too high"); 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 child = parent->child[port]; /* Child to be detached */
* it's equivalent to signal disconnection */
if (HCD_STATE_CONNECTED == parent->child[port]->state)
hcd_disconnect_cb(parent->child[port]);
/* Disconnect to release fields */ USB_ASSERT(NULL != child, "Child device does not exist");
hcd_disconnect_device(parent->child[port]);
/* Make this child device and all its attached children
* disconnect recursively */
hcd_disconnect_tree(child);
/* Delete to release device itself */ /* Delete to release device itself */
hcd_delete_device(parent->child[port]); hcd_delete_device(child);
/* Mark as released */ /* Mark as released */
parent->child[port] = NULL; parent->child[port] = NULL;
} }
/*===========================================================================*
* hcd_disconnect_tree *
*===========================================================================*/
static void
hcd_disconnect_tree(hcd_device_state * device)
{
hcd_reg1 child_num;
DEBUG_DUMP;
/* Generate disconnect event for all children */
for (child_num = 0; child_num < HCD_CHILDREN; child_num++) {
if (NULL != device->child[child_num])
hcd_handle_event(device, HCD_EVENT_PORT_DISCONNECTED,
child_num);
}
/* If this device was detached during URB handling, some steps must be
* taken to ensure that no process/thread is waiting for completion */
if (NULL != device->urb) {
USB_MSG("Unplugged device had unhandled URB");
/* Tell device driver that device was detached */
/* TODO: ENODEV selected for that */
device->urb->inout_status = ENODEV;
hcd_complete_urb(device);
}
/* If connect callback was used before, call
* it's equivalent to signal disconnection */
if (HCD_STATE_CONNECTED == device->state)
hcd_disconnect_cb(device);
/* Handle device disconnection (freeing memory etc.) */
hcd_disconnect_device(device);
}
/*===========================================================================*
* hcd_dump_tree *
*===========================================================================*/
static void
hcd_dump_tree(hcd_device_state * device, hcd_reg1 level)
{
hcd_reg1 child_num;
/* DEBUG_DUMP; */ /* Let's keep tree output cleaner */
USB_MSG("Device on level %03u: 0x%08X", level, device);
/* Traverse device tree recursively */
for (child_num = 0; child_num < HCD_CHILDREN; child_num++) {
if (NULL != device->child[child_num])
hcd_dump_tree(device->child[child_num], level + 1);
}
}
/*===========================================================================* /*===========================================================================*
* hcd_enumerate * * hcd_enumerate *
*===========================================================================*/ *===========================================================================*/
@ -803,10 +883,27 @@ hcd_handle_urb(hcd_device_state * this_device)
} else } else
USB_MSG("Invalid URB supplied"); USB_MSG("Invalid URB supplied");
/* Perform completion routine */
hcd_complete_urb(this_device);
}
/*===========================================================================*
* hcd_complete_urb *
*===========================================================================*/
static void
hcd_complete_urb(hcd_device_state * this_device)
{
DEBUG_DUMP;
/* Signal scheduler that URB was handled */ /* Signal scheduler that URB was handled */
/* TODO: This works based on the fact that device thread has higher this_device->urb->handled(this_device->urb);
* priority than scheduler and won't change context within this call */
urb->handled(urb); /* Use this callback in case it is an external URB */
hcd_completion_cb(this_device->urb);
/* Make device forget about this URB */
this_device->urb = NULL;
} }
@ -916,7 +1013,14 @@ hcd_non_control_urb(hcd_device_state * this_device, hcd_urb * urb)
request.direction = urb->direction; request.direction = urb->direction;
request.data_left = (int)urb->in_size; request.data_left = (int)urb->in_size;
request.data = urb->inout_data; request.data = urb->inout_data;
/* TODO: This was changed to allow software scheduler to work correctly
* by switching URBs when they NAK, rather than waiting forever if URB
* which requires such waiting, was issued */
#if 0
request.interval = urb->interval; request.interval = urb->interval;
#else
request.interval = HCD_DEFAULT_NAKLIMIT;
#endif
/* Assign to let know how much data can be transfered at a time */ /* Assign to let know how much data can be transfered at a time */
request.max_packet_size = UGETW(e->descriptor.wMaxPacketSize); request.max_packet_size = UGETW(e->descriptor.wMaxPacketSize);
@ -967,7 +1071,8 @@ hcd_setup_packet(hcd_device_state * this_device, hcd_ctrlrequest * setup,
this_device->control_len = 0; /* Nothing read yet */ this_device->control_len = 0; /* Nothing read yet */
/* Set parameters for further communication */ /* Set parameters for further communication */
d->setup_device(d->private_data, ep, this_device->current_address); d->setup_device(d->private_data, ep, this_device->current_address,
NULL, NULL);
/* Send setup packet */ /* Send setup packet */
d->setup_stage(d->private_data, setup); d->setup_stage(d->private_data, setup);
@ -1130,7 +1235,9 @@ hcd_data_transfer(hcd_device_state * this_device, hcd_datarequest * request)
/* Set parameters for further communication */ /* Set parameters for further communication */
d->setup_device(d->private_data, request->endpoint, d->setup_device(d->private_data, request->endpoint,
this_device->current_address); this_device->current_address,
&(this_device->ep_tx_tog[request->endpoint]),
&(this_device->ep_rx_tog[request->endpoint]));
/* Check transfer direction first */ /* Check transfer direction first */
if (HCD_DIRECTION_IN == request->direction) { if (HCD_DIRECTION_IN == request->direction) {

View File

@ -31,6 +31,13 @@ static hcd_reg1 hcd_reserve_addr(hcd_driver_state *);
static void hcd_release_addr(hcd_driver_state *, hcd_reg1); static void hcd_release_addr(hcd_driver_state *, hcd_reg1);
/*===========================================================================*
* Local definitions *
*===========================================================================*/
/* List of all allocated devices */
static hcd_device_state * dev_list = NULL;
/*===========================================================================* /*===========================================================================*
* hcd_os_interrupt_attach * * hcd_os_interrupt_attach *
*===========================================================================*/ *===========================================================================*/
@ -282,7 +289,7 @@ hcd_device_wait(hcd_device_state * device, hcd_event event, hcd_reg1 ep)
{ {
DEBUG_DUMP; DEBUG_DUMP;
USB_DBG("Device 0x%08X wait (0x%02X, 0x%02X)", device, event, ep); USB_DBG("0x%08X wait (0x%02X, 0x%02X)", device, event, ep);
device->wait_event = event; device->wait_event = event;
device->wait_ep = ep; device->wait_ep = ep;
@ -299,7 +306,7 @@ hcd_device_continue(hcd_device_state * device, hcd_event event, hcd_reg1 ep)
{ {
DEBUG_DUMP; DEBUG_DUMP;
USB_DBG("Device 0x%08X continue (0x%02X, 0x%02X)", device, event, ep); USB_DBG("0x%08X continue (0x%02X, 0x%02X)", device, event, ep);
USB_ASSERT(device->wait_event == event, "Unexpected event"); USB_ASSERT(device->wait_event == event, "Unexpected event");
USB_ASSERT(device->wait_ep == ep, "Unexpected endpoint"); USB_ASSERT(device->wait_ep == ep, "Unexpected endpoint");
@ -323,6 +330,18 @@ hcd_new_device(void)
USB_ASSERT(NULL != d, "Failed to allocate device"); USB_ASSERT(NULL != d, "Failed to allocate device");
if (NULL == dev_list) {
dev_list = d;
} else {
d->_next = dev_list;
dev_list = d;
}
#ifdef HCD_DUMP_DEVICE_LIST
/* Dump updated state of device list */
hcd_dump_devices();
#endif
return d; return d;
} }
@ -333,9 +352,78 @@ hcd_new_device(void)
void void
hcd_delete_device(hcd_device_state * d) hcd_delete_device(hcd_device_state * d)
{ {
hcd_device_state * temp;
DEBUG_DUMP; DEBUG_DUMP;
if (d == dev_list) {
dev_list = dev_list->_next;
} else {
temp = dev_list;
/* Find the device and ... */
while (temp->_next != d) {
USB_ASSERT(NULL != temp->_next,
"Invalid state of device list");
temp = temp->_next;
}
/* ...make device list forget about it */
temp->_next = temp->_next->_next;
}
free(d); free(d);
#ifdef HCD_DUMP_DEVICE_LIST
/* Dump updated state of device list */
hcd_dump_devices();
#endif
}
/*===========================================================================*
* hcd_dump_devices *
*===========================================================================*/
void
hcd_dump_devices(void)
{
hcd_device_state * temp;
DEBUG_DUMP;
temp = dev_list;
USB_MSG("Allocated devices:");
while (NULL != temp) {
USB_MSG("0x%08X", (int)temp);
temp = temp->_next;
}
}
/*===========================================================================*
* hcd_check_device *
*===========================================================================*/
int
hcd_check_device(hcd_device_state * d)
{
hcd_device_state * temp;
DEBUG_DUMP;
temp = dev_list;
/* Traverse the list of allocated devices
* to determine validity of this one */
while (NULL != temp) {
if (temp == d)
return EXIT_SUCCESS; /* Device found within the list */
temp = temp->_next;
}
/* Device was not found, may have been removed earlier */
return EXIT_FAILURE;
} }

View File

@ -308,8 +308,9 @@ hcd_completion_cb(hcd_urb * urb)
/* Recollect original URB */ /* Recollect original URB */
d_urb = (struct ddekit_usb_urb *)urb->original_urb; d_urb = (struct ddekit_usb_urb *)urb->original_urb;
USB_ASSERT(NULL != d_urb, "Original DDEKit URB missing"); /* Original URB will not be NULL if URB
* was external (from device driver) */
if (NULL != d_urb) {
/* Turn HCD URB format to one handled by DDEKit */ /* Turn HCD URB format to one handled by DDEKit */
hcd_encode_urb(urb, d_urb); hcd_encode_urb(urb, d_urb);
@ -317,6 +318,7 @@ hcd_completion_cb(hcd_urb * urb)
hcd_free_urb(urb); hcd_free_urb(urb);
completion_cb(d_urb->priv); completion_cb(d_urb->priv);
}
} }

View File

@ -68,7 +68,8 @@ usbd_init_scheduler(void)
num_stored_urbs = 0; num_stored_urbs = 0;
memset(stored_urb, 0, sizeof(stored_urb)); memset(stored_urb, 0, sizeof(stored_urb));
urb_thread = ddekit_thread_create(hcd_urb_scheduler_task, NULL, "urb"); urb_thread = ddekit_thread_create(hcd_urb_scheduler_task, NULL,
"scheduler");
if (NULL == urb_thread) if (NULL == urb_thread)
goto ERR1; goto ERR1;
@ -170,6 +171,7 @@ hcd_unschedule_urb(hcd_urb * urb)
static void static void
hcd_urb_scheduler_task(void * UNUSED(arg)) hcd_urb_scheduler_task(void * UNUSED(arg))
{ {
hcd_device_state * current_device;
hcd_urb * current_urb; hcd_urb * current_urb;
DEBUG_DUMP; DEBUG_DUMP;
@ -181,18 +183,37 @@ hcd_urb_scheduler_task(void * UNUSED(arg))
/* Get URB */ /* Get URB */
current_urb = hcd_get_urb(); current_urb = hcd_get_urb();
/* Get URB's target device */
current_device = current_urb->target_device;
/* Check for mismatch */ /* Check for mismatch */
USB_ASSERT(NULL != current_urb, "URB missing after URB unlock"); USB_ASSERT(NULL != current_urb, "URB missing after URB unlock");
/* Check if URB's device is still allocated */
if (EXIT_SUCCESS == hcd_check_device(current_device)) {
/* Tell device that this is its URB */ /* Tell device that this is its URB */
current_urb->target_device->urb = current_urb; current_device->urb = current_urb;
/* Start handling URB event */ /* Start handling URB event */
hcd_handle_event(current_urb->target_device, hcd_handle_event(current_device, HCD_EVENT_URB,
HCD_EVENT_URB, HCD_UNUSED_VAL); HCD_UNUSED_VAL);
/* Wait for completion */ /* Wait for completion */
ddekit_sem_down(handled_lock); ddekit_sem_down(handled_lock);
/* TODO: Not enough DDEKit thread priorities
* for a better solution */
/* Yield, to allow unlocking thread, to continue
* before next URB is used */
ddekit_yield();
/* Makes thread debugging easier */
USB_DBG("URB handled, scheduler unlocked");
} else {
USB_MSG("Device 0x%08X for URB 0x%08X, is unavailable",
(int)current_device,
(int)current_urb);
}
} }
} }
@ -206,8 +227,6 @@ hcd_urb_handled(hcd_urb * urb)
DEBUG_DUMP; DEBUG_DUMP;
/* This URB will be scheduled no more */ /* This URB will be scheduled no more */
/* TODO: It would be better if this was connected
* to setting urb_lock down */
hcd_unschedule_urb(urb); hcd_unschedule_urb(urb);
/* Handling completed */ /* Handling completed */

View File

@ -345,13 +345,16 @@ musb_core_stop(void * cfg)
* musb_setup_device * * musb_setup_device *
*===========================================================================*/ *===========================================================================*/
void void
musb_setup_device(void * cfg, hcd_reg1 ep, hcd_reg1 addr) musb_setup_device(void * cfg, hcd_reg1 ep, hcd_reg1 addr,
hcd_datatog * tx_tog, hcd_datatog * rx_tog)
{ {
DEBUG_DUMP; DEBUG_DUMP;
/* Assign */ /* Assign */
((musb_core_config *)cfg)->ep = ep; ((musb_core_config *)cfg)->ep = ep;
((musb_core_config *)cfg)->addr = addr; ((musb_core_config *)cfg)->addr = addr;
((musb_core_config *)cfg)->datatog_tx = tx_tog;
((musb_core_config *)cfg)->datatog_rx = rx_tog;
} }
@ -372,7 +375,7 @@ musb_reset_device(void * cfg, hcd_speed * speed)
r = core->regs; r = core->regs;
/* Set initial parameters */ /* Set initial parameters */
musb_setup_device(core, HCD_DEFAULT_EP, HCD_DEFAULT_ADDR); musb_setup_device(core, HCD_DEFAULT_EP, HCD_DEFAULT_ADDR, NULL, NULL);
/* Set EP and device address to be used in this command */ /* Set EP and device address to be used in this command */
musb_set_state(core); musb_set_state(core);
@ -534,16 +537,16 @@ musb_rx_stage(void * cfg, hcd_datarequest * request)
/* Make controller reconfigure */ /* Make controller reconfigure */
host_rxcsr = HCD_RD2(r, MUSB_REG_HOST_RXCSR); host_rxcsr = HCD_RD2(r, MUSB_REG_HOST_RXCSR);
if (MUSB_DATATOG_UNKNOWN == core->datatog_rx[core->ep]) { HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_DATATOGWREN); /* Enable first */
/* Reset DATA toggle on first transfer */
HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_CLRDATATOG);
core->datatog_rx[core->ep] = MUSB_DATATOG_INIT;
}
HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_FLUSHFIFO); HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_FLUSHFIFO);
HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_rxcsr); HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_rxcsr);
/* Request packet */ /* Set data toggle and start receiving */
host_rxcsr = HCD_RD2(r, MUSB_REG_HOST_RXCSR); host_rxcsr = HCD_RD2(r, MUSB_REG_HOST_RXCSR);
if (HCD_DATATOG_DATA0 == *(core->datatog_rx))
HCD_CLR(host_rxcsr, MUSB_VAL_HOST_RXCSR_DATATOG);
else
HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_DATATOG);
HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_REQPKT); HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_REQPKT);
HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_rxcsr); HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_rxcsr);
} }
@ -630,19 +633,20 @@ musb_tx_stage(void * cfg, hcd_datarequest * request)
HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_MODE); HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_MODE);
HCD_CLR(host_txcsr, MUSB_VAL_HOST_TXCSR_ISO); HCD_CLR(host_txcsr, MUSB_VAL_HOST_TXCSR_ISO);
HCD_CLR(host_txcsr, MUSB_VAL_HOST_TXCSR_AUTOSET); HCD_CLR(host_txcsr, MUSB_VAL_HOST_TXCSR_AUTOSET);
if (MUSB_DATATOG_UNKNOWN == core->datatog_tx[core->ep]) { HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_DATATOGWREN); /* Enable first */
/* Reset DATA toggle on first transfer */ /* TODO: May have no effect */
HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_CLRDATATOG);
core->datatog_tx[core->ep] = MUSB_DATATOG_INIT;
}
HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_FLUSHFIFO); HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_FLUSHFIFO);
HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_txcsr); HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_txcsr);
/* Put data in FIFO */ /* Put data in FIFO */
musb_write_fifo(cfg, request->data, request->data_left, core->ep); musb_write_fifo(cfg, request->data, request->data_left, core->ep);
/* Request packet */ /* Set data toggle and start transmitting */
host_txcsr = HCD_RD2(r, MUSB_REG_HOST_TXCSR); host_txcsr = HCD_RD2(r, MUSB_REG_HOST_TXCSR);
if (HCD_DATATOG_DATA0 == *(core->datatog_tx))
HCD_CLR(host_txcsr, MUSB_VAL_HOST_TXCSR_DATATOG);
else
HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_DATATOG);
HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_TXPKTRDY); HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_TXPKTRDY);
HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_txcsr); HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_txcsr);
} }
@ -790,6 +794,7 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir)
} }
musb_error_case; musb_error_case;
musb_core_config * core;
void * r; void * r;
hcd_reg2 host_csr; hcd_reg2 host_csr;
musb_error_case error_case; musb_error_case error_case;
@ -800,7 +805,8 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir)
USB_ASSERT(HCD_TRANSFER_ISOCHRONOUS != xfer, USB_ASSERT(HCD_TRANSFER_ISOCHRONOUS != xfer,
"ISO transfer not supported"); "ISO transfer not supported");
r = ((musb_core_config *)cfg)->regs; core = (musb_core_config *)cfg;
r = core->regs;
/* Set EP and device address to be used in this command */ /* Set EP and device address to be used in this command */
musb_set_state((musb_core_config *)cfg); musb_set_state((musb_core_config *)cfg);
@ -859,6 +865,13 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir)
/* Get TX status register */ /* Get TX status register */
host_csr = HCD_RD2(r, MUSB_REG_HOST_TXCSR); host_csr = HCD_RD2(r, MUSB_REG_HOST_TXCSR);
/* Check for completion */
if (!(host_csr & MUSB_VAL_HOST_TXCSR_TXPKTRDY)) {
/* ACK received update data toggle */
*(core->datatog_tx) ^= HCD_DATATOG_DATA1;
return EXIT_SUCCESS;
}
/* Check for common errors */ /* Check for common errors */
if (host_csr & MUSB_VAL_HOST_TXCSR_ERROR) { if (host_csr & MUSB_VAL_HOST_TXCSR_ERROR) {
USB_MSG("HOST_TXCSR ERROR: %04X", host_csr); USB_MSG("HOST_TXCSR ERROR: %04X", host_csr);
@ -876,18 +889,30 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir)
if (host_csr & MUSB_VAL_HOST_TXCSR_NAK_TIMEOUT) { if (host_csr & MUSB_VAL_HOST_TXCSR_NAK_TIMEOUT) {
USB_MSG("HOST_TXCSR NAK_TIMEOUT: %04X", host_csr); USB_MSG("HOST_TXCSR NAK_TIMEOUT: %04X", host_csr);
/* Flush FIFO before clearing NAKTIMEOUT
* to abort transfer */
HCD_SET(host_csr, MUSB_VAL_HOST_TXCSR_FLUSHFIFO);
HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_csr);
host_csr = HCD_RD2(r, MUSB_REG_HOST_TXCSR);
HCD_CLR(host_csr, MUSB_VAL_HOST_TXCSR_NAK_TIMEOUT); HCD_CLR(host_csr, MUSB_VAL_HOST_TXCSR_NAK_TIMEOUT);
HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_csr); HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_csr);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
return EXIT_SUCCESS; USB_ASSERT(0, "Invalid state of HOST_TXCSR");
} }
if (MUSB_IN_ERROR_CASE == error_case) { if (MUSB_IN_ERROR_CASE == error_case) {
/* Get RX status register */ /* Get RX status register */
host_csr = HCD_RD2(r, MUSB_REG_HOST_RXCSR); host_csr = HCD_RD2(r, MUSB_REG_HOST_RXCSR);
/* Check for completion */
if (host_csr & MUSB_VAL_HOST_RXCSR_RXPKTRDY) {
/* ACK received update data toggle */
*(core->datatog_rx) ^= HCD_DATATOG_DATA1;
return EXIT_SUCCESS;
}
/* Check for common errors */ /* Check for common errors */
if (host_csr & MUSB_VAL_HOST_RXCSR_ERROR) { if (host_csr & MUSB_VAL_HOST_RXCSR_ERROR) {
USB_MSG("HOST_RXCSR ERROR: %04X", host_csr); USB_MSG("HOST_RXCSR ERROR: %04X", host_csr);
@ -905,12 +930,16 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir)
if (host_csr & MUSB_VAL_HOST_RXCSR_NAKTIMEOUT) { if (host_csr & MUSB_VAL_HOST_RXCSR_NAKTIMEOUT) {
USB_MSG("HOST_RXCSR NAK_TIMEOUT: %04X", host_csr); USB_MSG("HOST_RXCSR NAK_TIMEOUT: %04X", host_csr);
/* Clear REQPKT before NAKTIMEOUT to abort transfer */
HCD_CLR(host_csr, MUSB_VAL_HOST_RXCSR_REQPKT);
HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_csr);
host_csr = HCD_RD2(r, MUSB_REG_HOST_RXCSR);
HCD_CLR(host_csr, MUSB_VAL_HOST_RXCSR_NAKTIMEOUT); HCD_CLR(host_csr, MUSB_VAL_HOST_RXCSR_NAKTIMEOUT);
HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_csr); HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_csr);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
return EXIT_SUCCESS; USB_ASSERT(0, "Invalid state of HOST_RXCSR");
} }
USB_MSG("Invalid USB transfer error check: 0x%X, 0x%X, 0x%X", USB_MSG("Invalid USB transfer error check: 0x%X, 0x%X, 0x%X",

View File

@ -11,15 +11,6 @@
/*===========================================================================* /*===========================================================================*
* Types and constants * * Types and constants *
*===========================================================================*/ *===========================================================================*/
/* Holds info on DATA toggle (DATA0/DATA1) initialization,
* required by bulk transfers */
typedef enum {
MUSB_DATATOG_UNKNOWN = 0, /* Default with memset 0 */
MUSB_DATATOG_INIT
}
musb_datatog;
/* Structure to hold Mentor USB core configuration /* Structure to hold Mentor USB core configuration
* May be more than one on a single chip * May be more than one on a single chip
* Should be initialized by MUSB's variant specific code (like AM335x) */ * Should be initialized by MUSB's variant specific code (like AM335x) */
@ -28,8 +19,8 @@ typedef struct {
void * regs; /* Points to beginning of memory mapped registers */ void * regs; /* Points to beginning of memory mapped registers */
hcd_reg1 ep; /* Currently used endpoint */ hcd_reg1 ep; /* Currently used endpoint */
hcd_reg1 addr; /* Currently used address */ hcd_reg1 addr; /* Currently used address */
musb_datatog datatog_tx[HCD_TOTAL_EP]; hcd_datatog * datatog_tx; /* Should point at currently used TX toggle */
musb_datatog datatog_rx[HCD_TOTAL_EP]; hcd_datatog * datatog_rx; /* Should point at currently used RX toggle */
} }
musb_core_config; musb_core_config;
@ -43,7 +34,8 @@ void musb_core_stop(void *);
/* For HCD interface */ /* For HCD interface */
void musb_setup_device(void *, hcd_reg1, hcd_reg1); void musb_setup_device(void *, hcd_reg1, hcd_reg1,
hcd_datatog *, hcd_datatog *);
int musb_reset_device(void *, hcd_speed *); int musb_reset_device(void *, hcd_speed *);
void musb_setup_stage(void *, hcd_ctrlrequest *); void musb_setup_stage(void *, hcd_ctrlrequest *);
void musb_rx_stage(void *, hcd_datarequest *); void musb_rx_stage(void *, hcd_datarequest *);

View File

@ -154,6 +154,14 @@ typedef enum {
} }
hcd_speed; hcd_speed;
/* Possible data toggle values (at least for bulk transfer) */
typedef enum {
HCD_DATATOG_DATA0 = 0,
HCD_DATATOG_DATA1 = 1
}
hcd_datatog;
/*===========================================================================* /*===========================================================================*
* HCD threading/device/URB types * * HCD threading/device/URB types *
@ -166,12 +174,15 @@ typedef struct usb_ctrlrequest hcd_ctrlrequest;
/* Largest value that can be transfered by this driver at a time /* Largest value that can be transfered by this driver at a time
* see MAXPAYLOAD in TXMAXP/RXMAXP */ * see MAXPAYLOAD in TXMAXP/RXMAXP */
#define MAX_WTOTALLENGTH 1024 #define MAX_WTOTALLENGTH 1024u
/* TODO: This has corresponding redefinition in hub driver */ /* TODO: This has corresponding redefinition in hub driver */
/* Limit of child devices for each parent */ /* Limit of child devices for each parent */
#define HCD_CHILDREN 8u #define HCD_CHILDREN 8u
/* Total number of endpoints available in USB 2.0 */
#define HCD_TOTAL_EP 16u
/* Forward declarations */ /* Forward declarations */
typedef struct hcd_datarequest hcd_datarequest; typedef struct hcd_datarequest hcd_datarequest;
typedef struct hcd_urb hcd_urb; typedef struct hcd_urb hcd_urb;
@ -219,6 +230,7 @@ struct hcd_device_state {
hcd_device_state * parent; /* In case of hub attachment */ hcd_device_state * parent; /* In case of hub attachment */
hcd_device_state * child[HCD_CHILDREN]; /* In case of being hub */ hcd_device_state * child[HCD_CHILDREN]; /* In case of being hub */
hcd_device_state * _next; /* To allow device lists */
hcd_driver_state * driver; /* Specific HCD driver object */ hcd_driver_state * driver; /* Specific HCD driver object */
hcd_thread * thread; hcd_thread * thread;
hcd_lock * lock; hcd_lock * lock;
@ -234,6 +246,8 @@ struct hcd_device_state {
hcd_state state; hcd_state state;
hcd_reg1 reserved_address; hcd_reg1 reserved_address;
hcd_reg1 current_address; hcd_reg1 current_address;
hcd_datatog ep_tx_tog[HCD_TOTAL_EP];
hcd_datatog ep_rx_tog[HCD_TOTAL_EP];
/* /*
* Control transfer's local data: * Control transfer's local data:
@ -266,8 +280,9 @@ struct hcd_device_state {
#define HCD_LAST_ADDR 0x7Fu #define HCD_LAST_ADDR 0x7Fu
#define HCD_TOTAL_ADDR 0x80u #define HCD_TOTAL_ADDR 0x80u
#define HCD_LAST_EP 0x0Fu #define HCD_LAST_EP 0x0Fu
#define HCD_TOTAL_EP 0x10u
#define HCD_UNUSED_VAL 0xFFu /* When number not needed */ #define HCD_UNUSED_VAL 0xFFu /* When number not needed */
#define HCD_DEFAULT_NAKLIMIT 0x10u
/* Legal interval values */ /* Legal interval values */
#define HCD_LOWEST_INTERVAL 0x00u #define HCD_LOWEST_INTERVAL 0x00u
@ -332,6 +347,8 @@ void hcd_device_continue(hcd_device_state *, hcd_event, hcd_reg1);
/* Allocation/deallocation of device structures */ /* Allocation/deallocation of device structures */
hcd_device_state * hcd_new_device(void); hcd_device_state * hcd_new_device(void);
void hcd_delete_device(hcd_device_state *); void hcd_delete_device(hcd_device_state *);
void hcd_dump_devices(void);
int hcd_check_device(hcd_device_state *);
/*===========================================================================* /*===========================================================================*

View File

@ -32,7 +32,8 @@ hcd_addr_state;
*===========================================================================*/ *===========================================================================*/
struct hcd_driver_state { struct hcd_driver_state {
/* Standard USB controller procedures */ /* Standard USB controller procedures */
void (*setup_device) (void *, hcd_reg1, hcd_reg1); void (*setup_device) (void *, hcd_reg1, hcd_reg1,
hcd_datatog *, hcd_datatog *);
int (*reset_device) (void *, hcd_speed *); int (*reset_device) (void *, hcd_speed *);
void (*setup_stage) (void *, hcd_ctrlrequest *); void (*setup_stage) (void *, hcd_ctrlrequest *);
void (*rx_stage) (void *, hcd_datarequest *); void (*rx_stage) (void *, hcd_datarequest *);

View File

@ -16,6 +16,13 @@
#define DEBUG #define DEBUG
#endif #endif
/* This allows us to analyze thread context in
* consecutive function calls (DEBUG_DUMP) */
#include <ddekit/thread.h>
/* Represents current thread's name string */
#define HCD_THREAD_NAME ddekit_thread_get_name(ddekit_thread_myself())
/*===========================================================================* /*===========================================================================*
* Standard output message * * Standard output message *
@ -34,12 +41,12 @@
#ifdef DEBUG #ifdef DEBUG
#define DEBUG_DUMP \ #define DEBUG_DUMP \
do { \ do { \
printf("USBD (DEBUG %s)\n", __func__); \ printf("USBD: [%s -> %s]\n", HCD_THREAD_NAME, __func__);\
} while(0) } while(0)
#define USB_DBG(fmt, ...) \ #define USB_DBG(fmt, ...) \
do { \ do { \
printf("USBD (DEBUG %s): ", __func__); \ printf("USBD: [%s -> %s] ", HCD_THREAD_NAME, __func__); \
printf(fmt, ##__VA_ARGS__); \ printf(fmt, ##__VA_ARGS__); \
printf("\n"); \ printf("\n"); \
} while(0) } while(0)
@ -55,9 +62,10 @@
*===========================================================================*/ *===========================================================================*/
#define USB_ASSERT(cond, otherwise) \ #define USB_ASSERT(cond, otherwise) \
do { \ do { \
if(!(cond)) { \ if (!(cond)) { \
USB_MSG("ASSERTION ERROR (%s:%d) - " \ USB_MSG("ASSERTION ERROR (%s -> %s:%d) - " \
otherwise, __func__, __LINE__); \ otherwise, HCD_THREAD_NAME, \
__func__, __LINE__); \
exit(EXIT_FAILURE); \ exit(EXIT_FAILURE); \
} \ } \
} while(0) } while(0)

View File

@ -288,13 +288,13 @@ void _ddekit_interrupt_trigger(int irq_id)
irq_s = find_by_irq_id(irq_id); irq_s = find_by_irq_id(irq_id);
if (irq_s) { if (irq_s) {
DDEBUG_MSG_VERBOSE("Triggering IRQ %d", irq); DDEBUG_MSG_VERBOSE("Triggering IRQ %d", irq_s->irq);
ddekit_sem_up(irq_s->sem); ddekit_sem_up(irq_s->sem);
if (0 != (err_code = sys_irqenable(&irq_s->irq_hook))) if (0 != (err_code = sys_irqenable(&irq_s->irq_hook)))
ddekit_panic("Failed to enable interrupt " ddekit_panic("Failed to enable interrupt "
"(ERROR %d)", err_code); "(ERROR %d)", err_code);
} else { } else {
DDEBUG_MSG_WARN("no handler for IRQ %d", irq); DDEBUG_MSG_WARN("no handler for IRQ %d", irq_s->irq);
} }
} }