📄 usbtarget.c
字号:
// Control-IN transfers. These have to be handled a little bit differently
// from bulk transfers. The target never actually initiates anything. Instead
// the host will send reserved control messages which are handled at DSR
// level and passed to handle_reserved_control_messages() below. Assuming
// a control-IN test is in progress, that will take appropriate action. The
// thread will be woken up only once all packets have been transferred, or
// on abnormal termination.
// Is a control-IN test currently in progress?
static UsbTest* control_in_test = 0;
// What is the expected packet size?
static int control_in_test_packet_size = 0;
// How many packets have been transferred so far?
static int control_in_packets_transferred = 0;
// Cancel a control-in test. handle_test_control_in() will have updated the static
// control_in_test so that handle_reserved_control_messages() knows what to do.
// If the test is not actually going to be run then system consistency demands
// that this update be undone. Also, the endpoint will have been locked to
// detect concurrent tests on the control endpoint.
static void
cancel_test_control_in(UsbTest* test)
{
CYG_ASSERTC(test == control_in_test);
control_in_test = (UsbTest*) 0;
control_in_test_packet_size = 0;
control_in_packets_transferred = 0;
unlock_endpoint(0, USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN);
test->cancel_fn = (void (*)(UsbTest*)) 0;
}
// Prepare for a control-IN transfer test.
static usbs_control_return
handle_test_control_in(usb_devreq* req)
{
UsbTest* test;
int index = 0;
CYG_ASSERTC((UsbTest*)0 == control_in_test);
test = pool_allocate();
unpack_usbtest_control_in(&(test->test_params.control_in), class_request, &index);
lock_endpoint(0, USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN);
test->which_test = usbtest_control_in;
test->recovery.endpoint = 0;
test->recovery.protocol = USB_ENDPOINT_DESCRIPTOR_ATTR_CONTROL;
test->recovery.size = 0; // Does not actually matter
test->cancel_fn = &cancel_test_control_in;
// Assume a pass. Failures are easy to detect.
test->result_pass = 1;
control_in_test = test;
control_in_test_packet_size = test->test_params.control_in.packet_size_initial;
control_in_packets_transferred = 0;
return USBS_CONTROL_RETURN_HANDLED;
}
// The thread for a control-in test. Actually all the hard work is done at DSR
// level, so this thread serves simply to detect when the test has completed
// and to perform some clean-ups.
static void
run_test_control_in(UsbTest* test)
{
CYG_ASSERTC(test == control_in_test);
cyg_semaphore_wait(&(test->sem));
// The DSR has detected that the test is complete.
control_in_test = (UsbTest*) 0;
control_in_test_packet_size = 0;
control_in_packets_transferred = 0;
test->cancel_fn = (void (*)(UsbTest*)) 0;
unlock_endpoint(0, USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN);
}
// ----------------------------------------------------------------------------
// This is installed from inside main() as the handler for reserved
// control messages.
static usbs_control_return
handle_reserved_control_messages(usbs_control_endpoint* endpoint, void* data)
{
usb_devreq* req = (usb_devreq*) endpoint->control_buffer;
usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
CYG_ASSERT(endpoint == control_endpoint, "control endpoint mismatch");
switch(req->request) {
case USBTEST_RESERVED_CONTROL_IN:
{
unsigned char* buf;
int len;
if ((UsbTest*)0 == control_in_test) {
result = USBS_CONTROL_RETURN_STALL;
break;
}
// Is this test over? If so indicate a failure because we
// cannot have received all the control packets.
if (current_tests_terminated) {
control_in_test->result_pass = 0;
snprintf(control_in_test->result_message, USBTEST_MAX_MESSAGE,
"Target, control IN transfer: not all packets received.");
cyg_semaphore_post(&(control_in_test->sem));
control_in_test = (UsbTest*) 0;
result = USBS_CONTROL_RETURN_STALL;
break;
}
// A control-IN test is indeed in progress, and the current state is
// held in control_in_test and control_in_test_packet_size. Check that
// the packet size matches up, i.e. that host and target are in sync.
len = (req->length_hi << 8) || req->length_lo;
if (control_in_test_packet_size != len) {
control_in_test->result_pass = 0;
snprintf(control_in_test->result_message, USBTEST_MAX_MESSAGE,
"Target, control IN transfer : the host only requested %d bytes instead of %d",
len, control_in_test_packet_size);
cyg_semaphore_post(&(control_in_test->sem));
control_in_test = (UsbTest*) 0;
result = USBS_CONTROL_RETURN_STALL;
break;
}
// Prepare a suitable reply buffer. This is happening at
// DSR level so runtime is important, but with an upper
// bound of 255 bytes the buffer should be small enough.
buf = control_in_test->buffer;
usbtest_fill_buffer(&(control_in_test->test_params.control_in.data), buf, control_in_test_packet_size);
control_endpoint->buffer_size = control_in_test_packet_size;
control_endpoint->buffer = buf;
USBTEST_CONTROL_NEXT_PACKET_SIZE(control_in_test_packet_size, control_in_test->test_params.control_in);
// Have all the packets been transferred?
control_in_packets_transferred++;
if (control_in_packets_transferred == control_in_test->test_params.control_in.number_packets) {
cyg_semaphore_post(&(control_in_test->sem));
control_in_test = (UsbTest*) 0;
}
result = USBS_CONTROL_RETURN_HANDLED;
break;
}
default:
CYG_FAIL("Unexpected reserved control message");
break;
}
return result;
}
/*}}}*/
// FIXME: add more tests.
// This utility is invoked from a thread in the thread pool whenever there is
// work to be done. It simply dispatches to the appropriate handler.
static void
run_test(UsbTest* test)
{
switch(test->which_test)
{
case usbtest_bulk_out : run_test_bulk_out(test); break;
case usbtest_bulk_in : run_test_bulk_in(test); break;
case usbtest_control_in: run_test_control_in(test); break;
default:
CYG_TEST_FAIL_EXIT("Internal error, attempt to run unknown test.\n");
break;
}
}
/*}}}*/
/*{{{ The thread pool */
// ----------------------------------------------------------------------------
// Just like on the host side, it is desirable to have a pool of
// threads available to perform test operations. Strictly speaking
// some tests will run without needing a separate thread, since many
// operations can be performed at DSR level. However typical
// application code will involve threads and it is desirable for test
// code to behave the same way. Also, some operations like validating
// the transferred data are expensive, and best done in thread context.
typedef struct PoolEntry {
cyg_sem_t wakeup;
cyg_thread thread_data;
cyg_handle_t thread_handle;
char thread_name[16];
char thread_stack[2 * CYGNUM_HAL_STACK_SIZE_TYPICAL];
cyg_bool in_use;
cyg_bool running;
UsbTest test;
} PoolEntry;
// This array must be uninitialized, or the executable size would
// be ludicrous.
PoolEntry pool[USBTEST_MAX_CONCURRENT_TESTS];
// The entry point for every thread in the pool. It just loops forever,
// waiting until it is supposed to run a test.
static void
pool_thread_function(cyg_addrword_t arg)
{
PoolEntry* pool_entry = (PoolEntry*) arg;
for ( ; ; ) {
cyg_semaphore_wait(&(pool_entry->wakeup));
run_test(&(pool_entry->test));
pool_entry->running = 0;
}
}
// Initialize all threads in the pool.
static void
pool_initialize(void)
{
int i;
for (i = 0; i < USBTEST_MAX_CONCURRENT_TESTS; i++) {
cyg_semaphore_init(&(pool[i].wakeup), 0);
pool[i].in_use = 0;
pool[i].running = 0;
sprintf(pool[i].thread_name, "worker%d", i);
cyg_thread_create( 0, &pool_thread_function, (cyg_addrword_t) &(pool[i]),
pool[i].thread_name, pool[i].thread_stack, 2 * CYGNUM_HAL_STACK_SIZE_TYPICAL,
&(pool[i].thread_handle), &(pool[i].thread_data));
cyg_thread_resume(pool[i].thread_handle);
}
}
// Allocate a single entry in the thread pool
static UsbTest*
pool_allocate(void)
{
UsbTest* result = (UsbTest*) 0;
if (thread_counter == USBTEST_MAX_CONCURRENT_TESTS) {
CYG_TEST_FAIL_EXIT("Internal error, thread resources exhaused.\n");
}
result = &(pool[thread_counter].test);
thread_counter++;
reset_usbtest(result);
return result;
}
// Start all the threads that are supposed to be running tests.
static void
pool_start(void)
{
int i;
for (i = 0; i < thread_counter; i++) {
pool[i].running = 1;
cyg_semaphore_post(&(pool[i].wakeup));
}
}
/*}}}*/
/*{{{ Class control messages */
// ----------------------------------------------------------------------------
// Handle class control messages. These provide the primary form of
// communication between host and target. There are requests to find out
// the number of endpoints, details of each endpoint, prepare a test run,
// abort a test run, get status, terminate the target-side, and so on.
// The handlers for starting specific test cases are kept alongside
// the test cases themselves.
//
// Note that these handlers will typically be invoked from DSR context
// and hence they are subject to the usual DSR restrictions.
//
// Problems have been experienced in some hosts sending control messages
// that involve additional host->target data. An ugly workaround is
// in place whereby any such data is sent in advance using separate
// control messages.
/*{{{ endpoint count */
// How many endpoints are supported by this device? That information is
// determined during initialization.
static usbs_control_return
handle_endpoint_count(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));
class_reply[0] = (unsigned char) number_endpoints;
control_endpoint->buffer = class_reply;
control_endpoint->buffer_size = 1;
return USBS_CONTROL_RETURN_HANDLED;
}
/*}}}*/
/*{{{ endpoint details */
// The host wants to know the details of a specific USB endpoint.
// The format is specified in protocol.h
static usbs_control_return
handle_endpoint_details(usb_devreq* req)
{
int buf_index;
CYG_ASSERTC((req->type & USB_DEVREQ_DIRECTION_MASK) == USB_DEVREQ_DIRECTION_IN);
CYG_ASSERTC((USBTEST_MAX_CONTROL_DATA == req->length_lo) && (0 == req->length_hi));
CYG_ASSERTC(req->index_lo < number_endpoints);
CYG_ASSERTC((0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
class_reply[0] = (unsigned char) usbs_testing_endpoints[req->index_lo].endpoint_type;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -