📄 usbhost.c
字号:
typedef struct PoolEntry {
pthread_t thread;
sem_t wakeup;
int running;
UsbTest test;
} PoolEntry;
static PoolEntry pool[USBTEST_MAX_CONCURRENT_TESTS];
// This is the entry point for every thread in the pool. It just loops forever,
// waiting until it is supposed to run a test. These threads never actually
// exit, instead there should be a call to exit() somewhere.
static void*
pool_function(void* arg)
{
PoolEntry* pool_entry = (PoolEntry*) arg;
int ret;
for ( ; ; ) {
do {
ret = sem_wait(&(pool_entry->wakeup));
if (ret != 0 && errno != EINTR) {
perror("sem_wait");
exit(1);
}
} while (ret != 0);
run_test(&(pool_entry->test));
pool_entry->running = 0;
}
return NULL;
}
// Initialize all threads in the pool.
static void
pool_initialize(void)
{
int i;
for (i = 0; i < USBTEST_MAX_CONCURRENT_TESTS; i++) {
pool[i].running = 0;
pool[i].test.fd = dup(usb_master_fd);
if (0 != sem_init(&(pool[i].wakeup), 0, 0)) {
fprintf(stderr, "usbhost: internal error, failed to initialize all semaphores.\n");
exit(EXIT_FAILURE);
}
if (0 != pthread_create(&(pool[i].thread), NULL, &pool_function, (void*) &(pool[i]))) {
fprintf(stderr, "usbhost: internal error, failed to start all threads.\n");
exit(EXIT_FAILURE);
}
}
}
// Allocate a single entry in the thread pool.
static UsbTest*
pool_allocate(void)
{
UsbTest* result = (UsbTest*) 0;
if (local_thread_count == USBTEST_MAX_CONCURRENT_TESTS) {
fprintf(stderr, "usbhost: internal error, thread resource exhausted.\n");
exit(EXIT_FAILURE);
}
result = &(pool[local_thread_count].test);
local_thread_count++;
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 < local_thread_count; i++) {
pool[i].running = 1;
sem_post(&(pool[i].wakeup));
}
}
/*}}}*/
/*{{{ Tcl routines */
// ----------------------------------------------------------------------------
// Tcl routines to provide access to the USB device from inside Tcl
// scripts, plus some general utilities. These routines deal mostly
// with preparing a test run. The actual work is done in C: the
// ioctl() operations are not readily accessible from Tcl, and
// operations like filling in buffers and calculating checksums are
// cpu-intensive.
/*{{{ pass/fail/abort */
// ----------------------------------------------------------------------------
// Some simple routines accessible from Tcl to get the target to report pass/fail or
// to make the target abort.
static int
tcl_target_pass(ClientData clientData __attribute__ ((unused)),
Tcl_Interp* interp,
int argc,
char** argv)
{
if (2 != argc) {
Tcl_SetResult(interp, "wrong # args: should be \"usbtest::target_pass <message>\"", TCL_STATIC);
return TCL_ERROR;
}
usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_PASS, 0, 0, strlen(argv[1]) + 1, argv[1]);
usb_sync(usb_master_fd, -1);
return TCL_OK;
}
static int
tcl_target_fail(ClientData clientData __attribute__ ((unused)),
Tcl_Interp* interp,
int argc,
char** argv)
{
if (2 != argc) {
Tcl_SetResult(interp, "wrong # args: should be \"usbtest::target_fail <message>\"", TCL_STATIC);
return TCL_ERROR;
}
usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_FAIL, 0, 0, strlen(argv[1]) + 1, argv[1]);
usb_sync(usb_master_fd, -1);
return TCL_OK;
}
// The next three routines cause the target to exit, so a usb_sync() is inappropriate.
static int
tcl_target_pass_exit(ClientData clientData __attribute__ ((unused)),
Tcl_Interp* interp,
int argc,
char** argv)
{
if (2 != argc) {
Tcl_SetResult(interp, "wrong # args: should be \"usbtest::target_pass_exit <message>\"", TCL_STATIC);
return TCL_ERROR;
}
usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_PASS_EXIT, 0, 0,
strlen(argv[1]) + 1, argv[1]);
return TCL_OK;
}
static int
tcl_target_fail_exit(ClientData clientData __attribute__ ((unused)),
Tcl_Interp* interp,
int argc,
char** argv)
{
if (2 != argc) {
Tcl_SetResult(interp, "wrong # args: should be \"usbtest::target_fail_exit <message>\"", TCL_STATIC);
return TCL_ERROR;
}
usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_FAIL_EXIT, 0, 0,
strlen(argv[1]) + 1, argv[1]);
return TCL_OK;
}
static int
tcl_target_abort(ClientData clientData __attribute__ ((unused)),
Tcl_Interp* interp,
int argc,
char** argv __attribute__ ((unused)) )
{
if (1 != argc) {
Tcl_SetResult(interp, "wrong # args: should be \"usbtest::target_abort\"", TCL_STATIC);
return TCL_ERROR;
}
usb_abort(usb_master_fd);
return TCL_OK;
}
/*}}}*/
/*{{{ start bulk test */
// ----------------------------------------------------------------------------
// Start a bulk test. The real Tcl interface to this functionality is
// implemented in Tcl: it takes care of figuring out sensible default
// arguments, validating the data, etc. All that this code does is
// allocate a thread and fill in the appropriate data, plus request
// the target-side to do the same thing.
static int
tcl_test_bulk(ClientData clientData __attribute__ ((unused)),
Tcl_Interp* interp,
int argc,
char** argv)
{
int i;
int tmp;
UsbTest* test;
unsigned char request[USBTEST_MAX_CONTROL_DATA];
int request_index;
// The data consists of 28 numbers for UsbTest_Bulk itself, and
// another 10 numbers for the test data definition.
if (39 != argc) {
Tcl_SetResult(interp, "wrong # args: should be \"usbtest::_test_bulk <message>\"", TCL_STATIC);
return TCL_ERROR;
}
for (i = 1; i < 39; i++) {
int discard;
if (TCL_OK != Tcl_GetInt(interp, argv[i], &discard)) {
Tcl_SetResult(interp, "invalid argument: all arguments should be numbers", TCL_STATIC);
return TCL_ERROR;
}
}
test = pool_allocate();
Tcl_GetInt(interp, argv[1], &(test->test_params.bulk.number_packets));
Tcl_GetInt(interp, argv[2], &(test->test_params.bulk.endpoint));
test->which_test = (USB_DIR_IN == (test->test_params.bulk.endpoint & USB_ENDPOINT_DIR_MASK))
? usbtest_bulk_in : usbtest_bulk_out;
Tcl_GetInt(interp, argv[ 3], &(test->test_params.bulk.tx_size));
Tcl_GetInt(interp, argv[ 4], &(test->test_params.bulk.tx_size_min));
Tcl_GetInt(interp, argv[ 5], &(test->test_params.bulk.tx_size_max));
Tcl_GetInt(interp, argv[ 6], &(test->test_params.bulk.tx_size_multiplier));
Tcl_GetInt(interp, argv[ 7], &(test->test_params.bulk.tx_size_divisor));
Tcl_GetInt(interp, argv[ 8], &(test->test_params.bulk.tx_size_increment));
Tcl_GetInt(interp, argv[ 9], &(test->test_params.bulk.rx_size));
Tcl_GetInt(interp, argv[10], &(test->test_params.bulk.rx_size_min));
Tcl_GetInt(interp, argv[11], &(test->test_params.bulk.rx_size_max));
Tcl_GetInt(interp, argv[12], &(test->test_params.bulk.rx_size_multiplier));
Tcl_GetInt(interp, argv[13], &(test->test_params.bulk.rx_size_divisor));
Tcl_GetInt(interp, argv[14], &(test->test_params.bulk.rx_size_increment));
Tcl_GetInt(interp, argv[15], &(test->test_params.bulk.rx_padding));
Tcl_GetInt(interp, argv[16], &(test->test_params.bulk.tx_delay));
Tcl_GetInt(interp, argv[17], &(test->test_params.bulk.tx_delay_min));
Tcl_GetInt(interp, argv[18], &(test->test_params.bulk.tx_delay_max));
Tcl_GetInt(interp, argv[19], &(test->test_params.bulk.tx_delay_multiplier));
Tcl_GetInt(interp, argv[20], &(test->test_params.bulk.tx_delay_divisor));
Tcl_GetInt(interp, argv[21], &(test->test_params.bulk.tx_delay_increment));
Tcl_GetInt(interp, argv[22], &(test->test_params.bulk.rx_delay));
Tcl_GetInt(interp, argv[23], &(test->test_params.bulk.rx_delay_min));
Tcl_GetInt(interp, argv[24], &(test->test_params.bulk.rx_delay_max));
Tcl_GetInt(interp, argv[25], &(test->test_params.bulk.rx_delay_multiplier));
Tcl_GetInt(interp, argv[26], &(test->test_params.bulk.rx_delay_divisor));
Tcl_GetInt(interp, argv[27], &(test->test_params.bulk.rx_delay_increment));
Tcl_GetInt(interp, argv[28], &tmp);
test->test_params.bulk.io_mechanism = (usb_io_mechanism) tmp;
Tcl_GetInt(interp, argv[29], &tmp);
test->test_params.bulk.data.format = (usbtestdata) tmp;
Tcl_GetInt(interp, argv[30], &(test->test_params.bulk.data.seed));
Tcl_GetInt(interp, argv[31], &(test->test_params.bulk.data.multiplier));
Tcl_GetInt(interp, argv[32], &(test->test_params.bulk.data.increment));
Tcl_GetInt(interp, argv[33], &(test->test_params.bulk.data.transfer_seed_multiplier));
Tcl_GetInt(interp, argv[34], &(test->test_params.bulk.data.transfer_seed_increment));
Tcl_GetInt(interp, argv[35], &(test->test_params.bulk.data.transfer_multiplier_multiplier));
Tcl_GetInt(interp, argv[36], &(test->test_params.bulk.data.transfer_multiplier_increment));
Tcl_GetInt(interp, argv[37], &(test->test_params.bulk.data.transfer_increment_multiplier));
Tcl_GetInt(interp, argv[38], &(test->test_params.bulk.data.transfer_increment_increment));
VERBOSE(3, "Preparing USB bulk test on endpoint %d, direction %s, for %d packets\n", \
test->test_params.bulk.endpoint, \
(usbtest_bulk_in == test->which_test) ? "IN" : "OUT", \
test->test_params.bulk.number_packets);
VERBOSE(3, " I/O mechanism is %s\n", \
(usb_io_mechanism_usb == test->test_params.bulk.io_mechanism) ? "low-level USB" : \
(usb_io_mechanism_dev == test->test_params.bulk.io_mechanism) ? "devtab" : "<invalid>");
VERBOSE(3, " Data format %s, data1 %d, data* %d, data+ %d, data1* %d, data1+ %d, data** %d, data*+ %d, data+* %d, data++ %d\n",\
(usbtestdata_none == test->test_params.bulk.data.format) ? "none" : \
(usbtestdata_bytefill == test->test_params.bulk.data.format) ? "bytefill" : \
(usbtestdata_wordfill == test->test_params.bulk.data.format) ? "wordfill" : \
(usbtestdata_byteseq == test->test_params.bulk.data.format) ? "byteseq" : \
(usbtestdata_wordseq == test->test_params.bulk.data.format) ? "wordseq" : "<invalid>", \
test->test_params.bulk.data.seed, \
test->test_params.bulk.data.multiplier, \
test->test_params.bulk.data.increment, \
test->test_params.bulk.data.transfer_seed_multiplier, \
test->test_params.bulk.data.transfer_seed_increment, \
test->test_params.bulk.data.transfer_multiplier_multiplier, \
test->test_params.bulk.data.transfer_multiplier_increment, \
test->test_params.bulk.data.transfer_increment_multiplier, \
test->test_params.bulk.data.transfer_increment_increment);
VERBOSE(3, " txsize1 %d, txsize>= %d, txsize<= %d, txsize* %d, txsize/ %d, txsize+ %d\n", \
test->test_params.bulk.tx_size, test->test_params.bulk.tx_size_min, \
test->test_params.bulk.tx_size_max, test->test_params.bulk.tx_size_multiplier, \
test->test_params.bulk.tx_size_divisor, test->test_params.bulk.tx_size_increment);
VERBOSE(3, " rxsize1 %d, rxsize>= %d, rxsize<= %d, rxsize* %d, rxsize/ %d, rxsize+ %d\n", \
test->test_params.bulk.rx_size, test->test_params.bulk.rx_size_min, \
test->test_params.bulk.rx_size_max, test->test_params.bulk.rx_size_multiplier, \
test->test_params.bulk.rx_size_divisor, test->test_params.bulk.rx_size_increment);
VERBOSE(3, " txdelay1 %d, txdelay>= %d, txdelay<= %d, txdelay* %d, txdelay/ %d, txdelay+ %d\n", \
test->test_params.bulk.tx_delay, test->test_params.bulk.tx_delay_min, \
test->test_params.bulk.tx_delay_max, test->test_params.bulk.tx_delay_multiplier, \
test->test_params.bulk.tx_delay_divisor, test->test_params.bulk.tx_delay_increment);
VERBOSE(3, " rxdelay1 %d, rxdelay>= %d, rxdelay<= %d, rxdelay* %d, rxdelay/ %d, rxdelay+ %d\n", \
test->test_params.bulk.rx_delay, test->test_params.bulk.rx_delay_min, \
test->test_params.bulk.rx_delay_max, test->test_params.bulk.rx_delay_multiplier, \
test->test_params.bulk.rx_delay_divisor, test->test_params.bulk.rx_delay_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_bulk(&(test->test_params.bulk), request, &request_index);
usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_TEST_BULK, 0, 0, request_index, request);
remote_thread_count++;
return TCL_OK;
}
/*}}}*/
/*{{{ start control-in test */
// ----------------------------------------------------------------------------
// Start a control-in test. The real Tcl interface to this
// functionality is implemented in Tcl: it takes care of figuring out
// sensible default arguments, validating the data, etc. All that this
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -