Exemple #1
0
/**
 * Get a USB configuration descriptor based on its index.
 */
int  libusb_get_config_descriptor(libusb_device *dev,
	uint8_t config_index, struct libusb_config_descriptor **config)
{
	struct libusb_config_descriptor *_config;
	unsigned char tmp[8];
	unsigned char *buf = NULL;
	int host_endian = 0;
	int r;

	usb_dbg("index %d", config_index);
	if (config_index >= dev->num_configurations)
		return LIBUSB_ERROR_NOT_FOUND;

	_config = malloc(sizeof(*_config));
	if (!_config)
		return LIBUSB_ERROR_NO_MEM;

	r = usb_backend->get_config_descriptor(dev, config_index, tmp,
		sizeof(tmp), &host_endian);
	if (r < 0)
		goto err;

	usb_parse_descriptor(tmp, "bbw", _config, host_endian);
	buf = malloc(_config->wTotalLength);
	if (!buf) {
		r = LIBUSB_ERROR_NO_MEM;
		goto err;
	}

	host_endian = 0;
	r = usb_backend->get_config_descriptor(dev, config_index, buf,
		_config->wTotalLength, &host_endian);
	if (r < 0)
		goto err;

	r = parse_configuration(dev->ctx, _config, buf, host_endian);
	if (r < 0) {
		usb_err(dev->ctx, "parse_configuration failed with error %d", r);
		goto err;
	} else if (r > 0) {
		usb_warn(dev->ctx, "descriptor data still left");
	}

	free(buf);
	*config = _config;
	return 0;

err:
	free(_config);
	if (buf)
		free(buf);
	return r;
}
Exemple #2
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;
}
Exemple #3
0
static int parse_endpoint(struct libusb_context *ctx,
	struct libusb_endpoint_descriptor *endpoint, unsigned char *buffer,
	int size, int host_endian)
{
	struct usb_descriptor_header header;
	unsigned char *extra;
	unsigned char *begin;
	int parsed = 0;
	int len;

	usb_parse_descriptor(buffer, "bb", &header, 0);

	/* Everything should be fine being passed into here, but we sanity */
	/*  check JIC */
	if (header.bLength > size) {
		usb_err(ctx, "ran out of descriptors parsing");
		return -1;
	}

	if (header.bDescriptorType != LIBUSB_DT_ENDPOINT) {
		usb_err(ctx, "unexpected descriptor %x (expected %x)",
			header.bDescriptorType, LIBUSB_DT_ENDPOINT);
		return parsed;
	}

	if (header.bLength >= ENDPOINT_AUDIO_DESC_LENGTH)
		usb_parse_descriptor(buffer, "bbbbwbbb", endpoint, host_endian);
	else if (header.bLength >= ENDPOINT_DESC_LENGTH)
		usb_parse_descriptor(buffer, "bbbbwb", endpoint, host_endian);

	buffer += header.bLength;
	size -= header.bLength;
	parsed += header.bLength;

	/* Skip over the rest of the Class Specific or Vendor Specific */
	/*  descriptors */
	begin = buffer;
	while (size >= DESC_HEADER_LENGTH) {
		usb_parse_descriptor(buffer, "bb", &header, 0);

		if (header.bLength < 2) {
			usb_err(ctx, "invalid descriptor length %d", header.bLength);
			return -1;
		}

		/* If we find another "proper" descriptor then we're done  */
		if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
				(header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
				(header.bDescriptorType == LIBUSB_DT_CONFIG) ||
				(header.bDescriptorType == LIBUSB_DT_DEVICE))
			break;

		usb_dbg("skipping descriptor %x", header.bDescriptorType);
		buffer += header.bLength;
		size -= header.bLength;
		parsed += header.bLength;
	}

	/* Copy any unknown descriptors into a storage area for drivers */
	/*  to later parse */
	len = (int)(buffer - begin);
	if (!len) {
		endpoint->extra = NULL;
		endpoint->extra_length = 0;
		return parsed;
	}

	extra = malloc(len);
	endpoint->extra = extra;
	if (!extra) {
		endpoint->extra_length = 0;
		return LIBUSB_ERROR_NO_MEM;
	}

	memcpy(extra, begin, len);
	endpoint->extra_length = len;

	return parsed;
}
Exemple #4
0
static int parse_interface(libusb_context *ctx,
	struct libusb_interface *usb_interface, unsigned char *buffer, int size,
	int host_endian)
{
	int i;
	int len;
	int r;
	int parsed = 0;
	size_t tmp;
	struct usb_descriptor_header header;
	struct libusb_interface_descriptor *ifp;
	unsigned char *begin;

	usb_interface->num_altsetting = 0;

	while (size >= INTERFACE_DESC_LENGTH) {
		struct libusb_interface_descriptor *altsetting =
			(struct libusb_interface_descriptor *) usb_interface->altsetting;
		altsetting = realloc(altsetting,
			sizeof(struct libusb_interface_descriptor) *
			(usb_interface->num_altsetting + 1));
		if (!altsetting) {
			r = LIBUSB_ERROR_NO_MEM;
			goto err;
		}
		usb_interface->altsetting = altsetting;

		ifp = altsetting + usb_interface->num_altsetting;
		usb_interface->num_altsetting++;
		usb_parse_descriptor(buffer, "bbbbbbbbb", ifp, 0);
		ifp->extra = NULL;
		ifp->extra_length = 0;
		ifp->endpoint = NULL;

		/* Skip over the interface */
		buffer += ifp->bLength;
		parsed += ifp->bLength;
		size -= ifp->bLength;

		begin = buffer;

		/* Skip over any interface, class or vendor descriptors */
		while (size >= DESC_HEADER_LENGTH) {
			usb_parse_descriptor(buffer, "bb", &header, 0);
			if (header.bLength < 2) {
				usb_err(ctx, "invalid descriptor of length %d",
					header.bLength);
				r = LIBUSB_ERROR_IO;
				goto err;
			}

			/* If we find another "proper" descriptor then we're done */
			if ((header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
					(header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
					(header.bDescriptorType == LIBUSB_DT_CONFIG) ||
					(header.bDescriptorType == LIBUSB_DT_DEVICE))
				break;

			buffer += header.bLength;
			parsed += header.bLength;
			size -= header.bLength;
		}

		/* Copy any unknown descriptors into a storage area for */
		/*  drivers to later parse */
		len = (int)(buffer - begin);
		if (len) {
			ifp->extra = malloc(len);
			if (!ifp->extra) {
				r = LIBUSB_ERROR_NO_MEM;
				goto err;
			}
			memcpy((unsigned char *) ifp->extra, begin, len);
			ifp->extra_length = len;
		}

		/* Did we hit an unexpected descriptor? */
		if (size >= DESC_HEADER_LENGTH) {
			usb_parse_descriptor(buffer, "bb", &header, 0);
			if ((header.bDescriptorType == LIBUSB_DT_CONFIG) ||
			    (header.bDescriptorType == LIBUSB_DT_DEVICE)) {
				return parsed;
			}
		}

		if (ifp->bNumEndpoints > USB_MAXENDPOINTS) {
			usb_err(ctx, "too many endpoints (%d)", ifp->bNumEndpoints);
			r = LIBUSB_ERROR_IO;
			goto err;
		}

		if (ifp->bNumEndpoints > 0) {
			struct libusb_endpoint_descriptor *endpoint;
			tmp = ifp->bNumEndpoints * sizeof(struct libusb_endpoint_descriptor);
			endpoint = malloc(tmp);
			ifp->endpoint = endpoint;
			if (!endpoint) {
				r = LIBUSB_ERROR_NO_MEM;
				goto err;
			}

			memset(endpoint, 0, tmp);
			for (i = 0; i < ifp->bNumEndpoints; i++) {
				usb_parse_descriptor(buffer, "bb", &header, 0);

				if (header.bLength > size) {
					usb_err(ctx, "ran out of descriptors parsing");
					r = LIBUSB_ERROR_IO;
					goto err;
				}

				r = parse_endpoint(ctx, endpoint + i, buffer, size,
					host_endian);
				if (r < 0)
					goto err;

				buffer += r;
				parsed += r;
				size -= r;
			}
		}

		/* We check to see if it's an alternate to this one */
		ifp = (struct libusb_interface_descriptor *) buffer;
		if (size < LIBUSB_DT_INTERFACE_SIZE ||
				ifp->bDescriptorType != LIBUSB_DT_INTERFACE ||
				!ifp->bAlternateSetting)
			return parsed;
	}

	return parsed;
err:
	clear_interface(usb_interface);
	return r;
}
Exemple #5
0
static int parse_configuration(struct libusb_context *ctx,
	struct libusb_config_descriptor *config, unsigned char *buffer,
	int host_endian)
{
	int i;
	int r;
	int size;
	size_t tmp;
	struct usb_descriptor_header header;
	struct libusb_interface *usb_interface;

	usb_parse_descriptor(buffer, "bbwbbbbb", config, host_endian);
	size = config->wTotalLength;

	if (config->bNumInterfaces > USB_MAXINTERFACES) {
		usb_err(ctx, "too many interfaces (%d)", config->bNumInterfaces);
		return LIBUSB_ERROR_IO;
	}

	tmp = config->bNumInterfaces * sizeof(struct libusb_interface);
	usb_interface = malloc(tmp);
	config->interface = usb_interface;
	if (!config->interface)
		return LIBUSB_ERROR_NO_MEM;

	memset(usb_interface, 0, tmp);
	buffer += config->bLength;
	size -= config->bLength;

	config->extra = NULL;
	config->extra_length = 0;

	for (i = 0; i < config->bNumInterfaces; i++) {
		int len;
		unsigned char *begin;

		/* Skip over the rest of the Class Specific or Vendor */
		/*  Specific descriptors */
		begin = buffer;
		while (size >= DESC_HEADER_LENGTH) {
			usb_parse_descriptor(buffer, "bb", &header, 0);

			if ((header.bLength > size) ||
					(header.bLength < DESC_HEADER_LENGTH)) {
				usb_err(ctx, "invalid descriptor length of %d",
					header.bLength);
				r = LIBUSB_ERROR_IO;
				goto err;
			}

			/* If we find another "proper" descriptor then we're done */
			if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
					(header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
					(header.bDescriptorType == LIBUSB_DT_CONFIG) ||
					(header.bDescriptorType == LIBUSB_DT_DEVICE))
				break;

			usb_dbg("skipping descriptor 0x%x\n", header.bDescriptorType);
			buffer += header.bLength;
			size -= header.bLength;
		}

		/* 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->extra_length) {
				config->extra = malloc(len);
				if (!config->extra) {
					r = LIBUSB_ERROR_NO_MEM;
					goto err;
				}

				memcpy((unsigned char *) config->extra, begin, len);
				config->extra_length = len;
			}
		}

		r = parse_interface(ctx, usb_interface + i, buffer, size, host_endian);
		if (r < 0)
			goto err;

		buffer += r;
		size -= r;
	}

	return size;

err:
	clear_configuration(config);
	return r;
}
Exemple #6
0
int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices)
{
    struct usb_device *fdev = NULL;
    int cfd, dfd;
    int device;

    cfd = open(bus->dirname, O_RDONLY);
    if (cfd < 0)
        USB_ERROR_STR(-errno, "couldn't open(%s): %s", bus->dirname,
                      strerror(errno));

    for (device = 1; device < USB_MAX_DEVICES; device++) {
        struct usb_device_info di;
        struct usb_device *dev;
        unsigned char device_desc[DEVICE_DESC_LENGTH];
        char buf[20];

        di.udi_addr = device;
        if (ioctl(cfd, USB_DEVICEINFO, &di) < 0)
            continue;

        /* There's a device; is it one we should mess with? */

        if (strncmp(di.udi_devnames[0], "ugen", 4) != 0)
            /* best not to play with things we don't understand */
            continue;

#ifdef __FreeBSD_kernel__
        snprintf(buf, sizeof(buf) - 1, "/dev/%s", di.udi_devnames[0]);
#else
        snprintf(buf, sizeof(buf) - 1, "/dev/%s.00", di.udi_devnames[0]);
#endif

        /* Open its control endpoint */
        dfd = open(buf, O_RDONLY);
        if (dfd < 0) {
            if (usb_debug >= 2)
                fprintf(stderr, "usb_os_find_devices: couldn't open device %s: %s\n",
                        buf, strerror(errno));
            continue;
        }

        dev = malloc(sizeof(*dev));
        if (!dev)
            USB_ERROR(-ENOMEM);

        memset((void *)dev, 0, sizeof(*dev));

        dev->bus = bus;

        /* we need to report the device name as /dev/ugenx NOT /dev/ugenx.00
         * This seemed easier than having 2 variables...
         */
#if (__NetBSD__ || __OpenBSD__)
        snprintf(buf, sizeof(buf) - 1, "/dev/%s", di.udi_devnames[0]);
#endif

        strncpy(dev->filename, buf, sizeof(dev->filename) - 1);
        dev->filename[sizeof(dev->filename) - 1] = 0;

        if (ioctl(dfd, USB_GET_DEVICE_DESC, device_desc) < 0)
            USB_ERROR_STR(-errno, "couldn't get device descriptor for %s: %s",
                          buf, strerror(errno));

        close(dfd);

        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);
    }

    close(cfd);

    *devices = fdev;

    return 0;
}
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;
}
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, (unsigned char)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, (unsigned char)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;
}
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;
}
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;
}