?? linux_usbfs.c
字號:
/* * Linux usbfs backend for libusb * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org> * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */#include <config.h>#include <ctype.h>#include <dirent.h>#include <errno.h>#include <fcntl.h>#include <poll.h>#include <pthread.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/ioctl.h>#include <sys/stat.h>#include <sys/types.h>#include <unistd.h>#include "libusb.h"#include "libusbi.h"#include "linux_usbfs.h"/* sysfs vs usbfs: * opening a usbfs node causes the device to be resumed, so we attempt to * avoid this during enumeration. * * sysfs allows us to read the kernel's in-memory copies of device descriptors * and so forth, avoiding the need to open the device: * - The binary "descriptors" file was added in 2.6.23. * - The "busnum" file was added in 2.6.22 * - The "devnum" file has been present since pre-2.6.18 * - the "bConfigurationValue" file has been present since pre-2.6.18 * * If we have bConfigurationValue, busnum, and devnum, then we can determine * the active configuration without having to open the usbfs node in RDWR mode. * We assume this is the case if we see the busnum file (indicates 2.6.22+). * The busnum file is important as that is the only way we can relate sysfs * devices to usbfs nodes. * * If we also have descriptors, we can obtain the device descriptor and active * configuration without touching usbfs at all. * * The descriptors file originally only contained the active configuration * descriptor alongside the device descriptor, but all configurations are * included as of Linux 2.6.26. */static const char *usbfs_path = NULL;/* do we have a busnum to relate devices? this also implies that we can read * the active configuration through bConfigurationValue */static int sysfs_can_relate_devices = -1;/* do we have a descriptors file? */static int sysfs_has_descriptors = -1;struct linux_device_priv { char *sysfs_dir; unsigned char *dev_descriptor; unsigned char *config_descriptor;};struct linux_device_handle_priv { int fd;};enum reap_action { NORMAL = 0, /* submission failed after the first URB, so await cancellation/completion * of all the others */ SUBMIT_FAILED, /* cancelled by user or timeout */ CANCELLED, /* completed multi-URB transfer in non-final URB */ COMPLETED_EARLY,};struct linux_transfer_priv { union { struct usbfs_urb *urbs; struct usbfs_urb **iso_urbs; }; enum reap_action reap_action; int num_urbs; unsigned int awaiting_reap; unsigned int awaiting_discard; /* next iso packet in user-supplied transfer to be populated */ int iso_packet_offset;};static void __get_usbfs_path(struct libusb_device *dev, char *path){ snprintf(path, PATH_MAX, "%s/%03d/%03d", usbfs_path, dev->bus_number, dev->device_address);}static struct linux_device_priv *__device_priv(struct libusb_device *dev){ return (struct linux_device_priv *) dev->os_priv;}static struct linux_device_handle_priv *__device_handle_priv( struct libusb_device_handle *handle){ return (struct linux_device_handle_priv *) handle->os_priv;}static int check_usb_vfs(const char *dirname){ DIR *dir; struct dirent *entry; int found = 0; dir = opendir(dirname); if (!dir) return 0; while ((entry = readdir(dir)) != NULL) { if (entry->d_name[0] == '.') continue; /* We assume if we find any files that it must be the right place */ found = 1; break; } closedir(dir); return found;}static const char *find_usbfs_path(void){ const char *path = "/dev/bus/usb"; const char *ret = NULL; if (check_usb_vfs(path)) { ret = path; } else { path = "/proc/bus/usb"; if (check_usb_vfs(path)) ret = path; } usbi_dbg("found usbfs at %s", ret); return ret;}static int op_init(struct libusb_context *ctx){ struct stat statbuf; int r; usbfs_path = find_usbfs_path(); if (!usbfs_path) { usbi_err(ctx, "could not find usbfs"); return LIBUSB_ERROR_OTHER; } r = stat(SYSFS_DEVICE_PATH, &statbuf); if (r == 0 && S_ISDIR(statbuf.st_mode)) { usbi_dbg("found usb devices in sysfs"); } else { usbi_dbg("sysfs usb info not available"); sysfs_has_descriptors = 0; sysfs_can_relate_devices = 0; } return 0;}static int usbfs_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer){ struct linux_device_priv *priv = __device_priv(dev); /* return cached copy */ memcpy(buffer, priv->dev_descriptor, DEVICE_DESC_LENGTH); return 0;}static int __open_sysfs_attr(struct libusb_device *dev, const char *attr){ struct linux_device_priv *priv = __device_priv(dev); char filename[PATH_MAX]; int fd; snprintf(filename, PATH_MAX, "%s/%s/%s", SYSFS_DEVICE_PATH, priv->sysfs_dir, attr); fd = open(filename, O_RDONLY); if (fd < 0) { usbi_err(DEVICE_CTX(dev), "open %s failed ret=%d errno=%d", filename, fd, errno); return LIBUSB_ERROR_IO; } return fd;}static int sysfs_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer){ int fd; ssize_t r; /* sysfs provides access to an in-memory copy of the device descriptor, * so we use that rather than keeping our own copy */ fd = __open_sysfs_attr(dev, "descriptors"); if (fd < 0) return fd; r = read(fd, buffer, DEVICE_DESC_LENGTH);; close(fd); if (r < 0) { usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d", fd, errno); return LIBUSB_ERROR_IO; } else if (r < DEVICE_DESC_LENGTH) { usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, DEVICE_DESC_LENGTH); return LIBUSB_ERROR_IO; } return 0;}static int op_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian){ if (sysfs_has_descriptors) { return sysfs_get_device_descriptor(dev, buffer); } else { *host_endian = 1; return usbfs_get_device_descriptor(dev, buffer); }}static int usbfs_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len){ struct linux_device_priv *priv = __device_priv(dev); if (!priv->config_descriptor) return LIBUSB_ERROR_NOT_FOUND; /* device is unconfigured */ /* retrieve cached copy */ memcpy(buffer, priv->config_descriptor, len); return 0;}/* read the bConfigurationValue for a device */static int sysfs_get_active_config(struct libusb_device *dev, int *config){ char *endptr; char tmp[4] = {0, 0, 0, 0}; long num; int fd; size_t r; fd = __open_sysfs_attr(dev, "bConfigurationValue"); if (fd < 0) return fd; r = read(fd, tmp, sizeof(tmp)); close(fd); if (r < 0) { usbi_err(DEVICE_CTX(dev), "read bConfigurationValue failed ret=%d errno=%d", r, errno); return LIBUSB_ERROR_IO; } else if (r == 0) { usbi_err(DEVICE_CTX(dev), "device unconfigured"); *config = -1; return 0; } if (tmp[sizeof(tmp) - 1] != 0) { usbi_err(DEVICE_CTX(dev), "not null-terminated?"); return LIBUSB_ERROR_IO; } else if (tmp[0] == 0) { usbi_err(DEVICE_CTX(dev), "no configuration value?"); return LIBUSB_ERROR_IO; } num = strtol(tmp, &endptr, 10); if (endptr == tmp) { usbi_err(DEVICE_CTX(dev), "error converting '%s' to integer", tmp); return LIBUSB_ERROR_IO; } *config = (int) num; return 0;}/* takes a usbfs/descriptors fd seeked to the start of a configuration, and * seeks to the next one. */static int seek_to_next_config(struct libusb_context *ctx, int fd){ struct libusb_config_descriptor config; unsigned char tmp[6]; off_t off; int r; /* read first 6 bytes of descriptor */ r = read(fd, tmp, sizeof(tmp)); if (r < 0) { usbi_err(ctx, "read failed ret=%d errno=%d", r, errno); return LIBUSB_ERROR_IO; } else if (r < sizeof(tmp)) { usbi_err(ctx, "short descriptor read %d/%d", r, sizeof(tmp)); return LIBUSB_ERROR_IO; } /* seek forward to end of config */ usbi_parse_descriptor(tmp, "bbwbb", &config, 1); off = lseek(fd, config.wTotalLength - sizeof(tmp), SEEK_CUR); if (off < 0) { usbi_err(ctx, "seek failed ret=%d errno=%d", off, errno); return LIBUSB_ERROR_IO; } return 0;}static int sysfs_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len){ int fd; ssize_t r; off_t off; int to_copy; int config; unsigned char tmp[6]; r = sysfs_get_active_config(dev, &config); if (r < 0) return r; if (config == -1) return LIBUSB_ERROR_NOT_FOUND; usbi_dbg("active configuration %d", config); /* sysfs provides access to an in-memory copy of the device descriptor, * so we use that rather than keeping our own copy */ fd = __open_sysfs_attr(dev, "descriptors"); if (fd < 0) return fd; /* device might have been unconfigured since we read bConfigurationValue, * so first check that there is any config descriptor data at all... */ off = lseek(fd, 0, SEEK_END); if (off < 1) { usbi_err(DEVICE_CTX(dev), "end seek failed, ret=%d errno=%d", off, errno); close(fd); return LIBUSB_ERROR_IO; } else if (off == DEVICE_DESC_LENGTH) { close(fd); return LIBUSB_ERROR_NOT_FOUND; } off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET); if (off < 0) { usbi_err(DEVICE_CTX(dev), "seek failed, ret=%d errno=%d", off, errno); close(fd); return LIBUSB_ERROR_IO; } /* unbounded loop: we expect the descriptor to be present under all * circumstances */ while (1) { r = read(fd, tmp, sizeof(tmp)); if (r < 0) { usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d", fd, errno); return LIBUSB_ERROR_IO; } else if (r < sizeof(tmp)) { usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, sizeof(tmp)); return LIBUSB_ERROR_IO; } /* check bConfigurationValue */ if (tmp[5] == config) break; /* try the next descriptor */ off = lseek(fd, 0 - sizeof(tmp), SEEK_CUR); if (off < 0) return LIBUSB_ERROR_IO; r = seek_to_next_config(DEVICE_CTX(dev), fd); if (r < 0) return r; } to_copy = (len < sizeof(tmp)) ? len : sizeof(tmp); memcpy(buffer, tmp, to_copy); if (len > sizeof(tmp)) { r = read(fd, buffer + sizeof(tmp), len - sizeof(tmp)); if (r < 0) { usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d", fd, errno); r = LIBUSB_ERROR_IO; } else if (r == 0) { usbi_dbg("device is unconfigured"); r = LIBUSB_ERROR_NOT_FOUND; } else if (r < len - sizeof(tmp)) { usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, len); r = LIBUSB_ERROR_IO; } } else { r = 0; } close(fd); return r;}static int op_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian){ if (sysfs_has_descriptors) { return sysfs_get_active_config_descriptor(dev, buffer, len); } else { *host_endian = 1; return usbfs_get_active_config_descriptor(dev, buffer, len); }}/* takes a usbfs fd, attempts to find the requested config and copy a certain * amount of it into an output buffer. */static int get_config_descriptor(struct libusb_context *ctx, int fd, uint8_t config_index, unsigned char *buffer, size_t len){ off_t off; ssize_t r; off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET); if (off < 0) { usbi_err(ctx, "seek failed ret=%d errno=%d", off, errno); return LIBUSB_ERROR_IO; } /* might need to skip some configuration descriptors to reach the * requested configuration */ while (config_index > 0) { r = seek_to_next_config(ctx, fd); if (r < 0) return r; config_index--; } /* read the rest of the descriptor */ r = read(fd, buffer, len); if (r < 0) { usbi_err(ctx, "read failed ret=%d errno=%d", r, errno); return LIBUSB_ERROR_IO; } else if (r < len) { usbi_err(ctx, "short output read %d/%d", r, len); return LIBUSB_ERROR_IO; } return 0;}static int op_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian){ char filename[PATH_MAX]; int fd; int r; /* always read from usbfs: sysfs only has the active descriptor * this will involve waking the device up, but oh well! */ /* FIXME: the above is no longer true, new kernels have all descriptors * in the descriptors file. but its kinda hard to detect if the kernel * is sufficiently new. */ __get_usbfs_path(dev, filename); fd = open(filename, O_RDONLY); if (fd < 0) { usbi_err(DEVICE_CTX(dev), "open '%s' failed, ret=%d errno=%d", filename, fd, errno); return LIBUSB_ERROR_IO;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -