⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usbhost.c

📁 开放源码实时操作系统源码.
💻 C
📖 第 1 页 / 共 5 页
字号:
// 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_control_in(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 6 numbers for UsbTest_ControlIn itself, and
    // another 10 numbers for the test data definition.
    if (17 != argc) {
        Tcl_SetResult(interp, "wrong # args: should be \"usbtest::_test_control_in <message>\"", TCL_STATIC);
        return TCL_ERROR;
    }
    for (i = 1; i < 17; 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();
    test->which_test    = usbtest_control_in;
    Tcl_GetInt(interp, argv[1], &(test->test_params.control_in.number_packets));
    Tcl_GetInt(interp, argv[2], &(test->test_params.control_in.packet_size_initial));
    Tcl_GetInt(interp, argv[3], &(test->test_params.control_in.packet_size_min));
    Tcl_GetInt(interp, argv[4], &(test->test_params.control_in.packet_size_max));
    Tcl_GetInt(interp, argv[5], &(test->test_params.control_in.packet_size_multiplier));
    Tcl_GetInt(interp, argv[6], &(test->test_params.control_in.packet_size_increment));
    Tcl_GetInt(interp, argv[7], &tmp);
    test->test_params.bulk.data.format = (usbtestdata) tmp;
    Tcl_GetInt(interp, argv[ 8], &(test->test_params.control_in.data.seed));
    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 int
tcl_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 int
local_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 int
remote_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 void
recover_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 void
recover_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 void
run_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 int
tcl_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

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -