📄 usbtarget.c
字号:
// that this update be undone. Also, the endpoint will have been locked to// detect concurrent tests on the control endpoint.static voidcancel_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_returnhandle_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 voidrun_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_returnhandle_reserved_control_messages(usbs_control_endpoint* endpoint, void* data){ usb_devreq* req = (usb_devreq*) endpoint->control_buffer; usbs_control_return result; 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 on endpoint %d : 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 voidrun_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 voidpool_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 voidpool_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 poolstatic 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 voidpool_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_returnhandle_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.hstatic usbs_control_returnhandle_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; 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -