306 lines
7.6 KiB
C
306 lines
7.6 KiB
C
/*
|
|
* Implementation of HCD URB scheduler
|
|
*/
|
|
|
|
#include <string.h> /* memset */
|
|
|
|
#include <usbd/hcd_common.h>
|
|
#include <usbd/hcd_ddekit.h>
|
|
#include <usbd/hcd_interface.h>
|
|
#include <usbd/hcd_schedule.h>
|
|
#include <usbd/usbd_common.h>
|
|
#include <usbd/usbd_schedule.h>
|
|
|
|
|
|
/*===========================================================================*
|
|
* 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;
|
|
|
|
/* This allows waiting for URB */
|
|
static hcd_lock * urb_lock;
|
|
|
|
/* This allows waiting for completion */
|
|
static hcd_lock * handled_lock;
|
|
|
|
/* Makes URB schedule enabled */
|
|
static int hcd_schedule_urb(hcd_urb *);
|
|
|
|
/* Makes URB schedule disabled */
|
|
static void hcd_unschedule_urb(hcd_urb *);
|
|
|
|
/* Scheduler task */
|
|
static void hcd_urb_scheduler_task(void *);
|
|
|
|
/* Completion callback */
|
|
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);
|
|
|
|
|
|
/*===========================================================================*
|
|
* usbd_init_scheduler *
|
|
*===========================================================================*/
|
|
int
|
|
usbd_init_scheduler(void)
|
|
{
|
|
DEBUG_DUMP;
|
|
|
|
/* Reset everything */
|
|
num_stored_urbs = 0;
|
|
memset(stored_urb, 0, sizeof(stored_urb));
|
|
|
|
urb_thread = ddekit_thread_create(hcd_urb_scheduler_task, NULL,
|
|
"scheduler");
|
|
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 ERR3;
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
ERR3:
|
|
ddekit_sem_deinit(urb_lock);
|
|
ERR2:
|
|
ddekit_thread_terminate(urb_thread);
|
|
ERR1:
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* usbd_deinit_scheduler *
|
|
*===========================================================================*/
|
|
void
|
|
usbd_deinit_scheduler(void)
|
|
{
|
|
DEBUG_DUMP;
|
|
|
|
ddekit_sem_deinit(handled_lock);
|
|
|
|
ddekit_sem_deinit(urb_lock);
|
|
|
|
ddekit_thread_terminate(urb_thread);
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* hcd_schedule_external_urb *
|
|
*===========================================================================*/
|
|
int
|
|
hcd_schedule_external_urb(hcd_urb * urb)
|
|
{
|
|
DEBUG_DUMP;
|
|
|
|
return hcd_schedule_urb(urb);
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* hcd_schedule_internal_urb *
|
|
*===========================================================================*/
|
|
int
|
|
hcd_schedule_internal_urb(hcd_urb * urb)
|
|
{
|
|
DEBUG_DUMP;
|
|
|
|
return hcd_schedule_urb(urb);
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* hcd_schedule_urb *
|
|
*===========================================================================*/
|
|
static int
|
|
hcd_schedule_urb(hcd_urb * urb)
|
|
{
|
|
DEBUG_DUMP;
|
|
|
|
/* Tell URB what to call on completion */
|
|
urb->handled = hcd_urb_handled;
|
|
|
|
/* Store and check if scheduler should be unlocked */
|
|
if (EXIT_SUCCESS == hcd_store_urb(urb)) {
|
|
ddekit_sem_up(urb_lock);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* hcd_unschedule_urb *
|
|
*===========================================================================*/
|
|
static void
|
|
hcd_unschedule_urb(hcd_urb * urb)
|
|
{
|
|
DEBUG_DUMP;
|
|
|
|
hcd_remove_urb(urb);
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* hcd_urb_scheduler_task *
|
|
*===========================================================================*/
|
|
static void
|
|
hcd_urb_scheduler_task(void * UNUSED(arg))
|
|
{
|
|
hcd_device_state * current_device;
|
|
hcd_urb * current_urb;
|
|
|
|
DEBUG_DUMP;
|
|
|
|
for (;;) {
|
|
/* Wait for scheduler to unlock on any URB submit */
|
|
ddekit_sem_down(urb_lock);
|
|
|
|
/* Get URB */
|
|
current_urb = hcd_get_urb();
|
|
|
|
/* Get URB's target device */
|
|
current_device = current_urb->target_device;
|
|
|
|
/* Check for mismatch */
|
|
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 */
|
|
current_device->urb = current_urb;
|
|
|
|
/* Start handling URB event */
|
|
hcd_handle_event(current_device, HCD_EVENT_URB,
|
|
HCD_UNUSED_VAL);
|
|
|
|
/* Wait for completion */
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* hcd_urb_handled *
|
|
*===========================================================================*/
|
|
static void
|
|
hcd_urb_handled(hcd_urb * urb)
|
|
{
|
|
DEBUG_DUMP;
|
|
|
|
/* This URB will be scheduled no more */
|
|
hcd_unschedule_urb(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;
|
|
}
|