diff options
author | Daniel Drake <dsd@gentoo.org> | 2008-05-16 22:37:56 +0100 |
---|---|---|
committer | Daniel Drake <dsd@gentoo.org> | 2008-05-16 22:37:56 +0100 |
commit | 2b2e9c40b195261b09ac52ebdb93eef25c79de90 (patch) | |
tree | 324484c5a2583d6d1f85b601edbc5082113a1f0e | |
parent | 1298c51f516a7bf04ca9add1b7db14417cdc66f3 (diff) | |
download | libusb-2b2e9c40b195261b09ac52ebdb93eef25c79de90.tar.gz |
Fix endianness with descriptor handling
Alan Stern pointed out that usbfs gives host-endian data, but sysfs gives
bus-endian.
-rw-r--r-- | libusb/core.c | 3 | ||||
-rw-r--r-- | libusb/descriptor.c | 94 | ||||
-rw-r--r-- | libusb/libusbi.h | 22 | ||||
-rw-r--r-- | libusb/os/linux_usbfs.c | 26 |
4 files changed, 87 insertions, 58 deletions
diff --git a/libusb/core.c b/libusb/core.c index 28dafec..74ad527 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -356,8 +356,9 @@ int usbi_sanitize_device(struct libusb_device *dev) int r; unsigned char raw_desc[DEVICE_DESC_LENGTH]; uint8_t num_configurations; + int host_endian; - r = usbi_backend->get_device_descriptor(dev, raw_desc); + r = usbi_backend->get_device_descriptor(dev, raw_desc, &host_endian); if (r < 0) return r; diff --git a/libusb/descriptor.c b/libusb/descriptor.c index 4a3be62..37dcc64 100644 --- a/libusb/descriptor.c +++ b/libusb/descriptor.c @@ -36,11 +36,13 @@ * for detected devices */ -int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest) +/* set host_endian if the w values are already in host endian format, + * as opposed to bus endian. */ +int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest, + int host_endian) { unsigned char *sp = source, *dp = dest; uint16_t w; - uint32_t d; char *cp; for (cp = descriptor; *cp; cp++) { @@ -49,22 +51,16 @@ int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest) *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; - 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; + + if (host_endian) { + memcpy(dp, sp, 2); + } else { + w = (sp[1] << 8) | sp[0]; + *((uint16_t *)dp) = w; + } + sp += 2; + dp += 2; break; } } @@ -79,7 +75,7 @@ static void clear_endpoint(struct libusb_endpoint_descriptor *endpoint) } static int parse_endpoint(struct libusb_endpoint_descriptor *endpoint, - unsigned char *buffer, int size) + unsigned char *buffer, int size, int host_endian) { struct usb_descriptor_header header; unsigned char *extra; @@ -87,7 +83,7 @@ static int parse_endpoint(struct libusb_endpoint_descriptor *endpoint, int parsed = 0; int len; - usbi_parse_descriptor(buffer, "bb", &header); + usbi_parse_descriptor(buffer, "bb", &header, 0); /* Everything should be fine being passed into here, but we sanity */ /* check JIC */ @@ -103,9 +99,9 @@ static int parse_endpoint(struct libusb_endpoint_descriptor *endpoint, } if (header.bLength >= ENDPOINT_AUDIO_DESC_LENGTH) - usbi_parse_descriptor(buffer, "bbbbwbbb", endpoint); + usbi_parse_descriptor(buffer, "bbbbwbbb", endpoint, host_endian); else if (header.bLength >= ENDPOINT_DESC_LENGTH) - usbi_parse_descriptor(buffer, "bbbbwb", endpoint); + usbi_parse_descriptor(buffer, "bbbbwb", endpoint, host_endian); buffer += header.bLength; size -= header.bLength; @@ -115,7 +111,7 @@ static int parse_endpoint(struct libusb_endpoint_descriptor *endpoint, /* descriptors */ begin = buffer; while (size >= DESC_HEADER_LENGTH) { - usbi_parse_descriptor(buffer, "bb", &header); + usbi_parse_descriptor(buffer, "bb", &header, 0); if (header.bLength < 2) { usbi_err("invalid descriptor length %d", header.bLength); @@ -182,7 +178,7 @@ static void clear_interface(struct libusb_interface *interface) } static int parse_interface(struct libusb_interface *interface, - unsigned char *buffer, int size) + unsigned char *buffer, int size, int host_endian) { int i; int len; @@ -209,7 +205,7 @@ static int parse_interface(struct libusb_interface *interface, ifp = altsetting + interface->num_altsetting; interface->num_altsetting++; - usbi_parse_descriptor(buffer, "bbbbbbbbb", ifp); + usbi_parse_descriptor(buffer, "bbbbbbbbb", ifp, 0); ifp->extra = NULL; ifp->extra_length = 0; ifp->endpoint = NULL; @@ -223,7 +219,7 @@ static int parse_interface(struct libusb_interface *interface, /* Skip over any interface, class or vendor descriptors */ while (size >= DESC_HEADER_LENGTH) { - usbi_parse_descriptor(buffer, "bb", &header); + usbi_parse_descriptor(buffer, "bb", &header, 0); if (header.bLength < 2) { usbi_err("invalid descriptor of length %d", header.bLength); r = LIBUSB_ERROR_IO; @@ -256,7 +252,7 @@ static int parse_interface(struct libusb_interface *interface, } /* Did we hit an unexpected descriptor? */ - usbi_parse_descriptor(buffer, "bb", &header); + usbi_parse_descriptor(buffer, "bb", &header, 0); if ((size >= DESC_HEADER_LENGTH) && ((header.bDescriptorType == LIBUSB_DT_CONFIG) || (header.bDescriptorType == LIBUSB_DT_DEVICE))) @@ -280,7 +276,7 @@ static int parse_interface(struct libusb_interface *interface, memset(endpoint, 0, tmp); for (i = 0; i < ifp->bNumEndpoints; i++) { - usbi_parse_descriptor(buffer, "bb", &header); + usbi_parse_descriptor(buffer, "bb", &header, 0); if (header.bLength > size) { usbi_err("ran out of descriptors parsing"); @@ -288,7 +284,7 @@ static int parse_interface(struct libusb_interface *interface, goto err; } - r = parse_endpoint(endpoint + i, buffer, size); + r = parse_endpoint(endpoint + i, buffer, size, host_endian); if (r < 0) goto err; @@ -326,7 +322,7 @@ static void clear_configuration(struct libusb_config_descriptor *config) } static int parse_configuration(struct libusb_config_descriptor *config, - unsigned char *buffer) + unsigned char *buffer, int host_endian) { int i; int r; @@ -335,7 +331,7 @@ static int parse_configuration(struct libusb_config_descriptor *config, struct usb_descriptor_header header; struct libusb_interface *interface; - usbi_parse_descriptor(buffer, "bbwbbbbb", config); + usbi_parse_descriptor(buffer, "bbwbbbbb", config, host_endian); size = config->wTotalLength; if (config->bNumInterfaces > USB_MAXINTERFACES) { @@ -364,7 +360,7 @@ static int parse_configuration(struct libusb_config_descriptor *config, /* Specific descriptors */ begin = buffer; while (size >= DESC_HEADER_LENGTH) { - usbi_parse_descriptor(buffer, "bb", &header); + usbi_parse_descriptor(buffer, "bb", &header, 0); if ((header.bLength > size) || (header.bLength < DESC_HEADER_LENGTH)) { @@ -402,7 +398,7 @@ static int parse_configuration(struct libusb_config_descriptor *config, } } - r = parse_interface(interface + i, buffer, size); + r = parse_interface(interface + i, buffer, size, host_endian); if (r < 0) goto err; @@ -430,14 +426,21 @@ API_EXPORTED int libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc) { unsigned char raw_desc[DEVICE_DESC_LENGTH]; + int host_endian = 0; int r; usbi_dbg(""); - r = usbi_backend->get_device_descriptor(dev, raw_desc); + r = usbi_backend->get_device_descriptor(dev, raw_desc, &host_endian); if (r < 0) return r; - usbi_parse_descriptor(raw_desc, "bbWbbbbWWWbbbb", desc); + memcpy((unsigned char *) desc, raw_desc, sizeof(raw_desc)); + if (host_endian) { + desc->bcdUSB = libusb_cpu_to_le16(desc->bcdUSB); + desc->idVendor = libusb_cpu_to_le16(desc->idVendor); + desc->idProduct = libusb_cpu_to_le16(desc->idProduct); + desc->bcdDevice = libusb_cpu_to_le16(desc->bcdDevice); + } return 0; } @@ -461,17 +464,19 @@ API_EXPORTED int libusb_get_active_config_descriptor(libusb_device *dev, struct libusb_config_descriptor *_config = malloc(sizeof(*_config)); unsigned char tmp[8]; unsigned char *buf = NULL; + int host_endian = 0; int r; usbi_dbg(""); if (!_config) return LIBUSB_ERROR_NO_MEM; - r = usbi_backend->get_active_config_descriptor(dev, tmp, sizeof(tmp)); + r = usbi_backend->get_active_config_descriptor(dev, tmp, sizeof(tmp), + &host_endian); if (r < 0) goto err; - usbi_parse_descriptor(tmp, "bbw", _config); + usbi_parse_descriptor(tmp, "bbw", _config, host_endian); buf = malloc(_config->wTotalLength); if (!buf) { r = LIBUSB_ERROR_NO_MEM; @@ -479,11 +484,11 @@ API_EXPORTED int libusb_get_active_config_descriptor(libusb_device *dev, } r = usbi_backend->get_active_config_descriptor(dev, buf, - _config->wTotalLength); + _config->wTotalLength, &host_endian); if (r < 0) goto err; - r = parse_configuration(_config, buf); + r = parse_configuration(_config, buf, host_endian); if (r < 0) { usbi_err("parse_configuration failed with error %d", r); goto err; @@ -523,6 +528,7 @@ API_EXPORTED int libusb_get_config_descriptor(libusb_device *dev, struct libusb_config_descriptor *_config; unsigned char tmp[8]; unsigned char *buf = NULL; + int host_endian = 0; int r; usbi_dbg("index %d", config_index); @@ -534,11 +540,11 @@ API_EXPORTED int libusb_get_config_descriptor(libusb_device *dev, return LIBUSB_ERROR_NO_MEM; r = usbi_backend->get_config_descriptor(dev, config_index, tmp, - sizeof(tmp)); + sizeof(tmp), &host_endian); if (r < 0) goto err; - usbi_parse_descriptor(tmp, "bbw", _config); + usbi_parse_descriptor(tmp, "bbw", _config, host_endian); buf = malloc(_config->wTotalLength); if (!buf) { r = LIBUSB_ERROR_NO_MEM; @@ -546,11 +552,11 @@ API_EXPORTED int libusb_get_config_descriptor(libusb_device *dev, } r = usbi_backend->get_config_descriptor(dev, config_index, buf, - _config->wTotalLength); + _config->wTotalLength, &host_endian); if (r < 0) goto err; - r = parse_configuration(_config, buf); + r = parse_configuration(_config, buf, host_endian); if (r < 0) { usbi_err("parse_configuration failed with error %d", r); goto err; @@ -581,7 +587,9 @@ int usbi_get_config_index_by_value(struct libusb_device *dev, usbi_dbg("value %d", bConfigurationValue); for (i = 0; i < dev->num_configurations; i++) { unsigned char tmp[6]; - int r = usbi_backend->get_config_descriptor(dev, i, tmp, sizeof(tmp)); + int host_endian; + int r = usbi_backend->get_config_descriptor(dev, i, tmp, sizeof(tmp), + &host_endian); if (r < 0) return r; if (tmp[5] == bConfigurationValue) { diff --git a/libusb/libusbi.h b/libusb/libusbi.h index 015d489..41c8097 100644 --- a/libusb/libusbi.h +++ b/libusb/libusbi.h @@ -217,7 +217,8 @@ void usbi_handle_transfer_completion(struct usbi_transfer *itransfer, enum libusb_transfer_status status); void usbi_handle_transfer_cancellation(struct usbi_transfer *transfer); -int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest); +int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest, + int host_endian); int usbi_get_config_index_by_value(struct libusb_device *dev, uint8_t bConfigurationValue, int *idx); @@ -378,10 +379,14 @@ struct usbi_os_backend { * it to the list of discovered devices, and also when the user requests * to read the device descriptor. * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * * Return 0 on success or a LIBUSB_ERROR code on failure. */ int (*get_device_descriptor)(struct libusb_device *device, - unsigned char *buffer); + unsigned char *buffer, int *host_endian); /* Get the ACTIVE configuration descriptor for a device. * @@ -394,13 +399,17 @@ struct usbi_os_backend { * is guaranteed to be big enough. If you can only do a partial write, * return an error code. * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * * Return: * - 0 on success * - LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state * - another LIBUSB_ERROR code on other failure */ int (*get_active_config_descriptor)(struct libusb_device *device, - unsigned char *buffer, size_t len); + unsigned char *buffer, size_t len, int *host_endian); /* Get a specific configuration descriptor for a device. * @@ -417,10 +426,15 @@ struct usbi_os_backend { * is guaranteed to be big enough. If you can only do a partial write, * return an error code. * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * * Return 0 on success or a LIBUSB_ERROR code on failure. */ int (*get_config_descriptor)(struct libusb_device *device, - uint8_t config_index, unsigned char *buffer, size_t len); + uint8_t config_index, unsigned char *buffer, size_t len, + int *host_endian); /* Set the active configuration for a device. * diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c index 1ed9ad8..82f1ac8 100644 --- a/libusb/os/linux_usbfs.c +++ b/libusb/os/linux_usbfs.c @@ -239,12 +239,14 @@ static int sysfs_get_device_descriptor(struct libusb_device *dev, } static int op_get_device_descriptor(struct libusb_device *dev, - unsigned char *buffer) + unsigned char *buffer, int *host_endian) { - if (sysfs_has_descriptors) + if (sysfs_has_descriptors) { return sysfs_get_device_descriptor(dev, buffer); - else + } else { + *host_endian = 1; return usbfs_get_device_descriptor(dev, buffer); + } } static int usbfs_get_active_config_descriptor(struct libusb_device *dev, @@ -297,12 +299,14 @@ static int sysfs_get_active_config_descriptor(struct libusb_device *dev, } static int op_get_active_config_descriptor(struct libusb_device *dev, - unsigned char *buffer, size_t len) + unsigned char *buffer, size_t len, int *host_endian) { - if (sysfs_has_descriptors) + if (sysfs_has_descriptors) { return sysfs_get_active_config_descriptor(dev, buffer, len); - else + } else { + *host_endian = 1; return usbfs_get_active_config_descriptor(dev, buffer, len); + } } @@ -337,7 +341,7 @@ static int get_config_descriptor(int fd, uint8_t config_index, return LIBUSB_ERROR_IO; } - usbi_parse_descriptor(tmp, "bbwbb", &config); + usbi_parse_descriptor(tmp, "bbwbb", &config, 1); /* seek forward to end of config */ off = lseek(fd, config.wTotalLength - sizeof(tmp), SEEK_CUR); @@ -363,7 +367,7 @@ static int get_config_descriptor(int fd, uint8_t config_index, } static int op_get_config_descriptor(struct libusb_device *dev, - uint8_t config_index, unsigned char *buffer, size_t len) + uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) { char filename[PATH_MAX + 1]; int fd; @@ -381,11 +385,13 @@ static int op_get_config_descriptor(struct libusb_device *dev, r = get_config_descriptor(fd, config_index, buffer, len); close(fd); + *host_endian = 1; return r; } /* cache the active config descriptor in memory. a value of -1 means that - * we aren't sure which one is active, so just assume the first one. */ + * we aren't sure which one is active, so just assume the first one. + * only for usbfs. */ static int cache_active_config(struct libusb_device *dev, int fd, int active_config) { @@ -412,7 +418,7 @@ static int cache_active_config(struct libusb_device *dev, int fd, return r; } - usbi_parse_descriptor(tmp, "bbw", &config); + usbi_parse_descriptor(tmp, "bbw", &config, 1); buf = malloc(config.wTotalLength); if (!buf) return LIBUSB_ERROR_NO_MEM; |