📄 usbhost.c
字号:
/*{{{ Banner *///=================================================================//// host.c//// USB testing - host-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####//// 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"/*}}}*//*{{{ 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 levelstatic 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 intusb_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. do { ch = getc(devs_file); } while ((EOF != ch) && ('\n' != ch)); 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 intusb_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); } 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 intusb_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); if (length > USBTEST_MAX_CONTROL_DATA) { fprintf(stderr, "usbhost: internal error, control message involves too much data.\n"); exit(EXIT_FAILURE); }#if 1 // Workaround - send additional data in the index and length fields. if ((length > 0) && (USB_DIR_OUT == (USB_ENDPOINT_DIR_MASK & request_type))) { int i; unsigned char* buf = (unsigned char*) data; for (i = 0; i < length; i+= 4) { int this_len = length - 1; int ioctl_result; transfer.requesttype = USB_TYPE_CLASS | USB_RECIP_DEVICE; if (this_len > 4) { this_len = 4; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -