Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
DISCONTINUED:openSUSE:11.1:Update
usbutils
usbutils.libusb0-1.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File usbutils.libusb0-1.patch of Package usbutils
--- Makefile.am | 5 configure.in | 2 descriptors.c | 520 +++++++++++++++++++++++++++++++++++++++++ error.c | 36 ++ error.h | 31 ++ linux.c | 733 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ linux.h | 119 +++++++++ usb.c | 307 ++++++++++++++++++++++++ usb.h | 338 ++++++++++++++++++++++++++ usbi.h | 74 +++++ 10 files changed, 2163 insertions(+), 2 deletions(-) --- a/Makefile.am +++ b/Makefile.am @@ -7,6 +7,11 @@ AM_CFLAGS = -Wall -W -Wunused sbin_PROGRAMS = lsusb lsusb_SOURCES = lsusb.c \ + usbi.h \ + usb.c usb.h \ + error.c error.h \ + linux.c linux.h \ + descriptors.c \ lsusb-t.c \ names.c names.h \ devtree.c devtree.h list.h \ --- a/configure.in +++ b/configure.in @@ -31,8 +31,6 @@ AC_TYPE_SIZE_T AC_FUNC_MALLOC AC_FUNC_VPRINTF AC_CHECK_FUNCS([getcwd memset getopt_long strchr strerror strstr strtoul uname]) -AC_CHECK_LIB(usb, usb_get_string_simple, , - [AC_MSG_ERROR(get libusb 0.1.8 or newer)]) dnl zlib on enabled by default (if found) --- /dev/null +++ b/descriptors.c @@ -0,0 +1,520 @@ +/* + * Parses descriptors + * + * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * + * This library is covered by the LGPL, read LICENSE for details. + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include "usbi.h" + +int usb_get_descriptor_by_endpoint(usb_dev_handle *udev, int ep, + unsigned char type, unsigned char index, void *buf, int size) +{ + memset(buf, 0, size); + + return usb_control_msg(udev, ep | USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, + (type << 8) + index, 0, buf, size, 1000); +} + +int usb_get_descriptor(usb_dev_handle *udev, unsigned char type, + unsigned char index, void *buf, int size) +{ + memset(buf, 0, size); + + return usb_control_msg(udev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, + (type << 8) + index, 0, buf, size, 1000); +} + +int usb_parse_descriptor(unsigned char *source, char *description, void *dest) +{ + unsigned char *sp = source, *dp = dest; + uint16_t w; + uint32_t d; + char *cp; + + for (cp = description; *cp; cp++) { + switch (*cp) { + case 'b': /* 8-bit byte */ + *dp++ = *sp++; + break; + case 'w': /* 16-bit word, convert from little endian to CPU */ + w = (sp[1] << 8) | sp[0]; sp += 2; + dp += ((unsigned long)dp & 1); /* Align to word boundary */ + *((uint16_t *)dp) = w; dp += 2; + break; + case 'd': /* 32-bit dword, convert from little endian to CPU */ + d = (sp[3] << 24) | (sp[2] << 16) | (sp[1] << 8) | sp[0]; sp += 4; + dp += ((unsigned long)dp & 2); /* Align to dword boundary */ + *((uint32_t *)dp) = d; dp += 4; + break; + /* These two characters are undocumented and just a hack for Linux */ + case 'W': /* 16-bit word, keep CPU endianess */ + dp += ((unsigned long)dp & 1); /* Align to word boundary */ + memcpy(dp, sp, 2); sp += 2; dp += 2; + break; + case 'D': /* 32-bit dword, keep CPU endianess */ + dp += ((unsigned long)dp & 2); /* Align to dword boundary */ + memcpy(dp, sp, 4); sp += 4; dp += 4; + break; + } + } + + return sp - source; +} + +/* + * This code looks surprisingly similar to the code I wrote for the Linux + * kernel. It's not a coincidence :) + */ + +static int usb_parse_endpoint(struct usb_endpoint_descriptor *endpoint, unsigned char *buffer, int size) +{ + struct usb_descriptor_header header; + unsigned char *begin; + int parsed = 0, len, numskipped; + + usb_parse_descriptor(buffer, "bb", &header); + + /* Everything should be fine being passed into here, but we sanity */ + /* check JIC */ + if (header.bLength > size) { + if (usb_debug >= 1) + fprintf(stderr, "ran out of descriptors parsing\n"); + return -1; + } + + if (header.bDescriptorType != USB_DT_ENDPOINT) { + if (usb_debug >= 2) + fprintf(stderr, "unexpected descriptor 0x%X, expecting endpoint descriptor, type 0x%X\n", + header.bDescriptorType, USB_DT_ENDPOINT); + return parsed; + } + + if (header.bLength >= ENDPOINT_AUDIO_DESC_LENGTH) + usb_parse_descriptor(buffer, "bbbbwbbb", endpoint); + else if (header.bLength >= ENDPOINT_DESC_LENGTH) + usb_parse_descriptor(buffer, "bbbbwb", endpoint); + + buffer += header.bLength; + size -= header.bLength; + parsed += header.bLength; + + /* Skip over the rest of the Class Specific or Vendor Specific */ + /* descriptors */ + begin = buffer; + numskipped = 0; + while (size >= DESC_HEADER_LENGTH) { + usb_parse_descriptor(buffer, "bb", &header); + + if (header.bLength < 2) { + if (usb_debug >= 1) + fprintf(stderr, "invalid descriptor length of %d\n", header.bLength); + return -1; + } + + /* If we find another "proper" descriptor then we're done */ + if ((header.bDescriptorType == USB_DT_ENDPOINT) || + (header.bDescriptorType == USB_DT_INTERFACE) || + (header.bDescriptorType == USB_DT_CONFIG) || + (header.bDescriptorType == USB_DT_DEVICE)) + break; + + if (usb_debug >= 1) + fprintf(stderr, "skipping descriptor 0x%X\n", header.bDescriptorType); + numskipped++; + + buffer += header.bLength; + size -= header.bLength; + parsed += header.bLength; + } + + if (numskipped && usb_debug >= 2) + fprintf(stderr, "skipped %d class/vendor specific endpoint descriptors\n", numskipped); + + /* Copy any unknown descriptors into a storage area for drivers */ + /* to later parse */ + len = (int)(buffer - begin); + if (!len) { + endpoint->extra = NULL; + endpoint->extralen = 0; + return parsed; + } + + endpoint->extra = malloc(len); + if (!endpoint->extra) { + if (usb_debug >= 1) + fprintf(stderr, "couldn't allocate memory for endpoint extra descriptors\n"); + endpoint->extralen = 0; + return parsed; + } + + memcpy(endpoint->extra, begin, len); + endpoint->extralen = len; + + return parsed; +} + +static int usb_parse_interface(struct usb_interface *interface, + unsigned char *buffer, int size) +{ + int i, len, numskipped, retval, parsed = 0; + struct usb_descriptor_header header; + struct usb_interface_descriptor *ifp; + unsigned char *begin; + + interface->num_altsetting = 0; + + while (size >= INTERFACE_DESC_LENGTH) { + interface->altsetting = realloc(interface->altsetting, sizeof(struct usb_interface_descriptor) * (interface->num_altsetting + 1)); + if (!interface->altsetting) { + if (usb_debug >= 1) + fprintf(stderr, "couldn't malloc interface->altsetting\n"); + return -1; + } + + ifp = interface->altsetting + interface->num_altsetting; + interface->num_altsetting++; + + usb_parse_descriptor(buffer, "bbbbbbbbb", ifp); + + /* Skip over the interface */ + buffer += ifp->bLength; + parsed += ifp->bLength; + size -= ifp->bLength; + + begin = buffer; + numskipped = 0; + + /* Skip over any interface, class or vendor descriptors */ + while (size >= DESC_HEADER_LENGTH) { + usb_parse_descriptor(buffer, "bb", &header); + + if (header.bLength < 2) { + if (usb_debug >= 1) + fprintf(stderr, "invalid descriptor length of %d\n", header.bLength); + return -1; + } + + /* If we find another "proper" descriptor then we're done */ + if ((header.bDescriptorType == USB_DT_INTERFACE) || + (header.bDescriptorType == USB_DT_ENDPOINT) || + (header.bDescriptorType == USB_DT_CONFIG) || + (header.bDescriptorType == USB_DT_DEVICE)) + break; + + numskipped++; + + buffer += header.bLength; + parsed += header.bLength; + size -= header.bLength; + } + + if (numskipped && usb_debug >= 2) + fprintf(stderr, "skipped %d class/vendor specific interface descriptors\n", numskipped); + + /* Copy any unknown descriptors into a storage area for */ + /* drivers to later parse */ + len = (int)(buffer - begin); + if (!len) { + ifp->extra = NULL; + ifp->extralen = 0; + } else { + ifp->extra = malloc(len); + if (!ifp->extra) { + if (usb_debug >= 1) + fprintf(stderr, "couldn't allocate memory for interface extra descriptors\n"); + ifp->extralen = 0; + return -1; + } + memcpy(ifp->extra, begin, len); + ifp->extralen = len; + } + + /* Did we hit an unexpected descriptor? */ + usb_parse_descriptor(buffer, "bb", &header); + if ((size >= DESC_HEADER_LENGTH) && + ((header.bDescriptorType == USB_DT_CONFIG) || + (header.bDescriptorType == USB_DT_DEVICE))) + return parsed; + + if (ifp->bNumEndpoints > USB_MAXENDPOINTS) { + if (usb_debug >= 1) + fprintf(stderr, "too many endpoints\n"); + return -1; + } + + if (ifp->bNumEndpoints > 0) { + ifp->endpoint = (struct usb_endpoint_descriptor *) + malloc(ifp->bNumEndpoints * + sizeof(struct usb_endpoint_descriptor)); + if (!ifp->endpoint) { + if (usb_debug >= 1) + fprintf(stderr, "couldn't allocate memory for ifp->endpoint\n"); + return -1; + } + + memset(ifp->endpoint, 0, ifp->bNumEndpoints * + sizeof(struct usb_endpoint_descriptor)); + + for (i = 0; i < ifp->bNumEndpoints; i++) { + usb_parse_descriptor(buffer, "bb", &header); + + if (header.bLength > size) { + if (usb_debug >= 1) + fprintf(stderr, "ran out of descriptors parsing\n"); + return -1; + } + + retval = usb_parse_endpoint(ifp->endpoint + i, buffer, size); + if (retval < 0) + return retval; + + buffer += retval; + parsed += retval; + size -= retval; + } + } else + ifp->endpoint = NULL; + + /* We check to see if it's an alternate to this one */ + ifp = (struct usb_interface_descriptor *)buffer; + if (size < USB_DT_INTERFACE_SIZE || + ifp->bDescriptorType != USB_DT_INTERFACE || + !ifp->bAlternateSetting) + return parsed; + } + + return parsed; +} + +int usb_parse_configuration(struct usb_config_descriptor *config, + unsigned char *buffer) +{ + int i, retval, size; + struct usb_descriptor_header header; + + usb_parse_descriptor(buffer, "bbwbbbbb", config); + size = config->wTotalLength; + + if (config->bNumInterfaces > USB_MAXINTERFACES) { + if (usb_debug >= 1) + fprintf(stderr, "too many interfaces\n"); + return -1; + } + + config->interface = (struct usb_interface *) + malloc(config->bNumInterfaces * + sizeof(struct usb_interface)); + if (!config->interface) { + if (usb_debug >= 1) + fprintf(stderr, "out of memory\n"); + return -1; + } + + memset(config->interface, 0, config->bNumInterfaces * sizeof(struct usb_interface)); + + buffer += config->bLength; + size -= config->bLength; + + config->extra = NULL; + config->extralen = 0; + + for (i = 0; i < config->bNumInterfaces; i++) { + int numskipped, len; + unsigned char *begin; + + /* Skip over the rest of the Class Specific or Vendor */ + /* Specific descriptors */ + begin = buffer; + numskipped = 0; + while (size >= DESC_HEADER_LENGTH) { + usb_parse_descriptor(buffer, "bb", &header); + + if ((header.bLength > size) || (header.bLength < DESC_HEADER_LENGTH)) { + if (usb_debug >= 1) + fprintf(stderr, "invalid descriptor length of %d\n", header.bLength); + return -1; + } + + /* If we find another "proper" descriptor then we're done */ + if ((header.bDescriptorType == USB_DT_ENDPOINT) || + (header.bDescriptorType == USB_DT_INTERFACE) || + (header.bDescriptorType == USB_DT_CONFIG) || + (header.bDescriptorType == USB_DT_DEVICE)) + break; + + if (usb_debug >= 2) + fprintf(stderr, "skipping descriptor 0x%X\n", header.bDescriptorType); + numskipped++; + + buffer += header.bLength; + size -= header.bLength; + } + + if (numskipped && usb_debug >= 2) + fprintf(stderr, "skipped %d class/vendor specific endpoint descriptors\n", numskipped); + + /* Copy any unknown descriptors into a storage area for */ + /* drivers to later parse */ + len = (int)(buffer - begin); + if (len) { + /* FIXME: We should realloc and append here */ + if (!config->extralen) { + config->extra = malloc(len); + if (!config->extra) { + if (usb_debug >= 1) + fprintf(stderr, "couldn't allocate memory for config extra descriptors\n"); + config->extralen = 0; + return -1; + } + + memcpy(config->extra, begin, len); + config->extralen = len; + } + } + + retval = usb_parse_interface(config->interface + i, buffer, size); + if (retval < 0) + return retval; + + buffer += retval; + size -= retval; + } + + return size; +} + +void usb_destroy_configuration(struct usb_device *dev) +{ + int c, i, j, k; + + if (!dev->config) + return; + + for (c = 0; c < dev->descriptor.bNumConfigurations; c++) { + struct usb_config_descriptor *cf = &dev->config[c]; + + if (!cf->interface) + continue; + + for (i = 0; i < cf->bNumInterfaces; i++) { + struct usb_interface *ifp = &cf->interface[i]; + + if (!ifp->altsetting) + continue; + + for (j = 0; j < ifp->num_altsetting; j++) { + struct usb_interface_descriptor *as = &ifp->altsetting[j]; + + if (as->extra) + free(as->extra); + + if (!as->endpoint) + continue; + + for (k = 0; k < as->bNumEndpoints; k++) { + if (as->endpoint[k].extra) + free(as->endpoint[k].extra); + } + free(as->endpoint); + } + + free(ifp->altsetting); + } + + free(cf->interface); + } + + free(dev->config); +} + +void usb_fetch_and_parse_descriptors(usb_dev_handle *udev) +{ + struct usb_device *dev = udev->device; + int i; + + if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) { + if (usb_debug >= 1) + fprintf(stderr, "Too many configurations (%d > %d)\n", dev->descriptor.bNumConfigurations, USB_MAXCONFIG); + return; + } + + if (dev->descriptor.bNumConfigurations < 1) { + if (usb_debug >= 1) + fprintf(stderr, "Not enough configurations (%d < %d)\n", dev->descriptor.bNumConfigurations, 1); + return; + } + + dev->config = (struct usb_config_descriptor *)malloc(dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor)); + if (!dev->config) { + if (usb_debug >= 1) + fprintf(stderr, "Unable to allocate memory for config descriptor\n"); + return; + } + + memset(dev->config, 0, dev->descriptor.bNumConfigurations * + sizeof(struct usb_config_descriptor)); + + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + unsigned char buffer[8], *bigbuffer; + struct usb_config_descriptor config; + int res; + + /* Get the first 8 bytes so we can figure out what the total length is */ + res = usb_get_descriptor(udev, USB_DT_CONFIG, i, buffer, 8); + if (res < 8) { + if (usb_debug >= 1) { + if (res < 0) + fprintf(stderr, "Unable to get descriptor (%d)\n", res); + else + fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", 8, res); + } + + goto err; + } + + usb_parse_descriptor(buffer, "bbw", &config); + + bigbuffer = malloc(config.wTotalLength); + if (!bigbuffer) { + if (usb_debug >= 1) + fprintf(stderr, "Unable to allocate memory for descriptors\n"); + goto err; + } + + res = usb_get_descriptor(udev, USB_DT_CONFIG, i, bigbuffer, config.wTotalLength); + if (res < config.wTotalLength) { + if (usb_debug >= 1) { + if (res < 0) + fprintf(stderr, "Unable to get descriptor (%d)\n", res); + else + fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", config.wTotalLength, res); + } + + free(bigbuffer); + goto err; + } + + res = usb_parse_configuration(&dev->config[i], bigbuffer); + if (usb_debug >= 2) { + if (res > 0) + fprintf(stderr, "Descriptor data still left\n"); + else if (res < 0) + fprintf(stderr, "Unable to parse descriptors\n"); + } + + free(bigbuffer); + } + + return; + +err: + free(dev->config); + + dev->config = NULL; +} + --- /dev/null +++ b/error.c @@ -0,0 +1,36 @@ +/* + * USB Error messages + * + * Copyright (c) 2000-2001 Johannes Erdfelt <johannes@erdfelt.com> + * + * This library is covered by the LGPL, read LICENSE for details. + */ + +#include <errno.h> +#include <string.h> + +#include "usb.h" +#include "error.h" + +char usb_error_str[1024] = ""; +int usb_error_errno = 0; +usb_error_type_t usb_error_type = USB_ERROR_TYPE_NONE; + +char *usb_strerror(void) +{ + switch (usb_error_type) { + case USB_ERROR_TYPE_NONE: + return "No error"; + case USB_ERROR_TYPE_STRING: + return usb_error_str; + case USB_ERROR_TYPE_ERRNO: + if (usb_error_errno > -USB_ERROR_BEGIN) + return strerror(usb_error_errno); + else + /* Any error we don't know falls under here */ + return "Unknown error"; + } + + return "Unknown error"; +} + --- /dev/null +++ b/error.h @@ -0,0 +1,31 @@ +#ifndef _ERROR_H_ +#define _ERROR_H_ + +typedef enum { + USB_ERROR_TYPE_NONE = 0, + USB_ERROR_TYPE_STRING, + USB_ERROR_TYPE_ERRNO, +} usb_error_type_t; + +extern char usb_error_str[1024]; +extern int usb_error_errno; +extern usb_error_type_t usb_error_type; + +#define USB_ERROR(x) \ + do { \ + usb_error_type = USB_ERROR_TYPE_ERRNO; \ + usb_error_errno = x; \ + return x; \ + } while (0) + +#define USB_ERROR_STR(x, format, args...) \ + do { \ + usb_error_type = USB_ERROR_TYPE_STRING; \ + snprintf(usb_error_str, sizeof(usb_error_str) - 1, format, ## args); \ + if (usb_debug >= 2) \ + fprintf(stderr, "USB error: %s\n", usb_error_str); \ + return x; \ + } while (0) + +#endif /* _ERROR_H_ */ + --- /dev/null +++ b/linux.c @@ -0,0 +1,733 @@ +/* + * Linux USB support + * + * Copyright (c) 2000-2003 Johannes Erdfelt <johannes@erdfelt.com> + * + * This library is covered by the LGPL, read LICENSE for details. + */ + +#include <stdlib.h> /* getenv, etc */ +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/time.h> +#include <dirent.h> + +#include "linux.h" +#include "usbi.h" + +static char usb_path[PATH_MAX + 1] = ""; + +static int device_open(struct usb_device *dev) +{ + char filename[PATH_MAX + 1]; + int fd; + + snprintf(filename, sizeof(filename) - 1, "%s/%s/%s", + usb_path, dev->bus->dirname, dev->filename); + + fd = open(filename, O_RDWR); + if (fd < 0) { + fd = open(filename, O_RDONLY); + if (fd < 0) + USB_ERROR_STR(-errno, "failed to open %s: %s", + filename, strerror(errno)); + } + + return fd; +} + +int usb_os_open(usb_dev_handle *dev) +{ + dev->fd = device_open(dev->device); + + return 0; +} + +int usb_os_close(usb_dev_handle *dev) +{ + if (dev->fd < 0) + return 0; + + if (close(dev->fd) == -1) + /* Failing trying to close a file really isn't an error, so return 0 */ + USB_ERROR_STR(0, "tried to close device fd %d: %s", dev->fd, + strerror(errno)); + + return 0; +} + +int usb_set_configuration(usb_dev_handle *dev, int configuration) +{ + int ret; + + ret = ioctl(dev->fd, IOCTL_USB_SETCONFIG, &configuration); + if (ret < 0) + USB_ERROR_STR(-errno, "could not set config %d: %s", configuration, + strerror(errno)); + + dev->config = configuration; + + return 0; +} + +int usb_claim_interface(usb_dev_handle *dev, int interface) +{ + int ret; + + ret = ioctl(dev->fd, IOCTL_USB_CLAIMINTF, &interface); + if (ret < 0) { + if (errno == EBUSY && usb_debug > 0) + fprintf(stderr, "Check that you have permissions to write to %s/%s and, if you don't, that you set up hotplug (http://linux-hotplug.sourceforge.net/) correctly.\n", dev->bus->dirname, dev->device->filename); + + USB_ERROR_STR(-errno, "could not claim interface %d: %s", interface, + strerror(errno)); + } + + dev->interface = interface; + + return 0; +} + +int usb_release_interface(usb_dev_handle *dev, int interface) +{ + int ret; + + ret = ioctl(dev->fd, IOCTL_USB_RELEASEINTF, &interface); + if (ret < 0) + USB_ERROR_STR(-errno, "could not release intf %d: %s", interface, + strerror(errno)); + + dev->interface = -1; + + return 0; +} + +int usb_set_altinterface(usb_dev_handle *dev, int alternate) +{ + int ret; + struct usb_setinterface setintf; + + if (dev->interface < 0) + USB_ERROR(-EINVAL); + + setintf.interface = dev->interface; + setintf.altsetting = alternate; + + ret = ioctl(dev->fd, IOCTL_USB_SETINTF, &setintf); + if (ret < 0) + USB_ERROR_STR(-errno, "could not set alt intf %d/%d: %s", + dev->interface, alternate, strerror(errno)); + + dev->altsetting = alternate; + + return 0; +} + +/* + * Linux usbfs has a limit of one page size for synchronous bulk read/write. + * 4096 is the most portable maximum we can do for now. + * Linux usbfs has a limit of 16KB for the URB interface. We use this now + * to get better performance for USB 2.0 devices. + */ +#define MAX_READ_WRITE (16 * 1024) + +int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, + int value, int index, char *bytes, int size, int timeout) +{ + struct usb_ctrltransfer ctrl; + int ret; + + ctrl.bRequestType = requesttype; + ctrl.bRequest = request; + ctrl.wValue = value; + ctrl.wIndex = index; + ctrl.wLength = size; + + ctrl.data = bytes; + ctrl.timeout = timeout; + + ret = ioctl(dev->fd, IOCTL_USB_CONTROL, &ctrl); + if (ret < 0) + USB_ERROR_STR(-errno, "error sending control message: %s", strerror(errno)); + + return ret; +} + +#define URB_USERCONTEXT_COOKIE ((void *)0x1) + +/* Reading and writing are the same except for the endpoint */ +static int usb_urb_transfer(usb_dev_handle *dev, int ep, int urbtype, + char *bytes, int size, int timeout) +{ + struct usb_urb urb; + int bytesdone = 0, requested; + struct timeval tv, tv_ref, tv_now; + struct usb_urb *context; + int ret, waiting; + + /* + * HACK: The use of urb.usercontext is a hack to get threaded applications + * sort of working again. Threaded support is still not recommended, but + * this should allow applications to work in the common cases. Basically, + * if we get the completion for an URB we're not waiting for, then we update + * the usercontext pointer to 1 for the other threads URB and it will see + * the change after it wakes up from the the timeout. Ugly, but it works. + */ + + /* + * Get actual time, and add the timeout value. The result is the absolute + * time where we have to quit waiting for an message. + */ + gettimeofday(&tv_ref, NULL); + tv_ref.tv_sec = tv_ref.tv_sec + timeout / 1000; + tv_ref.tv_usec = tv_ref.tv_usec + (timeout % 1000) * 1000; + + if (tv_ref.tv_usec > 1000000) { + tv_ref.tv_usec -= 1000000; + tv_ref.tv_sec++; + } + + do { + fd_set writefds; + + requested = size - bytesdone; + if (requested > MAX_READ_WRITE) + requested = MAX_READ_WRITE; + + urb.type = urbtype; + urb.endpoint = ep; + urb.flags = 0; + urb.buffer = bytes + bytesdone; + urb.buffer_length = requested; + urb.signr = 0; + urb.actual_length = 0; + urb.number_of_packets = 0; /* don't do isochronous yet */ + urb.usercontext = NULL; + + ret = ioctl(dev->fd, IOCTL_USB_SUBMITURB, &urb); + if (ret < 0) { + USB_ERROR_STR(-errno, "error submitting URB: %s", strerror(errno)); + return ret; + } + + FD_ZERO(&writefds); + FD_SET(dev->fd, &writefds); + +restart: + waiting = 1; + context = NULL; + while (!urb.usercontext && ((ret = ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context)) == -1) && waiting) { + tv.tv_sec = 0; + tv.tv_usec = 1000; // 1 msec + select(dev->fd + 1, NULL, &writefds, NULL, &tv); //sub second wait + + if (timeout) { + /* compare with actual time, as the select timeout is not that precise */ + gettimeofday(&tv_now, NULL); + + if ((tv_now.tv_sec > tv_ref.tv_sec) || + ((tv_now.tv_sec == tv_ref.tv_sec) && (tv_now.tv_usec >= tv_ref.tv_usec))) + waiting = 0; + } + } + + if (context && context != &urb) { + context->usercontext = URB_USERCONTEXT_COOKIE; + /* We need to restart since we got a successful URB, but not ours */ + goto restart; + } + + /* + * If there was an error, that wasn't EAGAIN (no completion), then + * something happened during the reaping and we should return that + * error now + */ + if (ret < 0 && !urb.usercontext && errno != EAGAIN) + USB_ERROR_STR(-errno, "error reaping URB: %s", strerror(errno)); + + bytesdone += urb.actual_length; + } while ((ret == 0 || urb.usercontext) && bytesdone < size && urb.actual_length == requested); + + /* If the URB didn't complete in success or error, then let's unlink it */ + if (ret < 0 && !urb.usercontext) { + int rc; + + if (!waiting) + rc = -ETIMEDOUT; + else + rc = urb.status; + + ret = ioctl(dev->fd, IOCTL_USB_DISCARDURB, &urb); + if (ret < 0 && errno != EINVAL && usb_debug >= 1) + fprintf(stderr, "error discarding URB: %s", strerror(errno)); + + /* + * When the URB is unlinked, it gets moved to the completed list and + * then we need to reap it or else the next time we call this function, + * we'll get the previous completion and exit early + */ + ioctl(dev->fd, IOCTL_USB_REAPURB, &context); + + return rc; + } + + return bytesdone; +} + +int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout) +{ + /* Ensure the endpoint address is correct */ + return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, bytes, size, + timeout); +} + +int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout) +{ + /* Ensure the endpoint address is correct */ + ep |= USB_ENDPOINT_IN; + return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, bytes, size, + timeout); +} + +/* + * FIXME: Packetize large buffers here. 2.4 HCDs (atleast, haven't checked + * 2.5 HCDs yet) don't handle multi-packet Interrupt transfers. So we need + * to lookup the endpoint packet size and packetize appropriately here. + */ +int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout) +{ + /* Ensure the endpoint address is correct */ + return usb_urb_transfer(dev, ep, USB_URB_TYPE_INTERRUPT, bytes, size, + timeout); +} + +int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout) +{ + /* Ensure the endpoint address is correct */ + ep |= USB_ENDPOINT_IN; + return usb_urb_transfer(dev, ep, USB_URB_TYPE_INTERRUPT, bytes, size, + timeout); +} + +int usb_os_find_busses(struct usb_bus **busses) +{ + struct usb_bus *fbus = NULL; + DIR *dir; + struct dirent *entry; + + dir = opendir(usb_path); + if (!dir) + USB_ERROR_STR(-errno, "couldn't opendir(%s): %s", usb_path, + strerror(errno)); + + while ((entry = readdir(dir)) != NULL) { + struct usb_bus *bus; + + /* Skip anything starting with a . */ + if (entry->d_name[0] == '.') + continue; + + if (!strchr("0123456789", entry->d_name[strlen(entry->d_name) - 1])) { + if (usb_debug >= 2) + fprintf(stderr, "usb_os_find_busses: Skipping non bus directory %s\n", + entry->d_name); + continue; + } + + bus = malloc(sizeof(*bus)); + if (!bus) + USB_ERROR(-ENOMEM); + + memset((void *)bus, 0, sizeof(*bus)); + + strncpy(bus->dirname, entry->d_name, sizeof(bus->dirname) - 1); + bus->dirname[sizeof(bus->dirname) - 1] = 0; + + LIST_ADD(fbus, bus); + + if (usb_debug >= 2) + fprintf(stderr, "usb_os_find_busses: Found %s\n", bus->dirname); + } + + closedir(dir); + + *busses = fbus; + + return 0; +} + +int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices) +{ + struct usb_device *fdev = NULL; + DIR *dir; + struct dirent *entry; + char dirpath[PATH_MAX + 1]; + + snprintf(dirpath, PATH_MAX, "%s/%s", usb_path, bus->dirname); + + dir = opendir(dirpath); + if (!dir) + USB_ERROR_STR(-errno, "couldn't opendir(%s): %s", dirpath, + strerror(errno)); + + while ((entry = readdir(dir)) != NULL) { + unsigned char device_desc[DEVICE_DESC_LENGTH]; + char filename[PATH_MAX + 1]; + struct usb_device *dev; + struct usb_connectinfo connectinfo; + int i, fd, ret; + + /* Skip anything starting with a . */ + if (entry->d_name[0] == '.') + continue; + + dev = malloc(sizeof(*dev)); + if (!dev) + USB_ERROR(-ENOMEM); + + memset((void *)dev, 0, sizeof(*dev)); + + dev->bus = bus; + + strncpy(dev->filename, entry->d_name, sizeof(dev->filename) - 1); + dev->filename[sizeof(dev->filename) - 1] = 0; + + snprintf(filename, sizeof(filename) - 1, "%s/%s", dirpath, entry->d_name); + fd = open(filename, O_RDWR); + if (fd < 0) { + fd = open(filename, O_RDONLY); + if (fd < 0) { + if (usb_debug >= 2) + fprintf(stderr, "usb_os_find_devices: Couldn't open %s\n", + filename); + + free(dev); + continue; + } + } + + /* Get the device number */ + ret = ioctl(fd, IOCTL_USB_CONNECTINFO, &connectinfo); + if (ret < 0) { + if (usb_debug) + fprintf(stderr, "usb_os_find_devices: couldn't get connect info\n"); + } else + dev->devnum = connectinfo.devnum; + + ret = read(fd, (void *)device_desc, DEVICE_DESC_LENGTH); + if (ret < 0) { + if (usb_debug) + fprintf(stderr, "usb_os_find_devices: Couldn't read descriptor\n"); + + free(dev); + + goto err; + } + + /* + * Linux kernel converts the words in this descriptor to CPU endian, so + * we use the undocumented W character for usb_parse_descriptor() that + * doesn't convert endianess when parsing the descriptor + */ + usb_parse_descriptor(device_desc, "bbWbbbbWWWbbbb", &dev->descriptor); + + LIST_ADD(fdev, dev); + + if (usb_debug >= 2) + fprintf(stderr, "usb_os_find_devices: Found %s on %s\n", + dev->filename, bus->dirname); + + /* Now try to fetch the rest of the descriptors */ + if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) + /* Silent since we'll try again later */ + goto err; + + if (dev->descriptor.bNumConfigurations < 1) + /* Silent since we'll try again later */ + goto err; + + dev->config = (struct usb_config_descriptor *)malloc(dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor)); + if (!dev->config) + /* Silent since we'll try again later */ + goto err; + + memset(dev->config, 0, dev->descriptor.bNumConfigurations * + sizeof(struct usb_config_descriptor)); + + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + unsigned char buffer[8], *bigbuffer; + struct usb_config_descriptor config; + + /* Get the first 8 bytes so we can figure out what the total length is */ + ret = read(fd, (void *)buffer, 8); + if (ret < 8) { + if (usb_debug >= 1) { + if (ret < 0) + fprintf(stderr, "Unable to get descriptor (%d)\n", ret); + else + fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", 8, ret); + } + + goto err; + } + + usb_parse_descriptor(buffer, "bbw", &config); + + bigbuffer = malloc(config.wTotalLength); + if (!bigbuffer) { + if (usb_debug >= 1) + fprintf(stderr, "Unable to allocate memory for descriptors\n"); + goto err; + } + + /* Read the rest of the config descriptor */ + memcpy(bigbuffer, buffer, 8); + + ret = read(fd, (void *)(bigbuffer + 8), config.wTotalLength - 8); + if (ret < config.wTotalLength - 8) { + if (usb_debug >= 1) { + if (ret < 0) + fprintf(stderr, "Unable to get descriptor (%d)\n", ret); + else + fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", config.wTotalLength, ret); + } + + free(bigbuffer); + goto err; + } + + ret = usb_parse_configuration(&dev->config[i], bigbuffer); + if (usb_debug >= 2) { + if (ret > 0) + fprintf(stderr, "Descriptor data still left\n"); + else if (ret < 0) + fprintf(stderr, "Unable to parse descriptors\n"); + } + + free(bigbuffer); + } + +err: + close(fd); + } + + closedir(dir); + + *devices = fdev; + + return 0; +} + +int usb_os_determine_children(struct usb_bus *bus) +{ + struct usb_device *dev, *devices[256]; + struct usb_ioctl command; + int ret, i, i1; + + /* Create a list of devices first */ + memset(devices, 0, sizeof(devices)); + for (dev = bus->devices; dev; dev = dev->next) + if (dev->devnum) + devices[dev->devnum] = dev; + + /* Now fetch the children for each device */ + for (dev = bus->devices; dev; dev = dev->next) { + struct usb_hub_portinfo portinfo; + int fd; + + fd = device_open(dev); + if (fd < 0) + continue; + + /* Query the hub driver for the children of this device */ + if (dev->config && dev->config->interface && dev->config->interface->altsetting) + command.ifno = dev->config->interface->altsetting->bInterfaceNumber; + else + command.ifno = 0; + command.ioctl_code = IOCTL_USB_HUB_PORTINFO; + command.data = &portinfo; + ret = ioctl(fd, IOCTL_USB_IOCTL, &command); + if (ret < 0) { + /* errno == ENOSYS means the device probably wasn't a hub */ + if (errno != ENOSYS && usb_debug > 1) + fprintf(stderr, "error obtaining child information: %s\n", + strerror(errno)); + + close(fd); + continue; + } + + dev->num_children = 0; + for (i = 0; i < portinfo.numports; i++) + if (portinfo.port[i]) + dev->num_children++; + + /* Free any old children first */ + free(dev->children); + + dev->children = malloc(sizeof(struct usb_device *) * dev->num_children); + if (!dev->children) { + if (usb_debug > 1) + fprintf(stderr, "error allocating %zu bytes memory for dev->children\n", + sizeof(struct usb_device *) * dev->num_children); + + dev->num_children = 0; + close(fd); + continue; + } + + for (i = 0, i1 = 0; i < portinfo.numports; i++) { + if (!portinfo.port[i]) + continue; + + dev->children[i1++] = devices[portinfo.port[i]]; + + devices[portinfo.port[i]] = NULL; + } + + close(fd); + } + + /* + * There should be one device left in the devices list and that should be + * the root device + */ + for (i = 0; i < sizeof(devices) / sizeof(devices[0]); i++) { + if (devices[i]) + bus->root_dev = devices[i]; + } + + return 0; +} + +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) { + /* Skip anything starting with a . */ + 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; +} + +void usb_os_init(void) +{ + /* Find the path to the virtual filesystem */ + if (getenv("USB_DEVFS_PATH")) { + if (check_usb_vfs(getenv("USB_DEVFS_PATH"))) { + strncpy(usb_path, getenv("USB_DEVFS_PATH"), sizeof(usb_path) - 1); + usb_path[sizeof(usb_path) - 1] = 0; + } else if (usb_debug) + fprintf(stderr, "usb_os_init: couldn't find USB VFS in USB_DEVFS_PATH\n"); + } + + if (!usb_path[0]) { + if (check_usb_vfs("/dev/bus/usb")) { + strncpy(usb_path, "/dev/bus/usb", sizeof(usb_path) - 1); + usb_path[sizeof(usb_path) - 1] = 0; + } else if (check_usb_vfs("/proc/bus/usb")) { + strncpy(usb_path, "/proc/bus/usb", sizeof(usb_path) - 1); + usb_path[sizeof(usb_path) - 1] = 0; + } else + usb_path[0] = 0; /* No path, no USB support */ + } + + if (usb_debug) { + if (usb_path[0]) + fprintf(stderr, "usb_os_init: Found USB VFS at %s\n", usb_path); + else + fprintf(stderr, "usb_os_init: No USB VFS found, is it mounted?\n"); + } +} + +int usb_resetep(usb_dev_handle *dev, unsigned int ep) +{ + int ret; + + ret = ioctl(dev->fd, IOCTL_USB_RESETEP, &ep); + if (ret) + USB_ERROR_STR(-errno, "could not reset ep %d: %s", ep, + strerror(errno)); + + return 0; +} + +int usb_clear_halt(usb_dev_handle *dev, unsigned int ep) +{ + int ret; + + ret = ioctl(dev->fd, IOCTL_USB_CLEAR_HALT, &ep); + if (ret) + USB_ERROR_STR(-errno, "could not clear/halt ep %d: %s", ep, + strerror(errno)); + + return 0; +} + +int usb_reset(usb_dev_handle *dev) +{ + int ret; + + ret = ioctl(dev->fd, IOCTL_USB_RESET, NULL); + if (ret) + USB_ERROR_STR(-errno, "could not reset: %s", strerror(errno)); + + return 0; +} + +int usb_get_driver_np(usb_dev_handle *dev, int interface, char *name, + unsigned int namelen) +{ + struct usb_getdriver getdrv; + int ret; + + getdrv.interface = interface; + ret = ioctl(dev->fd, IOCTL_USB_GETDRIVER, &getdrv); + if (ret) + USB_ERROR_STR(-errno, "could not get bound driver: %s", strerror(errno)); + + strncpy(name, getdrv.driver, namelen - 1); + name[namelen - 1] = 0; + + return 0; +} + +int usb_detach_kernel_driver_np(usb_dev_handle *dev, int interface) +{ + struct usb_ioctl command; + int ret; + + command.ifno = interface; + command.ioctl_code = IOCTL_USB_DISCONNECT; + command.data = NULL; + + ret = ioctl(dev->fd, IOCTL_USB_IOCTL, &command); + if (ret) + USB_ERROR_STR(-errno, "could not detach kernel driver from interface %d: %s", + interface, strerror(errno)); + + return 0; +} + --- /dev/null +++ b/linux.h @@ -0,0 +1,119 @@ +#ifndef __LINUX_H__ +#define __LINUX_H__ + +#include <unistd.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +struct usb_ctrltransfer { + /* keep in sync with usbdevice_fs.h:usbdevfs_ctrltransfer */ + u_int8_t bRequestType; + u_int8_t bRequest; + u_int16_t wValue; + u_int16_t wIndex; + u_int16_t wLength; + + u_int32_t timeout; /* in milliseconds */ + + /* pointer to data */ + void *data; +}; + +struct usb_bulktransfer { + /* keep in sync with usbdevice_fs.h:usbdevfs_bulktransfer */ + unsigned int ep; + unsigned int len; + unsigned int timeout; /* in milliseconds */ + + /* pointer to data */ + void *data; +}; + +struct usb_setinterface { + /* keep in sync with usbdevice_fs.h:usbdevfs_setinterface */ + unsigned int interface; + unsigned int altsetting; +}; + +#define USB_MAXDRIVERNAME 255 + +struct usb_getdriver { + unsigned int interface; + char driver[USB_MAXDRIVERNAME + 1]; +}; + +#define USB_URB_DISABLE_SPD 1 +#define USB_URB_ISO_ASAP 2 +#define USB_URB_QUEUE_BULK 0x10 + +#define USB_URB_TYPE_ISO 0 +#define USB_URB_TYPE_INTERRUPT 1 +#define USB_URB_TYPE_CONTROL 2 +#define USB_URB_TYPE_BULK 3 + +struct usb_iso_packet_desc { + unsigned int length; + unsigned int actual_length; + unsigned int status; +}; + +struct usb_urb { + unsigned char type; + unsigned char endpoint; + int status; + unsigned int flags; + void *buffer; + int buffer_length; + int actual_length; + int start_frame; + int number_of_packets; + int error_count; + unsigned int signr; /* signal to be sent on error, -1 if none should be sent */ + void *usercontext; + struct usb_iso_packet_desc iso_frame_desc[0]; +}; + +struct usb_connectinfo { + unsigned int devnum; + unsigned char slow; +}; + +struct usb_ioctl { + int ifno; /* interface 0..N ; negative numbers reserved */ + int ioctl_code; /* MUST encode size + direction of data so the + * macros in <asm/ioctl.h> give correct values */ + void *data; /* param buffer (in, or out) */ +}; + +struct usb_hub_portinfo { + unsigned char numports; + unsigned char port[127]; /* port to device num mapping */ +}; + +#define IOCTL_USB_CONTROL _IOWR('U', 0, struct usb_ctrltransfer) +#define IOCTL_USB_BULK _IOWR('U', 2, struct usb_bulktransfer) +#define IOCTL_USB_RESETEP _IOR('U', 3, unsigned int) +#define IOCTL_USB_SETINTF _IOR('U', 4, struct usb_setinterface) +#define IOCTL_USB_SETCONFIG _IOR('U', 5, unsigned int) +#define IOCTL_USB_GETDRIVER _IOW('U', 8, struct usb_getdriver) +#define IOCTL_USB_SUBMITURB _IOR('U', 10, struct usb_urb) +#define IOCTL_USB_DISCARDURB _IO('U', 11) +#define IOCTL_USB_REAPURB _IOW('U', 12, void *) +#define IOCTL_USB_REAPURBNDELAY _IOW('U', 13, void *) +#define IOCTL_USB_CLAIMINTF _IOR('U', 15, unsigned int) +#define IOCTL_USB_RELEASEINTF _IOR('U', 16, unsigned int) +#define IOCTL_USB_CONNECTINFO _IOW('U', 17, struct usb_connectinfo) +#define IOCTL_USB_IOCTL _IOWR('U', 18, struct usb_ioctl) +#define IOCTL_USB_HUB_PORTINFO _IOR('U', 19, struct usb_hub_portinfo) +#define IOCTL_USB_RESET _IO('U', 20) +#define IOCTL_USB_CLEAR_HALT _IOR('U', 21, unsigned int) +#define IOCTL_USB_DISCONNECT _IO('U', 22) +#define IOCTL_USB_CONNECT _IO('U', 23) + +/* + * IOCTL_USB_HUB_PORTINFO, IOCTL_USB_DISCONNECT and IOCTL_USB_CONNECT + * all work via IOCTL_USB_IOCTL + */ + +#endif + --- /dev/null +++ b/usb.c @@ -0,0 +1,307 @@ +/* + * Main API entry point + * + * Copyright (c) 2000-2003 Johannes Erdfelt <johannes@erdfelt.com> + * + * This library is covered by the LGPL, read LICENSE for details. + */ + +#include <stdlib.h> /* getenv */ +#include <stdio.h> /* stderr */ +#include <string.h> /* strcmp */ +#include <errno.h> + +#include "usbi.h" + +int usb_debug = 0; +struct usb_bus *usb_busses = NULL; + +int usb_find_busses(void) +{ + struct usb_bus *busses, *bus; + int ret, changes = 0; + + ret = usb_os_find_busses(&busses); + if (ret < 0) + return ret; + + /* + * Now walk through all of the busses we know about and compare against + * this new list. Any duplicates will be removed from the new list. + * If we don't find it in the new list, the bus was removed. Any + * busses still in the new list, are new to us. + */ + bus = usb_busses; + while (bus) { + int found = 0; + struct usb_bus *nbus, *tbus = bus->next; + + nbus = busses; + while (nbus) { + struct usb_bus *tnbus = nbus->next; + + if (!strcmp(bus->dirname, nbus->dirname)) { + /* Remove it from the new busses list */ + LIST_DEL(busses, nbus); + + usb_free_bus(nbus); + found = 1; + break; + } + + nbus = tnbus; + } + + if (!found) { + /* The bus was removed from the system */ + LIST_DEL(usb_busses, bus); + usb_free_bus(bus); + changes++; + } + + bus = tbus; + } + + /* + * Anything on the *busses list is new. So add them to usb_busses and + * process them like the new bus it is. + */ + bus = busses; + while (bus) { + struct usb_bus *tbus = bus->next; + + /* + * Remove it from the temporary list first and add it to the real + * usb_busses list. + */ + LIST_DEL(busses, bus); + + LIST_ADD(usb_busses, bus); + + changes++; + + bus = tbus; + } + + return changes; +} + +int usb_find_devices(void) +{ + struct usb_bus *bus; + int ret, changes = 0; + + for (bus = usb_busses; bus; bus = bus->next) { + struct usb_device *devices, *dev; + + /* Find all of the devices and put them into a temporary list */ + ret = usb_os_find_devices(bus, &devices); + if (ret < 0) + return ret; + + /* + * Now walk through all of the devices we know about and compare + * against this new list. Any duplicates will be removed from the new + * list. If we don't find it in the new list, the device was removed. + * Any devices still in the new list, are new to us. + */ + dev = bus->devices; + while (dev) { + int found = 0; + struct usb_device *ndev, *tdev = dev->next; + + ndev = devices; + while (ndev) { + struct usb_device *tndev = ndev->next; + + if (!strcmp(dev->filename, ndev->filename)) { + /* Remove it from the new devices list */ + LIST_DEL(devices, ndev); + + usb_free_dev(ndev); + found = 1; + break; + } + + ndev = tndev; + } + + if (!found) { + /* The device was removed from the system */ + LIST_DEL(bus->devices, dev); + usb_free_dev(dev); + changes++; + } + + dev = tdev; + } + + /* + * Anything on the *devices list is new. So add them to bus->devices and + * process them like the new device it is. + */ + dev = devices; + while (dev) { + struct usb_device *tdev = dev->next; + + /* + * Remove it from the temporary list first and add it to the real + * bus->devices list. + */ + LIST_DEL(devices, dev); + + LIST_ADD(bus->devices, dev); + + /* + * Some ports fetch the descriptors on scanning (like Linux) so we don't + * need to fetch them again. + */ + if (!dev->config) { + usb_dev_handle *udev; + + udev = usb_open(dev); + if (udev) { + usb_fetch_and_parse_descriptors(udev); + + usb_close(udev); + } + } + + changes++; + + dev = tdev; + } + + usb_os_determine_children(bus); + } + + return changes; +} + +void usb_set_debug(int level) +{ + if (usb_debug || level) + fprintf(stderr, "usb_set_debug: Setting debugging level to %d (%s)\n", + level, level ? "on" : "off"); + + usb_debug = level; +} + +void usb_init(void) +{ + if (getenv("USB_DEBUG")) + usb_set_debug(atoi(getenv("USB_DEBUG"))); + + usb_os_init(); +} + +usb_dev_handle *usb_open(struct usb_device *dev) +{ + usb_dev_handle *udev; + + udev = malloc(sizeof(*udev)); + if (!udev) + return NULL; + + udev->fd = -1; + udev->device = dev; + udev->bus = dev->bus; + udev->config = udev->interface = udev->altsetting = -1; + + if (usb_os_open(udev) < 0) { + free(udev); + return NULL; + } + + return udev; +} + +int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, + size_t buflen) +{ + /* + * We can't use usb_get_descriptor() because it's lacking the index + * parameter. This will be fixed in libusb 1.0 + */ + return usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, + (USB_DT_STRING << 8) + index, langid, buf, buflen, 1000); +} + +int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, size_t buflen) +{ + char tbuf[255]; /* Some devices choke on size > 255 */ + int ret, langid, si, di; + + /* + * Asking for the zero'th index is special - it returns a string + * descriptor that contains all the language IDs supported by the + * device. Typically there aren't many - often only one. The + * language IDs are 16 bit numbers, and they start at the third byte + * in the descriptor. See USB 2.0 specification, section 9.6.7, for + * more information on this. */ + ret = usb_get_string(dev, 0, 0, tbuf, sizeof(tbuf)); + if (ret < 0) + return ret; + + if (ret < 4) + return -EIO; + + langid = tbuf[2] | (tbuf[3] << 8); + + ret = usb_get_string(dev, index, langid, tbuf, sizeof(tbuf)); + if (ret < 0) + return ret; + + if (tbuf[1] != USB_DT_STRING) + return -EIO; + + if (tbuf[0] > ret) + return -EFBIG; + + for (di = 0, si = 2; si < tbuf[0]; si += 2) { + if (di >= (buflen - 1)) + break; + + if (tbuf[si + 1]) /* high byte */ + buf[di++] = '?'; + else + buf[di++] = tbuf[si]; + } + + buf[di] = 0; + + return di; +} + +int usb_close(usb_dev_handle *dev) +{ + int ret; + + ret = usb_os_close(dev); + free(dev); + + return ret; +} + +struct usb_device *usb_device(usb_dev_handle *dev) +{ + return dev->device; +} + +void usb_free_dev(struct usb_device *dev) +{ + usb_destroy_configuration(dev); + free(dev->children); + free(dev); +} + +struct usb_bus *usb_get_busses(void) +{ + return usb_busses; +} + +void usb_free_bus(struct usb_bus *bus) +{ + free(bus); +} + --- /dev/null +++ b/usb.h @@ -0,0 +1,338 @@ +/* + * Prototypes, structure definitions and macros. + * + * Copyright (c) 2000-2003 Johannes Erdfelt <johannes@erdfelt.com> + * + * This library is covered by the LGPL, read LICENSE for details. + * + * This file (and only this file) may alternatively be licensed under the + * BSD license as well, read LICENSE for details. + */ +#ifndef __USB_H__ +#define __USB_H__ + +#include <unistd.h> +#include <stdlib.h> +#include <limits.h> + +#include <dirent.h> + +/* + * USB spec information + * + * This is all stuff grabbed from various USB specs and is pretty much + * not subject to change + */ + +/* + * Device and/or Interface Class codes + */ +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_PTP 6 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_DATA 10 +#define USB_CLASS_VENDOR_SPEC 0xff + +/* + * Descriptor types + */ +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + +#define USB_DT_HID 0x21 +#define USB_DT_REPORT 0x22 +#define USB_DT_PHYSICAL 0x23 +#define USB_DT_HUB 0x29 + +/* + * Descriptor sizes per descriptor type + */ +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define USB_DT_HUB_NONVAR_SIZE 7 + +/* All standard descriptors have these 2 fields in common */ +struct usb_descriptor_header { + u_int8_t bLength; + u_int8_t bDescriptorType; +}; + +/* String descriptor */ +struct usb_string_descriptor { + u_int8_t bLength; + u_int8_t bDescriptorType; + u_int16_t wData[1]; +}; + +/* HID descriptor */ +struct usb_hid_descriptor { + u_int8_t bLength; + u_int8_t bDescriptorType; + u_int16_t bcdHID; + u_int8_t bCountryCode; + u_int8_t bNumDescriptors; + /* u_int8_t bReportDescriptorType; */ + /* u_int16_t wDescriptorLength; */ + /* ... */ +}; + +/* Endpoint descriptor */ +#define USB_MAXENDPOINTS 32 +struct usb_endpoint_descriptor { + u_int8_t bLength; + u_int8_t bDescriptorType; + u_int8_t bEndpointAddress; + u_int8_t bmAttributes; + u_int16_t wMaxPacketSize; + u_int8_t bInterval; + u_int8_t bRefresh; + u_int8_t bSynchAddress; + + unsigned char *extra; /* Extra descriptors */ + int extralen; +}; + +#define USB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_TYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_TYPE_CONTROL 0 +#define USB_ENDPOINT_TYPE_ISOCHRONOUS 1 +#define USB_ENDPOINT_TYPE_BULK 2 +#define USB_ENDPOINT_TYPE_INTERRUPT 3 + +/* Interface descriptor */ +#define USB_MAXINTERFACES 32 +struct usb_interface_descriptor { + u_int8_t bLength; + u_int8_t bDescriptorType; + u_int8_t bInterfaceNumber; + u_int8_t bAlternateSetting; + u_int8_t bNumEndpoints; + u_int8_t bInterfaceClass; + u_int8_t bInterfaceSubClass; + u_int8_t bInterfaceProtocol; + u_int8_t iInterface; + + struct usb_endpoint_descriptor *endpoint; + + unsigned char *extra; /* Extra descriptors */ + int extralen; +}; + +#define USB_MAXALTSETTING 128 /* Hard limit */ +struct usb_interface { + struct usb_interface_descriptor *altsetting; + + int num_altsetting; +}; + +/* Configuration descriptor information.. */ +#define USB_MAXCONFIG 8 +struct usb_config_descriptor { + u_int8_t bLength; + u_int8_t bDescriptorType; + u_int16_t wTotalLength; + u_int8_t bNumInterfaces; + u_int8_t bConfigurationValue; + u_int8_t iConfiguration; + u_int8_t bmAttributes; + u_int8_t MaxPower; + + struct usb_interface *interface; + + unsigned char *extra; /* Extra descriptors */ + int extralen; +}; + +/* Device descriptor */ +struct usb_device_descriptor { + u_int8_t bLength; + u_int8_t bDescriptorType; + u_int16_t bcdUSB; + u_int8_t bDeviceClass; + u_int8_t bDeviceSubClass; + u_int8_t bDeviceProtocol; + u_int8_t bMaxPacketSize0; + u_int16_t idVendor; + u_int16_t idProduct; + u_int16_t bcdDevice; + u_int8_t iManufacturer; + u_int8_t iProduct; + u_int8_t iSerialNumber; + u_int8_t bNumConfigurations; +}; + +struct usb_ctrl_setup { + u_int8_t bRequestType; + u_int8_t bRequest; + u_int16_t wValue; + u_int16_t wIndex; + u_int16_t wLength; +}; + +/* + * Standard requests + */ +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +/* 0x02 is reserved */ +#define USB_REQ_SET_FEATURE 0x03 +/* 0x04 is reserved */ +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +/* + * Various libusb API related stuff + */ + +#define USB_ENDPOINT_IN 0x80 +#define USB_ENDPOINT_OUT 0x00 + +/* Error codes */ +#define USB_ERROR_BEGIN 500000 + +/* + * This is supposed to look weird. This file is generated from autoconf + * and I didn't want to make this too complicated. + */ +#include <endian.h> +#if __BYTE_ORDER == __BIG_ENDIAN +#define USB_LE16_TO_CPU(x) do { x = ((x & 0xff) << 8) | ((x & 0xff00) >> 8); } while(0) +#else +#define USB_LE16_TO_CPU(x) +#endif + +/* Data types */ +struct usb_device; +struct usb_bus; + +/* + * To maintain compatibility with applications already built with libusb, + * we must only add entries to the end of this structure. NEVER delete or + * move members and only change types if you really know what you're doing. + */ +struct usb_device { + struct usb_device *next, *prev; + + char filename[PATH_MAX + 1]; + + struct usb_bus *bus; + + struct usb_device_descriptor descriptor; + struct usb_config_descriptor *config; + + void *dev; /* Darwin support */ + + u_int8_t devnum; + + unsigned char num_children; + struct usb_device **children; +}; + +struct usb_bus { + struct usb_bus *next, *prev; + + char dirname[PATH_MAX + 1]; + + struct usb_device *devices; + u_int32_t location; + + struct usb_device *root_dev; +}; + +struct usb_dev_handle; +typedef struct usb_dev_handle usb_dev_handle; + +/* Variables */ +extern struct usb_bus *usb_busses; + +#ifdef __cplusplus +extern "C" { +#endif + +/* Function prototypes */ + +/* usb.c */ +usb_dev_handle *usb_open(struct usb_device *dev); +int usb_close(usb_dev_handle *dev); +int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, + size_t buflen); +int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, + size_t buflen); + +/* descriptors.c */ +int usb_get_descriptor_by_endpoint(usb_dev_handle *udev, int ep, + unsigned char type, unsigned char index, void *buf, int size); +int usb_get_descriptor(usb_dev_handle *udev, unsigned char type, + unsigned char index, void *buf, int size); + +/* <arch>.c */ +int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout); +int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout); +int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout); +int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout); +int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, + int value, int index, char *bytes, int size, int timeout); +int usb_set_configuration(usb_dev_handle *dev, int configuration); +int usb_claim_interface(usb_dev_handle *dev, int interface); +int usb_release_interface(usb_dev_handle *dev, int interface); +int usb_set_altinterface(usb_dev_handle *dev, int alternate); +int usb_resetep(usb_dev_handle *dev, unsigned int ep); +int usb_clear_halt(usb_dev_handle *dev, unsigned int ep); +int usb_reset(usb_dev_handle *dev); + +#if 1 /* LINUX_API */ +#define LIBUSB_HAS_GET_DRIVER_NP 1 +int usb_get_driver_np(usb_dev_handle *dev, int interface, char *name, + unsigned int namelen); +#define LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP 1 +int usb_detach_kernel_driver_np(usb_dev_handle *dev, int interface); +#endif + +char *usb_strerror(void); + +void usb_init(void); +void usb_set_debug(int level); +int usb_find_busses(void); +int usb_find_devices(void); +struct usb_device *usb_device(usb_dev_handle *dev); +struct usb_bus *usb_get_busses(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __USB_H__ */ + --- /dev/null +++ b/usbi.h @@ -0,0 +1,74 @@ +#ifndef _USBI_H_ +#define _USBI_H_ + +#include "usb.h" + +#include "error.h" + +extern int usb_debug; + +/* Some quick and generic macros for the simple kind of lists we use */ +#define LIST_ADD(begin, ent) \ + do { \ + if (begin) { \ + ent->next = begin; \ + ent->next->prev = ent; \ + } else \ + ent->next = NULL; \ + ent->prev = NULL; \ + begin = ent; \ + } while(0) + +#define LIST_DEL(begin, ent) \ + do { \ + if (ent->prev) \ + ent->prev->next = ent->next; \ + else \ + begin = ent->next; \ + if (ent->next) \ + ent->next->prev = ent->prev; \ + ent->prev = NULL; \ + ent->next = NULL; \ + } while (0) + +#define DESC_HEADER_LENGTH 2 +#define DEVICE_DESC_LENGTH 18 +#define CONFIG_DESC_LENGTH 9 +#define INTERFACE_DESC_LENGTH 9 +#define ENDPOINT_DESC_LENGTH 7 +#define ENDPOINT_AUDIO_DESC_LENGTH 9 + +struct usb_dev_handle { + int fd; + + struct usb_bus *bus; + struct usb_device *device; + + int config; + int interface; + int altsetting; + + /* Added by RMT so implementations can store other per-open-device data */ + void *impl_info; +}; + +/* descriptors.c */ +int usb_parse_descriptor(unsigned char *source, char *description, void *dest); +int usb_parse_configuration(struct usb_config_descriptor *config, + unsigned char *buffer); +void usb_fetch_and_parse_descriptors(usb_dev_handle *udev); +void usb_destroy_configuration(struct usb_device *dev); + +/* OS specific routines */ +int usb_os_find_busses(struct usb_bus **busses); +int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices); +int usb_os_determine_children(struct usb_bus *bus); +void usb_os_init(void); +int usb_os_open(usb_dev_handle *dev); +int usb_os_close(usb_dev_handle *dev); + +void usb_free_dev(struct usb_device *dev); +void usb_free_bus(struct usb_bus *bus); + +#endif /* _USBI_H_ */ +
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor