📄 usbhost.c
字号:
Tcl_GetInt(interp, argv[ 9], &(test->test_params.control_in.data.multiplier)); Tcl_GetInt(interp, argv[10], &(test->test_params.control_in.data.increment)); Tcl_GetInt(interp, argv[11], &(test->test_params.control_in.data.transfer_seed_multiplier)); Tcl_GetInt(interp, argv[12], &(test->test_params.control_in.data.transfer_seed_increment)); Tcl_GetInt(interp, argv[13], &(test->test_params.control_in.data.transfer_multiplier_multiplier)); Tcl_GetInt(interp, argv[14], &(test->test_params.control_in.data.transfer_multiplier_increment)); Tcl_GetInt(interp, argv[15], &(test->test_params.control_in.data.transfer_increment_multiplier)); Tcl_GetInt(interp, argv[16], &(test->test_params.control_in.data.transfer_increment_increment)); // That is all the data converted from Tcl to C, and a local thread is set up to handle this // request. Also set up a thread on the target. request_index = 0; pack_usbtest_control_in(&(test->test_params.control_in), request, &request_index); usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_TEST_CONTROL_IN, 0, 0, request_index, request); remote_thread_count++; return TCL_OK;}/*}}}*//*{{{ Cancel the current batch of tests */static inttcl_cancel(ClientData clientData __attribute__ ((unused)), Tcl_Interp* interp, int argc, char** argv __attribute__ ((unused)) ){ if (1 != argc) { Tcl_SetResult(interp, "wrong # args: should be \"usbtest::cancel\"", TCL_STATIC); return TCL_ERROR; } // Send the request on to the target. usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_CANCEL, 0, 0, 0, (void*)0); // Now cancel all the local tests. This can be done by resetting the counter // of allocated threads: no actual work will have been started yet. local_thread_count = 0; // And synchronise with the target if (!usb_sync(usb_master_fd, 30)) { fprintf(stderr, "usbhost: error, target has failed to process test cancel request.\n"); exit(EXIT_FAILURE); } remote_thread_count = 0; return TCL_OK;}/*}}}*//*{{{ Run a batch of tests */// ----------------------------------------------------------------------------// This code does an awful lot of the hard work. Start with various utilities.// Has the current batch finished as far as the local threads are concerned?static intlocal_batch_finished(void){ int result = 1; int i; for (i = 0; i < local_thread_count; i++) { if (pool[i].running) { result = 0; break; } } return result;}// Has the current batch finished as far as remote threads are concerned?static intremote_batch_finished(void){ char buf[1]; usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN, USBTEST_FINISHED, 0, 0, 1, (void*) buf); return buf[0];}// Perform recovery for a thread on the target. This involves asking the// target for recovery information, then performing an appropriate// action. If no data is returned then no recovery is needed for this thread.static voidrecover_remote(int index){ unsigned char buffer[USBTEST_MAX_CONTROL_DATA]; int buffer_index; UsbTest_Recovery recovery; int i; if (0 != usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN, USBTEST_GET_RECOVERY, 0, index, 12, buffer)) { // There is work to be done buffer_index = 0; unpack_usbtest_recovery(&recovery, buffer, &buffer_index); // We have an endpoint, a protocol, and a size. if (0 == recovery.endpoint) { // The target just needs a dummy reserved control message usb_reliable_control_message(usb_master_fd, USB_TYPE_RESERVED | USB_RECIP_DEVICE, USBTEST_RESERVED_CONTROL_IN, 0, 0, 0, (void*) 0); } else if (USB_ENDPOINT_XFER_BULK == recovery.protocol) { // Either we need to send some data to the target, or we need to accept some data. static unsigned char recovery_buffer[USBTEST_MAX_BULK_DATA + USBTEST_MAX_BULK_DATA_EXTRA]; struct usbdevfs_bulktransfer transfer; transfer.ep = recovery.endpoint; transfer.timeout = 2000; // Two seconds. Should be plenty, even for a large bulk transfer. transfer.data = recovery_buffer; if (USB_DIR_IN == (recovery.endpoint & USB_ENDPOINT_DIR_MASK)) { transfer.len = recovery.size; } else { transfer.len = 1; } errno = 0; i = ioctl(usb_master_fd, USBDEVFS_BULK, &transfer); } // There is no recovery support yet for other protocols. }}// Perform recovery for a local thread. This involves extracting the// recovery information from the local thread and asking the target// to take appropriate action.static voidrecover_local(int index){ unsigned char buffer[USBTEST_MAX_CONTROL_DATA]; int buffer_index; if (pool[index].running) { buffer_index = 0; pack_usbtest_recovery(&(pool[index].test.recovery), buffer, &buffer_index); usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_PERFORM_RECOVERY, 0, 0, buffer_index, (void*) buffer); }}// All done, time for a clean-up on both target and host. The latter// is achieved simply by resetting the thread pool, which actually// just means resetting the counter since all the threads are blocked// waiting for the next batch.static voidrun_done(void){ usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_BATCH_DONE, 0, 0, 0, (void*) NULL); local_thread_count = 0; remote_thread_count = 0;}// The main routine, as invoked from Tcl. This takes a single// argument, a timeout in seconds.static inttcl_run(ClientData clientData __attribute__ ((unused)), Tcl_Interp* interp, int argc, char** argv __attribute__ ((unused)) ){ struct timespec delay; int timeout; time_t start; time_t now; int i, j; unsigned char result_buf[USBTEST_MAX_CONTROL_DATA]; int all_ok; if (2 != argc) { Tcl_SetResult(interp, "wrong # args: should be \"usbtest::_run <timeout>\"", TCL_STATIC); return TCL_ERROR; } if (TCL_OK != Tcl_GetInt(interp, argv[1], &timeout)) { Tcl_SetResult(interp, "invalid argument: timeout should be numeric", TCL_STATIC); return TCL_ERROR; } VERBOSE(2, "Starting a testrun, timeout %d seconds\n", timeout); // Start the tests running on the target. The target USB hardware // will not actually do anything except in response to packets // from the host, so it is better to start the target before the // local threads. usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_START, 0, 0, 0, (void*) 0); // Now the local threads can get going. current_tests_terminated = 0; pool_start(); // Now leave the various testing threads to do their thing until // either side believes that the batch has finished, or until the // timeout expires. Note that if one side decides that the batch // has finished but the other disagrees, that in itself indicates // a test failure of sorts. // // There is a question of polling frequency. Once a second avoids // excessive polling traffic on the USB bus, and should not impose // intolerable delays for short-duration tests. start = time(NULL); do { VERBOSE(3, "The tests are running, waiting for termination\n"); delay.tv_sec = 1; delay.tv_nsec = 0; nanosleep(&delay, NULL); now = time(NULL); } while (((start + timeout) > now) && !local_batch_finished() && !remote_batch_finished()); VERBOSE(2, "Termination detected, time elapsed %ld\n", (long) now - start); // If either side believes that testing is not complete, things // get messy. Start by setting the terminated flag. Any tests that // are actually still running happily but have not finished within // the timeout should detect this and stop. if (!local_batch_finished() || !remote_batch_finished()) { VERBOSE(2, "Testing is not yet complete, setting TERMINATED flag\n"); current_tests_terminated = 1; usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_SET_TERMINATED, 0, 0, 0, (void*) 0); // And another delay, to give threads a chance to detect the // flag's update delay.tv_sec = 1; delay.tv_nsec = 0; nanosleep(&delay, NULL); } // If there is still are unfinished threads, recovery action // is needed. It is not clear whether it is better to unlock // the local threads first, or the remote threads. For now the // latter approach is taken. if (!remote_batch_finished()) { int i; VERBOSE(2, "Remote threads still running, performing remote recovery\n"); for (i = 0; i < remote_thread_count; i++) { recover_remote(i); } // Allow the recovery actions to take effect delay.tv_sec = 1; delay.tv_nsec = 0; nanosleep(&delay, NULL); } if (!local_batch_finished()) { int i; VERBOSE(2, "Local threads still running, performing local recovery\n"); for (i = 0; i < local_thread_count; i++) { recover_local(i); } // Allow the recovery actions to take effect delay.tv_sec = 1; delay.tv_nsec = 0; nanosleep(&delay, NULL); } // One last check to make sure that everything is finished. If not, // testing has broken down and it is necessary to abort. if (!local_batch_finished() || !remote_batch_finished()) { VERBOSE(2, "Giving local and remote threads another chance to finish.\n"); // Allow the recovery actions to take effect delay.tv_sec = 5; delay.tv_nsec = 0; nanosleep(&delay, NULL); if (!local_batch_finished() || !remote_batch_finished()) { // OK, normality has not been restored. // It would be nice to get hold of and display any error messages. usb_abort(usb_master_fd); fprintf(stderr, "Fatal error: the host test program and the remote target are out of synch.\n"); fprintf(stderr, " recovery has been attempted, without success.\n"); fprintf(stderr, " USB testing cannot continue.\n"); exit(EXIT_FAILURE); } } VERBOSE(2, "Local and remote threads are in synch, collecting results.\n"); // The world is in a coherent state. Time to collect the results. // The return value of this function is a simple boolean. More // detailed results will be held in a Tcl variable as a list of // messages. It is desirable to keep both local and remote results // in order. for (i = 0; i < ((local_thread_count < remote_thread_count) ? local_thread_count : remote_thread_count); i++) { if (!pool[i].test.result_pass) { Tcl_SetVar(interp, "usbtest::results", pool[i].test.result_message, all_ok ? (TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT) : (TCL_GLOBAL_ONLY | TCL_APPEND_VALUE | TCL_LIST_ELEMENT)); all_ok = 0; } usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN, USBTEST_GET_RESULT, 0, i, USBTEST_MAX_CONTROL_DATA, (void*) result_buf); if (!result_buf[0]) { Tcl_SetVar(interp, "usbtest::results", &(result_buf[1]), all_ok ? TCL_GLOBAL_ONLY : (TCL_GLOBAL_ONLY | TCL_APPEND_VALUE | TCL_LIST_ELEMENT)); all_ok = 0; } } for (j = i; j < local_thread_count; j++) { if (!pool[j].test.result_pass) { Tcl_SetVar(interp, "usbtest::results", pool[j].test.result_message, all_ok ? TCL_GLOBAL_ONLY : (TCL_GLOBAL_ONLY | TCL_APPEND_VALUE | TCL_LIST_ELEMENT)); all_ok = 0; } } for (j = i; j < remote_thread_count; j++) { usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN, USBTEST_GET_RESULT, 0, i, USBTEST_MAX_CONTROL_DATA, (void*) result_buf); if (!result_buf[0]) { Tcl_SetVar(interp, "usbtest::results", &(result_buf[1]), all_ok ? TCL_GLOBAL_ONLY : (TCL_GLOBAL_ONLY | TCL_APPEND_VALUE | TCL_LIST_ELEMENT)); all_ok = 0; } } VERBOSE(2, "Overall test result %d
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -