📄 usbtarget.c
字号:
class_reply[1] = (unsigned char) usbs_testing_endpoints[req->index_lo].endpoint_number;
class_reply[2] = (unsigned char) usbs_testing_endpoints[req->index_lo].endpoint_direction;
class_reply[3] = (unsigned char) usbs_testing_endpoints[req->index_lo].max_in_padding;
buf_index = 4;
pack_int(usbs_testing_endpoints[req->index_lo].min_size, class_reply, &buf_index);
pack_int(usbs_testing_endpoints[req->index_lo].max_size, class_reply, &buf_index);
if (NULL == usbs_testing_endpoints[req->index_lo].devtab_entry) {
class_reply[buf_index] = '\0';
control_endpoint->buffer_size = buf_index + 1;
} else {
int len = strlen(usbs_testing_endpoints[req->index_lo].devtab_entry) + buf_index + 1;
if (len > USBTEST_MAX_CONTROL_DATA) {
return USBS_CONTROL_RETURN_STALL;
} else {
strcpy(&(class_reply[buf_index]), usbs_testing_endpoints[req->index_lo].devtab_entry);
control_endpoint->buffer_size = len;
}
}
control_endpoint->buffer = class_reply;
return USBS_CONTROL_RETURN_HANDLED;
}
/*}}}*/
/*{{{ sync */
// The host wants to know whether or not the target is currently busy doing
// stuff. This information is held in a static.
static usbs_control_return
handle_sync(usb_devreq* req)
{
CYG_ASSERTC((1 == req->length_lo) && (0 == req->length_hi) && \
((req->type & USB_DEVREQ_DIRECTION_MASK) == USB_DEVREQ_DIRECTION_IN));
CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
CYG_ASSERT(0 == class_request_size, "A sync operation should not involve any data");
class_reply[0] = (unsigned char) idle;
control_endpoint->buffer = class_reply;
control_endpoint->buffer_size = 1;
return USBS_CONTROL_RETURN_HANDLED;
}
/*}}}*/
/*{{{ pass/fail */
// Allow the host to generate some pass or fail messages, and
// optionally terminate the test. These are synchronous requests
// so the data can be left in class_request.
static int passfail_request = 0;
// Invoked from thread context
static void
handle_passfail_action(void)
{
switch (passfail_request) {
case USBTEST_PASS:
CYG_TEST_PASS(class_request);
break;
case USBTEST_PASS_EXIT:
CYG_TEST_PASS(class_request);
CYG_TEST_EXIT("Exiting normally as requested by the host");
break;
case USBTEST_FAIL:
CYG_TEST_FAIL(class_request);
break;
case USBTEST_FAIL_EXIT:
CYG_TEST_FAIL(class_request);
CYG_TEST_EXIT("Exiting normally as requested by the host");
break;
default:
CYG_FAIL("Bogus invocation of usbtest_main_passfail");
break;
}
}
// Invoked from DSR context
static usbs_control_return
handle_passfail(usb_devreq* req)
{
CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
CYG_ASSERT(class_request_size > 0, "A pass/fail message should be supplied");
CYG_ASSERT(idle, "Pass/fail messages are only allowed when idle");
CYG_ASSERT((void (*)(void))0 == main_thread_action, "No thread operation should be pending.");
passfail_request = req->request;
idle = false;
main_thread_action = &handle_passfail_action;
cyg_semaphore_post(&main_wakeup);
return USBS_CONTROL_RETURN_HANDLED;
}
/*}}}*/
/*{{{ abort */
// The host has concluded that there is no easy way to get both target and
// host back to a sensible state. For example there may be a thread that
// is blocked waiting for some I/O that is not going to complete. The abort
// should be handled at thread level, not DSR level, so that the host
// still sees the low-level USB handshake.
static void
handle_abort_action(void)
{
CYG_TEST_FAIL_EXIT("Test abort requested by host application");
}
static usbs_control_return
handle_abort(usb_devreq* req)
{
CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
CYG_ASSERT(idle, "Abort messages are only allowed when idle");
CYG_ASSERT((void (*)(void))0 == main_thread_action, "No thread operation should be pending.");
idle = false;
main_thread_action = &handle_abort_action;
cyg_semaphore_post(&main_wakeup);
return USBS_CONTROL_RETURN_HANDLED;
}
/*}}}*/
/*{{{ cancel */
// Invoked from thread context
// Cancelling pending test cases simply involves iterating over the allocated
// entries in the pool, invoking any cancellation functions that have been
// defined, and then resetting the tread count. The actual tests have not
// yet started so none of the threads will be active.
static void
handle_cancel_action(void)
{
int i;
for (i = 0; i < thread_counter; i++) {
if ((void (*)(UsbTest*))0 != pool[i].test.cancel_fn) {
(*(pool[i].test.cancel_fn))(&(pool[i].test));
pool[i].test.cancel_fn = (void (*)(UsbTest*)) 0;
}
}
thread_counter = 0;
}
// Invoked from DSR context
static usbs_control_return
handle_cancel(usb_devreq* req)
{
CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
CYG_ASSERT(0 == class_request_size, "A cancel operation should not involve any data");
CYG_ASSERT(idle, "Cancel requests are only allowed when idle");
CYG_ASSERT(!running, "Cancel requests cannot be sent once the system is running");
CYG_ASSERT((void (*)(void))0 == main_thread_action, "No thread operation should be pending.");
idle = false;
main_thread_action = &handle_cancel_action;
cyg_semaphore_post(&main_wakeup);
return USBS_CONTROL_RETURN_HANDLED;
}
/*}}}*/
/*{{{ start */
// Start the tests running. This just involves waking up the pool threads
// and setting the running flag, with the latter serving primarily for
// assertions.
static usbs_control_return
handle_start(usb_devreq* req)
{
CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
CYG_ASSERT(0 == class_request_size, "A start operation should not involve any data");
CYG_ASSERT(!running, "Start requests cannot be sent if the system is already running");
current_tests_terminated = false;
running = true;
pool_start();
return USBS_CONTROL_RETURN_HANDLED;
}
/*}}}*/
/*{{{ finished */
// Have all the tests finished? This involves checking all the threads
// involved in the current batch of tests and seeing whether or not
// their running flag is still set.
static usbs_control_return
handle_finished(usb_devreq* req)
{
int i;
int result = 1;
CYG_ASSERTC((1 == req->length_lo) && (0 == req->length_hi) && \
((req->type & USB_DEVREQ_DIRECTION_MASK) == USB_DEVREQ_DIRECTION_IN));
CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
CYG_ASSERT(0 == class_request_size, "A finished operation should not involve any data");
CYG_ASSERT(running, "Finished requests can only be sent if the system is already running");
for (i = 0; i < thread_counter; i++) {
if (pool[i].running) {
result = 0;
break;
}
}
class_reply[0] = (unsigned char) result;
control_endpoint->buffer = class_reply;
control_endpoint->buffer_size = 1;
return USBS_CONTROL_RETURN_HANDLED;
}
/*}}}*/
/*{{{ set terminated */
// A timeout has occurred, or there is some other failure. The first step
// in recovery is to set the terminated flag so that as recovery action
// takes place and the threads wake up they make no attempt to continue
// doing more transfers.
static usbs_control_return
handle_set_terminated(usb_devreq* req)
{
CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
CYG_ASSERT(0 == class_request_size, "A set-terminated operation should not involve any data");
CYG_ASSERT(running, "The terminated flag can only be set when there are running tests");
current_tests_terminated = 1;
return USBS_CONTROL_RETURN_HANDLED;
}
/*}}}*/
/*{{{ get recovery */
// Return the recovery information for one of the threads involved in the
// current batch of tests, so that the host can perform a USB operation
// that will sort out that thread.
static usbs_control_return
handle_get_recovery(usb_devreq* req)
{
int buffer_index;
CYG_ASSERT(current_tests_terminated, "Recovery should only be attempted when the terminated flag is set");
CYG_ASSERT(running, "If there are no tests running then recovery is impossible");
CYG_ASSERTC((12 == req->length_lo) && (0 == req->length_hi) && \
((req->type & USB_DEVREQ_DIRECTION_MASK) == USB_DEVREQ_DIRECTION_IN));
CYG_ASSERTC(req->index_lo <= thread_counter);
CYG_ASSERTC((0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
CYG_ASSERT(0 == class_request_size, "A get-recovery operation should not involve any data");
control_endpoint->buffer = class_reply;
if (!pool[req->index_lo].running) {
// Actually, this particular thread has terminated so no recovery is needed.
control_endpoint->buffer_size = 0;
} else {
buffer_index = 0;
pack_usbtest_recovery(&(pool[req->index_lo].test.recovery), class_reply, &buffer_index);
control_endpoint->buffer_size = buffer_index;
}
return USBS_CONTROL_RETURN_HANDLED;
}
/*}}}*/
/*{{{ perform recovery */
// The host has identified a course of action that could unlock a thread
// on the host-side that is currently blocked performing a USB operation.
// Typically this involves either sending or accepting some data. If the
// endpoint is still locked, in other words if there is a still a local
// thread attempting to communicate on the specified endpoint, then
// things are messed up: both sides are trying to communicate, but nothing
// is happening. The eCos USB API is such that attempting multiple
// concurrent operations on a single endpoint is disallowed, so
// the recovery request has to be ignored. If things do not sort themselves
// out then the whole test run will have to be aborted.
// A dummy completion function for when a recovery operation has completed.
static void
recovery_callback(void* callback_arg, int transferred)
{
CYG_UNUSED_PARAM(void*, callback_arg);
CYG_UNUSED_PARAM(int, transferred);
}
static usbs_control_return
handle_perform_recovery(usb_devreq* req)
{
int buffer_index;
int endpoint_number;
int endpoint_direction;
UsbTest_Recovery recovery;
CYG_ASSERT(current_tests_terminated, "Recovery should only be attempted when the terminated flag is set");
CYG_ASSERT(running, "If there are no tests running then recovery is impossible");
CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
CYG_ASSERT(12 == class_request_size, "A perform-recovery operation requires recovery data");
buffer_index = 0;
unpack_usbtest_recovery(&recovery, class_request, &buffer_index);
endpoint_number = recovery.endpoint & ~USB_DEVREQ_DIRECTION_MASK;
endpoint_direction = recovery.endpo
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -