📄 usbtarget.c
字号:
/*{{{ 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 + -