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

📄 usbhost.c

📁 开放源码实时操作系统源码.
💻 C
📖 第 1 页 / 共 5 页
字号:
/*{{{  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", &current_bus, &current_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 + -