📄 usbtarget.c
字号:
// stuff. This information is held in a static.static usbs_control_returnhandle_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 contextstatic voidhandle_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 contextstatic usbs_control_returnhandle_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 voidhandle_abort_action(void){ CYG_TEST_FAIL_EXIT("Test abort requested by host application");}static usbs_control_returnhandle_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 voidhandle_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 contextstatic usbs_control_returnhandle_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_returnhandle_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_returnhandle_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_returnhandle_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_returnhandle_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 voidrecovery_callback(void* callback_arg, int transferred){ CYG_UNUSED_PARAM(void*, callback_arg); CYG_UNUSED_PARAM(int, transferred);} static usbs_control_returnhandle_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.endpoint & USB_DEVREQ_DIRECTION_MASK; if (!is_endpoint_locked(endpoint_number, endpoint_direction)) { // Locking the endpoint here would be good, but the endpoint would then // have to be unlocked again - probably in the recovery callback. // This complication is ignored for now. if (USB_ENDPOINT_DESCRIPTOR_ATTR_BULK == recovery.protocol) { int ep_index = lookup_endpoint(endpoint_number, endpoint_direction, USB_ENDPOINT_DESCRIPTOR_ATTR_BULK); CYG_ASSERTC(-1 != ep_index); if (USB_DEVREQ_DIRECTION_IN == endpoint_direction) { // The host wants some data. Supply it. A single byte will do fine to // complete the transfer. usbs_start_tx_buffer((usbs_tx_endpoint*) usbs_testing_endpoints[ep_index].endpoint, recovery_buffer, 1, &recovery_callback, (void*) 0); } else { // The host is trying to send some data. Accept all of it. usbs_start_rx_buffer((usbs_rx_endpoint*) usbs_testing_endpoints[ep_index].endpoint, recovery_buffer, recovery.size, &recovery_callback, (void*) 0); } } // No support for isochronous or interrupt transfers yet. // handle_reserved_control_messages() should generate stalls which // have the desired effect. } return USBS_CONTROL_RETURN_HANDLED;}/*}}}*//*{{{ get result */// Return the result of one the tests. This can be a single
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -