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

📄 usbtarget.c

📁 开放源码实时操作系统源码.
💻 C
📖 第 1 页 / 共 5 页
字号:
/*{{{  Banner                                                   */

/*=================================================================
//
//        target.c
//
//        USB testing - target-side
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// This program performs appropriate USB initialization and initializes
// itself as a specific type of USB peripheral, Red Hat eCos testing.
// There is no actual host-side device driver for this, instead there is
// a test application which performs ioctl's on /proc/bus/usb/... and
// makes appropriate functionality available to a Tcl script.
//
// Author(s):     bartv
// Date:          2001-07-04
//####DESCRIPTIONEND####
//==========================================================================
*/

/*}}}*/
/*{{{  #include's                                               */

#include <stdio.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/infra/diag.h>
#include <cyg/kernel/kapi.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/io/io.h>
#include <cyg/io/usb/usbs.h>
#include <cyg/infra/testcase.h>
#include "protocol.h"

/*}}}*/

/*{{{  Statics                                                  */

// ----------------------------------------------------------------------------
// Statics.

// The number of endpoints supported by the device driver.
static int number_endpoints     = 0;

// The control endpoint
static usbs_control_endpoint* control_endpoint = (usbs_control_endpoint*) 0;

// Buffers for incoming and outgoing data, and a length field.
static unsigned char class_request[USBTEST_MAX_CONTROL_DATA + 1];
static unsigned char class_reply[USBTEST_MAX_CONTROL_DATA + 1];
static int           class_request_size  = 0;

// This semaphore is used by DSRs to wake up the main thread when work has to
// be done at thread level.
static cyg_sem_t    main_wakeup;

// And this function pointer identifies the work that has to be done.
static void         (*main_thread_action)(void)  = (void (*)(void)) 0;

// Is the system still busy processing a previous request? This variable is
// checked in response to the synch request. It may get updated in
// DSRs as well as at thread level, hence volatile.
static volatile int idle    = 1;

// Are any tests currently running?
static int          running = 0;

// Has the current batch of tests been terminated by the host? This
// flag is checked by the various test handlers at appropriate
// intervals, and helps to handle the case where one of the side has
// terminated early because an error has been detected.
static int          current_tests_terminated = 0;

// A counter for the number of threads involved in the current batch of tests.
static int          thread_counter    = 0;

// An extra buffer for recovery operations, for example to accept and discard
// data which the host is still trying to send.
static unsigned char recovery_buffer[USBTEST_MAX_BULK_DATA + USBTEST_MAX_BULK_DATA_EXTRA];

/*}}}*/
/*{{{  Logging                                                  */

// ----------------------------------------------------------------------------
// The target-side code can provide various levels of run-time logging.
// Obviously the verbose flag cannot be controlled by a command-line
// argument, but it can be set from inside gdb or alternatively by
// a request from the host.
//
// NOTE: is printf() the best I/O routine to use here?

static int verbose = 0;

#define VERBOSE(_level_, _format_, _args_...)   \
    do {                                        \
        if (verbose >= _level_) {               \
            diag_printf(_format_, ## _args_);        \
        }                                       \
    } while (0);

/*}}}*/
/*{{{  Utilities                                                */

// ----------------------------------------------------------------------------
// A reimplementation of nanosleep, to avoid having to pull in the
// POSIX compatibility testing for USB testing.
static void
usbs_nanosleep(int delay)
{
    cyg_tick_count_t ticks;
    cyg_resolution_t resolution = cyg_clock_get_resolution(cyg_real_time_clock());

    // (resolution.dividend/resolution.divisor) == nanoseconds/tick
    //   e.g. 1000000000/100, i.e. 10000000 ns or 10 ms per tick
    // So ticks = (delay * divisor) / dividend
    //   e.g. (10000000 * 100) / 1000000000
    // with a likely value of 0 for delays of less than the clock resolution,
    // so round those up to one tick.

    cyg_uint64 tmp = (cyg_uint64) delay;
    tmp *= (cyg_uint64) resolution.divisor;
    tmp /= (cyg_uint64) resolution.dividend;

    ticks = (int) tmp;
    if (0 != ticks) {
        cyg_thread_delay(ticks);
    }
}

// ----------------------------------------------------------------------------
// Fix any problems in the driver-supplied endpoint data
//
// Maximum transfer sizes are limited not just by the capabilities
// of the driver but also by the testing code itself, since e.g.
// buffers for transfers are statically allocated.
static void
fix_driver_endpoint_data(void)
{
    int i;
    
    for (i = 0; !USBS_TESTING_ENDPOINTS_IS_TERMINATOR(usbs_testing_endpoints[i]); i++) {
        if (USB_ENDPOINT_DESCRIPTOR_ATTR_BULK == usbs_testing_endpoints[i].endpoint_type) {
            if ((-1 == usbs_testing_endpoints[i].max_size) ||
                (usbs_testing_endpoints[i].max_size > USBTEST_MAX_BULK_DATA)) {
                usbs_testing_endpoints[i].max_size = USBTEST_MAX_BULK_DATA;
            }
        }
    }
}

// ----------------------------------------------------------------------------
// A heartbeat thread.
//
// USB tests can run for a long time with no traffic on the debug channel,
// which can cause problems. To avoid problems it is possible to have a
// heartbeat thread running in the background, sending output at one
// second intervals.
//
// Depending on the configuration the output may still be line-buffered,
// but that is still sufficient to keep things happy.

static cyg_bool     heartbeat = false;
static cyg_thread   heartbeat_data;
static cyg_handle_t heartbeat_handle;
static char         heartbeat_stack[CYGNUM_HAL_STACK_SIZE_TYPICAL];

static void
heartbeat_function(cyg_addrword_t arg __attribute((unused)))
{
    char* message = "alive\n";
    int i;
    
    for ( i = 0; ; i = (i + 1) % 6) {
        usbs_nanosleep(1000000000);
        if (heartbeat) {
            diag_write_char(message[i]);
        }
    }
}

static void
start_heartbeat(void)
{
    cyg_thread_create( 0, &heartbeat_function, 0,
                       "heartbeat", heartbeat_stack, CYGNUM_HAL_STACK_SIZE_TYPICAL,
                       &heartbeat_handle, &heartbeat_data);
    cyg_thread_resume(heartbeat_handle);
}


/*}}}*/
/*{{{  Endpoint usage                                           */

// ----------------------------------------------------------------------------
// It is important to keep track of which endpoints are currently in use,
// because the behaviour of the USB I/O routines is undefined if there are
// concurrent attempts to communicate on the same endpoint. Normally this is
// not a problem because the host will ensure that a given endpoint is used
// for only one endpoint at a time, but when performing recovery action it
// is important that the system is sure that a given endpoint can be accessed
// safely.

static cyg_bool in_endpoint_in_use[16];
static cyg_bool out_endpoint_in_use[16];

// Lock the given endpoint. In theory this is only ever accessed from a single
// test thread at a time, but just in case...
static void
lock_endpoint(int endpoint, int direction)
{
    CYG_ASSERTC((endpoint >=0) && (endpoint < 16));
    CYG_ASSERTC((USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN == direction) || (USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT == direction));
    
    cyg_scheduler_lock();
    if (0 == endpoint) {
        // Comms traffic on endpoint 0 is implemented using reserved control messages.
        // It is not really possible to have concurrent IN and OUT operations because
        // the two would interfere with each other.
        CYG_ASSERTC(!in_endpoint_in_use[0] && !out_endpoint_in_use[0]);
        in_endpoint_in_use[0]  = true;
        out_endpoint_in_use[0] = true;
    } else if (USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN == direction) {
        CYG_ASSERTC(!in_endpoint_in_use[endpoint]);
        in_endpoint_in_use[endpoint] = true;
    } else {
        CYG_ASSERTC(!out_endpoint_in_use[endpoint]);
        out_endpoint_in_use[endpoint] = true;
    }
    cyg_scheduler_unlock();
}

static void
unlock_endpoint(int endpoint, int direction)
{
    CYG_ASSERTC((endpoint >= 0) && (endpoint < 16));
    CYG_ASSERTC((USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN == direction) || (USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT == direction));
    
    if (0 == endpoint) {
        CYG_ASSERTC(in_endpoint_in_use[0] && out_endpoint_in_use[0]);
        in_endpoint_in_use[0]   = false;
        out_endpoint_in_use[0]  = false;
    } else if (USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN == direction) {
        CYG_ASSERTC(in_endpoint_in_use[endpoint]);
        in_endpoint_in_use[endpoint] = false;
    } else {
        CYG_ASSERTC(out_endpoint_in_use[endpoint]);
        out_endpoint_in_use[endpoint] = false;
    }
}

static cyg_bool
is_endpoint_locked(int endpoint, int direction)
{
    cyg_bool    result = false;
    
    if (0 == endpoint) {
        result = in_endpoint_in_use[0];
    } else if (USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN == direction) {
        result = in_endpoint_in_use[endpoint];
    } else {
        result = out_endpoint_in_use[endpoint];
    }
    return result;
}

// For a given endpoint number, direction and protocol, search through the table 
// supplied by the device driver of all available endpoints. This can be used
// to e.g. get hold of the name of the devtab entry or a pointer to the endpoint
// data structure itself.
static int
lookup_endpoint(int endpoint_number, int direction, int protocol)
{
    int result = -1;
    int i;

    for (i = 0; !USBS_TESTING_ENDPOINTS_IS_TERMINATOR(usbs_testing_endpoints[i]); i++) {

⌨️ 快捷键说明

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