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

📄 usbtarget.c

📁 开放源码实时操作系统源码.
💻 C
📖 第 1 页 / 共 5 页
字号:
// Control-IN transfers. These have to be handled a little bit differently
// from bulk transfers. The target never actually initiates anything. Instead
// the host will send reserved control messages which are handled at DSR
// level and passed to handle_reserved_control_messages() below. Assuming
// a control-IN test is in progress, that will take appropriate action. The
// thread will be woken up only once all packets have been transferred, or
// on abnormal termination.

// Is a control-IN test currently in progress?
static UsbTest* control_in_test    = 0;

// What is the expected packet size?
static int      control_in_test_packet_size = 0;

// How many packets have been transferred so far?
static int      control_in_packets_transferred  = 0;

// Cancel a control-in test. handle_test_control_in() will have updated the static
// control_in_test so that handle_reserved_control_messages() knows what to do.
// If the test is not actually going to be run then system consistency demands
// that this update be undone. Also, the endpoint will have been locked to
// detect concurrent tests on the control endpoint.
static void
cancel_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_return
handle_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 void
run_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_return
handle_reserved_control_messages(usbs_control_endpoint* endpoint, void* data)
{
    usb_devreq*         req = (usb_devreq*) endpoint->control_buffer;
    usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;

    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 : 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 void
run_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 void
pool_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 void
pool_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 pool
static 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 void
pool_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_return
handle_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.h
static usbs_control_return
handle_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;

⌨️ 快捷键说明

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