📄 usbhost.c
字号:
/*{{{ Banner */
//=================================================================
//
// host.c
//
// USB testing - host-side
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 2005 eCosCentric Ltd.
// 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####
//
// Author(s): bartv
// Date: 2001-07-04
//####DESCRIPTIONEND####
//==========================================================================
// The overall architecture is as follows.
//
// The target hardware runs a special application which provides a
// particular type of USB application, "Red Hat eCos USB testing".
// This will not be recognised by any device driver, so the Linux
// kernel will pretty much ignore the device (other host OS's are not
// considered at this time).
//
// This program is the only supported way to interact with that service.
// It acts as an extended Tcl interpreter, providing a number of new
// Tcl commands for interacting with the target. All test cases can
// then be written as Tcl scripts which invoke a series of these commands.
// These Tcl commands operate essentially though the LINUX usb devfs
// service which allows ordinary application code to perform USB operations
// via ioctl()'s.
/*}}}*/
/*{{{ #include's */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
// Avoid compatibility problems with Tcl 8.4 vs. earlier
#define USE_NON_CONST
#include <tcl.h>
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
#include "../tests/protocol.h"
#include "config.h"
/*}}}*/
/*{{{ Backwards compatibility */
// The header file <linux/usbdevice_fs.h> has changed in an incompatible
// way. This is detected by autoconf
#ifndef CYGBLD_USE_NEW_FIELD_NAMES
# define bRequestType requesttype
# define bRequest request
# define wValue value
# define wIndex index
# define wLength length
#endif
/*}}}*/
/*{{{ Statics */
// ----------------------------------------------------------------------------
// Statics.
// Has the current batch of tests actually terminated? 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;
// The next local thread to be allocated for testing. This variable can also
// be used to find out how many threads are involved in the current test.
// This counter should always be reset to 0 at the end of every test run.
static int local_thread_count = 0;
// A similar counter for remote threads.
static int remote_thread_count = 0;
// A file handle for manipulating the USB device at a low level
static int usb_master_fd = -1;
/*}}}*/
/*{{{ Logging */
// ----------------------------------------------------------------------------
// The user can provide one or more -V/--verbose arguments to increase
// the amount of output generated.
static int verbose = 0;
#define VERBOSE(_level_, _format_, _args_...) \
do { \
if (verbose >= _level_) { \
printf(_format_, ## _args_); \
} \
} while (0);
/*}}}*/
/*{{{ Low-level USB access */
// ----------------------------------------------------------------------------
// Low-level access to a USB device.
//
// The various ioctl() calls require a file handle which corresponds to one
// of the /proc/bus/usb/<abc>/<def> entries. <abc> is a bus number,
// typically 001 or 001, and <def> is a device number on that bus,
// e.g. 003. Figuring out <abc> and <def> requires scanning
// /proc/bus/usb/devices, which is a somewhat complicated text file.
//
// This is all somewhat vulnerable to incompatible changes in the
// Linux kernel, specifically the implementation of the /proc/bus/usb.
// An alternative approach would be to write a new Linux device driver
// and interact with that, but that approach is vulnerable to any
// internal kernel API changes affecting USB device drivers.
// How to access USB devices from userland
#define USB_ROOT "/proc/bus/usb/"
// How to identify the eCos test case
#define PRODUCT_STRING "Red Hat eCos USB test"
// Scan through /proc/bus/usb/devices looking for an entry that
// matches what we are after, specifically a line
// S: Product=Red Hat eCos USB testcase
// The required information can then be obtained from the previous
// line:
// T: Bus=<abc> ... Dev#= <def> ...
//
// Of course the T: line is going to come first, so it is necessary
// to keep track of the current bus and device numbers.
//
// Note: this code is duplicated in usbchmod.c. Any changes here
// should be propagated. For now the routine is too small to warrant
// a separate source file.
static int
usb_scan_devices(int* bus, int* dev)
{
FILE* devs_file;
int current_bus = -1;
int current_dev = -1;
int ch;
*bus = -1;
*dev = -1;
VERBOSE(1, "Searching " USB_ROOT "devices for the eCos USB test code\n");
devs_file = fopen(USB_ROOT "devices", "r");
if (NULL == devs_file) {
fprintf(stderr, "usbhost: error, unable to access " USB_ROOT "devices\n");
return 0;
}
ch = getc(devs_file);
while (EOF != ch) {
if ('T' == ch) {
if (2 !=fscanf(devs_file, ": Bus=%d %*[^D\n]Dev#=%d", ¤t_bus, ¤t_dev)) {
current_bus = -1;
current_dev = -1;
}
} else if ('S' == ch) {
int start = 0, end = 0;
if (EOF != fscanf(devs_file, ": Product=%n" PRODUCT_STRING "%n", &start, &end)) {
if (start < end) {
*bus = current_bus;
*dev = current_dev;
break;
}
}
}
// Move to the end of the current line.
while ((EOF != ch) && ('\n' != ch)) {
ch = getc(devs_file);
}
if (EOF != ch) {
ch = getc(devs_file);
}
}
fclose(devs_file);
if ((-1 != *bus) && (-1 != *dev)) {
VERBOSE(1, "Found eCos USB test code on bus %d, device %d\n", *bus, *dev);
return 1;
}
fprintf(stderr, "usbhost: error, failed to find a USB device \"" PRODUCT_STRING "\"\n");
return 0;
}
// Actually open the USB device, allowing subsequent ioctl() operations.
//
// Typically /proc/bus/usb/... will not allow ordinary applications
// to perform ioctl()'s. Instead root privileges are required. To work
// around this there is a little utility usbchmod, installed suid,
// which can be used to get access to the raw device.
static int
usb_open_device(void)
{
char devname[_POSIX_PATH_MAX];
static int bus = -1;
static int dev = -1;
int result;
if ((-1 == bus) || (-1 == dev)) {
if (!usb_scan_devices(&bus, &dev)) {
return -1;
}
}
if (_POSIX_PATH_MAX == snprintf(devname, _POSIX_PATH_MAX, USB_ROOT "%03d/%03d", bus, dev)) {
fprintf(stderr, "usbhost: internal error, buffer overflow\n");
exit(EXIT_FAILURE);
}
VERBOSE(1, "Attempting to access USB target via %s\n", devname);
result = open(devname, O_RDWR);
if (-1 == result) {
// Check for access right problems. If so, try to work around them
// by invoking usbchmod. Always look for this in the install tree,
// since it is only that version which is likely to have been
// chown'ed and chmod'ed to be suid root.
if (EACCES == errno) {
char command_name[_POSIX_PATH_MAX];
VERBOSE(1, "Insufficient access to USB target, running usbchmod\n");
if (_POSIX_PATH_MAX == snprintf(command_name, _POSIX_PATH_MAX, "%s/usbchmod %d %d", USBAUXDIR, bus, dev)) {
fprintf(stderr, "usbhost: internal error, buffer overflow\n");
exit(EXIT_FAILURE);
}
(void) system(command_name);
result = open(devname, O_RDWR);
}
}
if (-1 == result) {
fprintf(stderr, "usbhost: error, failed to open \"%s\", errno %d\n", devname, errno);
exit(EXIT_FAILURE);
}
VERBOSE(1, "USB device now accessible via file descriptor %d\n", result);
// Also perform a set-configuration call, to avoid warnings from
// the Linux kernel. Target-side testing is always configuration 1
// because only a single configuration is supported.
(void) ioctl(result, USBDEVFS_SETCONFIGURATION, 1);
return result;
}
// Exchange a control message with the host. The return value should
// be 0, or a small positive number indicating the actual number of
// bytes received which may be less than requested.
//
// There appear to be problems with some hosts, manifesting itself as
// an inability to send control messages that involve additional data
// from host->target. These problems are not yet well-understood. For
// now the workaround is to send multiple packets, each with up to
// four bytes encoded in the index and length fields.
static int
usb_control_message(int fd, int request_type, int request, int value, int index, int length, void* data)
{
struct usbdevfs_ctrltransfer transfer;
int result = 0;
VERBOSE(3, "usb_control_message, request %02x, len %d\n", request, length);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -