/********************************************************************* * Software License Agreement (BSD License) * * Copyright (C) 2010-2012 Ken Tossell * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the author nor other contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. *********************************************************************/ /** * @defgroup device Device handling and enumeration * @brief Support for finding, inspecting and opening UVC devices */ #include "libuvc/libuvc.h" #include "libuvc/libuvc_internal.h" int uvc_already_open(uvc_context_t *ctx, struct libusb_device *usb_dev); void uvc_free_devh(uvc_device_handle_t *devh); uvc_error_t uvc_get_device_info(uvc_device_t *dev, uvc_device_info_t **info); void uvc_free_device_info(uvc_device_info_t *info); uvc_error_t uvc_scan_control(uvc_device_t *dev, uvc_device_info_t *info); uvc_error_t uvc_parse_vc(uvc_device_t *dev, uvc_device_info_t *info, const unsigned char *block, size_t block_size); uvc_error_t uvc_parse_vc_selector_unit(uvc_device_t *dev, uvc_device_info_t *info, const unsigned char *block, size_t block_size); uvc_error_t uvc_parse_vc_extension_unit(uvc_device_t *dev, uvc_device_info_t *info, const unsigned char *block, size_t block_size); uvc_error_t uvc_parse_vc_header(uvc_device_t *dev, uvc_device_info_t *info, const unsigned char *block, size_t block_size); uvc_error_t uvc_parse_vc_input_terminal(uvc_device_t *dev, uvc_device_info_t *info, const unsigned char *block, size_t block_size); uvc_error_t uvc_parse_vc_processing_unit(uvc_device_t *dev, uvc_device_info_t *info, const unsigned char *block, size_t block_size); uvc_error_t uvc_scan_streaming(uvc_device_t *dev, uvc_device_info_t *info, int interface_idx); uvc_error_t uvc_parse_vs(uvc_device_t *dev, uvc_device_info_t *info, uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size); uvc_error_t uvc_parse_vs_format_uncompressed(uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size); uvc_error_t uvc_parse_vs_format_mjpeg(uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size); uvc_error_t uvc_parse_vs_frame_uncompressed(uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size); uvc_error_t uvc_parse_vs_frame_format(uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size); uvc_error_t uvc_parse_vs_frame_frame(uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size); uvc_error_t uvc_parse_vs_input_header(uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size); void LIBUSB_CALL _uvc_status_callback(struct libusb_transfer *transfer); /** @internal * @brief Test whether the specified USB device has been opened as a UVC device * @ingroup device * * @param ctx Context in which to search for the UVC device * @param usb_dev USB device to find * @return true if the device is open in this context */ int uvc_already_open(uvc_context_t *ctx, struct libusb_device *usb_dev) { uvc_device_handle_t *devh; DL_FOREACH(ctx->open_devices, devh) { if (usb_dev == devh->dev->usb_dev) return 1; } return 0; } /** @brief Finds a camera identified by vendor, product and/or serial number * @ingroup device * * @param[in] ctx UVC context in which to search for the camera * @param[out] dev Reference to the camera, or NULL if not found * @param[in] vid Vendor ID number, optional * @param[in] pid Product ID number, optional * @param[in] sn Serial number or NULL * @return Error finding device or UVC_SUCCESS */ uvc_error_t uvc_find_device( uvc_context_t *ctx, uvc_device_t **dev, int vid, int pid, const char *sn) { uvc_error_t ret = UVC_SUCCESS; uvc_device_t **list; uvc_device_t *test_dev; int dev_idx; int found_dev; UVC_ENTER(); ret = uvc_get_device_list(ctx, &list); if (ret != UVC_SUCCESS) { UVC_EXIT(ret); return ret; } dev_idx = 0; found_dev = 0; while (!found_dev && (test_dev = list[dev_idx++]) != NULL) { uvc_device_descriptor_t *desc; if (uvc_get_device_descriptor(test_dev, &desc) != UVC_SUCCESS) continue; if ((!vid || desc->idVendor == vid) && (!pid || desc->idProduct == pid) && (!sn || (desc->serialNumber && !strcmp(desc->serialNumber, sn)))) found_dev = 1; uvc_free_device_descriptor(desc); } if (found_dev) uvc_ref_device(test_dev); uvc_free_device_list(list, 1); if (found_dev) { *dev = test_dev; UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } else { UVC_EXIT(UVC_ERROR_NO_DEVICE); return UVC_ERROR_NO_DEVICE; } } /** @brief Finds all cameras identified by vendor, product and/or serial number * @ingroup device * * @param[in] ctx UVC context in which to search for the camera * @param[out] devs List of matching cameras * @param[in] vid Vendor ID number, optional * @param[in] pid Product ID number, optional * @param[in] sn Serial number or NULL * @return Error finding device or UVC_SUCCESS */ uvc_error_t uvc_find_devices( uvc_context_t *ctx, uvc_device_t ***devs, int vid, int pid, const char *sn) { uvc_error_t ret = UVC_SUCCESS; uvc_device_t **list; uvc_device_t *test_dev; int dev_idx; int found_dev; uvc_device_t **list_internal; int num_uvc_devices; UVC_ENTER(); ret = uvc_get_device_list(ctx, &list); if (ret != UVC_SUCCESS) { UVC_EXIT(ret); return ret; } num_uvc_devices = 0; dev_idx = 0; found_dev = 0; list_internal = malloc(sizeof(*list_internal)); *list_internal = NULL; while ((test_dev = list[dev_idx++]) != NULL) { uvc_device_descriptor_t *desc; if (uvc_get_device_descriptor(test_dev, &desc) != UVC_SUCCESS) continue; if ((!vid || desc->idVendor == vid) && (!pid || desc->idProduct == pid) && (!sn || (desc->serialNumber && !strcmp(desc->serialNumber, sn)))) { found_dev = 1; uvc_ref_device(test_dev); num_uvc_devices++; list_internal = realloc(list_internal, (num_uvc_devices + 1) * sizeof(*list_internal)); list_internal[num_uvc_devices - 1] = test_dev; list_internal[num_uvc_devices] = NULL; } uvc_free_device_descriptor(desc); } uvc_free_device_list(list, 1); if (found_dev) { *devs = list_internal; UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } else { UVC_EXIT(UVC_ERROR_NO_DEVICE); return UVC_ERROR_NO_DEVICE; } } /** @brief Get the number of the bus to which the device is attached * @ingroup device */ uint8_t uvc_get_bus_number(uvc_device_t *dev) { return libusb_get_bus_number(dev->usb_dev); } /** @brief Get the number assigned to the device within its bus * @ingroup device */ uint8_t uvc_get_device_address(uvc_device_t *dev) { return libusb_get_device_address(dev->usb_dev); } static uvc_error_t uvc_open_internal(uvc_device_t *dev, struct libusb_device_handle *usb_devh, uvc_device_handle_t **devh); #if LIBUSB_API_VERSION >= 0x01000107 /** @brief Wrap a platform-specific system device handle and obtain a UVC device handle. * The handle allows you to use libusb to perform I/O on the device in question. * * On Linux, the system device handle must be a valid file descriptor opened on the device node. * * The system device handle must remain open until uvc_close() is called. The system device handle will not be closed by uvc_close(). * @ingroup device * * @param sys_dev the platform-specific system device handle * @param context UVC context to prepare the device * @param[out] devh Handle on opened device * @return Error opening device or SUCCESS */ uvc_error_t uvc_wrap( int sys_dev, uvc_context_t *context, uvc_device_handle_t **devh) { uvc_error_t ret; struct libusb_device_handle *usb_devh; UVC_ENTER(); uvc_device_t *dev = NULL; ret = libusb_wrap_sys_device(context->usb_ctx, sys_dev, &usb_devh); UVC_DEBUG("libusb_wrap_sys_device() = %d", ret); if (ret != LIBUSB_SUCCESS) { UVC_EXIT(ret); return ret; } dev = calloc(1, sizeof(uvc_device_t)); dev->ctx = context; dev->usb_dev = libusb_get_device(usb_devh); ret = uvc_open_internal(dev, usb_devh, devh); UVC_EXIT(ret); return ret; } #endif /** @brief Open a UVC device * @ingroup device * * @param dev Device to open * @param[out] devh Handle on opened device * @return Error opening device or SUCCESS */ uvc_error_t uvc_open( uvc_device_t *dev, uvc_device_handle_t **devh) { uvc_error_t ret; struct libusb_device_handle *usb_devh; UVC_ENTER(); ret = libusb_open(dev->usb_dev, &usb_devh); UVC_DEBUG("libusb_open() = %d", ret); if (ret != UVC_SUCCESS) { UVC_EXIT(ret); return ret; } ret = uvc_open_internal(dev, usb_devh, devh); UVC_EXIT(ret); return ret; } static uvc_error_t uvc_open_internal( uvc_device_t *dev, struct libusb_device_handle *usb_devh, uvc_device_handle_t **devh) { uvc_error_t ret; uvc_device_handle_t *internal_devh; struct libusb_device_descriptor desc; UVC_ENTER(); uvc_ref_device(dev); internal_devh = calloc(1, sizeof(*internal_devh)); internal_devh->dev = dev; internal_devh->usb_devh = usb_devh; ret = uvc_get_device_info(dev, &(internal_devh->info)); if (ret != UVC_SUCCESS) goto fail; UVC_DEBUG("claiming control interface %d", internal_devh->info->ctrl_if.bInterfaceNumber); ret = uvc_claim_if(internal_devh, internal_devh->info->ctrl_if.bInterfaceNumber); if (ret != UVC_SUCCESS) goto fail; libusb_get_device_descriptor(dev->usb_dev, &desc); internal_devh->is_isight = (desc.idVendor == 0x05ac && desc.idProduct == 0x8501); if (internal_devh->info->ctrl_if.bEndpointAddress) { internal_devh->status_xfer = libusb_alloc_transfer(0); if (!internal_devh->status_xfer) { ret = UVC_ERROR_NO_MEM; goto fail; } libusb_fill_interrupt_transfer(internal_devh->status_xfer, usb_devh, internal_devh->info->ctrl_if.bEndpointAddress, internal_devh->status_buf, sizeof(internal_devh->status_buf), _uvc_status_callback, internal_devh, 0); ret = libusb_submit_transfer(internal_devh->status_xfer); UVC_DEBUG("libusb_submit_transfer() = %d", ret); if (ret) { fprintf(stderr, "uvc: device has a status interrupt endpoint, but unable to read from it\n"); goto fail; } } if (dev->ctx->own_usb_ctx && dev->ctx->open_devices == NULL) { /* Since this is our first device, we need to spawn the event handler thread */ uvc_start_handler_thread(dev->ctx); } DL_APPEND(dev->ctx->open_devices, internal_devh); *devh = internal_devh; UVC_EXIT(ret); return ret; fail: if ( internal_devh->info ) { uvc_release_if(internal_devh, internal_devh->info->ctrl_if.bInterfaceNumber); } libusb_close(usb_devh); uvc_unref_device(dev); uvc_free_devh(internal_devh); UVC_EXIT(ret); return ret; } /** * @internal * @brief Parses the complete device descriptor for a device * @ingroup device * @note Free *info with uvc_free_device_info when you're done * * @param dev Device to parse descriptor for * @param info Where to store a pointer to the new info struct */ uvc_error_t uvc_get_device_info(uvc_device_t *dev, uvc_device_info_t **info) { uvc_error_t ret; uvc_device_info_t *internal_info; UVC_ENTER(); internal_info = calloc(1, sizeof(*internal_info)); if (!internal_info) { UVC_EXIT(UVC_ERROR_NO_MEM); return UVC_ERROR_NO_MEM; } if (libusb_get_config_descriptor(dev->usb_dev, 0, &(internal_info->config)) != 0) { free(internal_info); UVC_EXIT(UVC_ERROR_IO); return UVC_ERROR_IO; } ret = uvc_scan_control(dev, internal_info); if (ret != UVC_SUCCESS) { uvc_free_device_info(internal_info); UVC_EXIT(ret); return ret; } *info = internal_info; UVC_EXIT(ret); return ret; } /** * @internal * @brief Frees the device descriptor for a device * @ingroup device * * @param info Which device info block to free */ void uvc_free_device_info(uvc_device_info_t *info) { uvc_input_terminal_t *input_term, *input_term_tmp; uvc_processing_unit_t *proc_unit, *proc_unit_tmp; uvc_extension_unit_t *ext_unit, *ext_unit_tmp; uvc_streaming_interface_t *stream_if, *stream_if_tmp; uvc_format_desc_t *format, *format_tmp; uvc_frame_desc_t *frame, *frame_tmp; uvc_still_frame_desc_t *still_frame, *still_frame_tmp; uvc_still_frame_res_t *still_res, *still_res_tmp; UVC_ENTER(); DL_FOREACH_SAFE(info->ctrl_if.input_term_descs, input_term, input_term_tmp) { DL_DELETE(info->ctrl_if.input_term_descs, input_term); free(input_term); } DL_FOREACH_SAFE(info->ctrl_if.processing_unit_descs, proc_unit, proc_unit_tmp) { DL_DELETE(info->ctrl_if.processing_unit_descs, proc_unit); free(proc_unit); } DL_FOREACH_SAFE(info->ctrl_if.extension_unit_descs, ext_unit, ext_unit_tmp) { DL_DELETE(info->ctrl_if.extension_unit_descs, ext_unit); free(ext_unit); } DL_FOREACH_SAFE(info->stream_ifs, stream_if, stream_if_tmp) { DL_FOREACH_SAFE(stream_if->format_descs, format, format_tmp) { DL_FOREACH_SAFE(format->frame_descs, frame, frame_tmp) { if (frame->intervals) free(frame->intervals); DL_DELETE(format->frame_descs, frame); free(frame); } if(format->still_frame_desc) { DL_FOREACH_SAFE(format->still_frame_desc, still_frame, still_frame_tmp) { DL_FOREACH_SAFE(still_frame->imageSizePatterns, still_res, still_res_tmp) { free(still_res); } if(still_frame->bCompression) { free(still_frame->bCompression); } free(still_frame); } } DL_DELETE(stream_if->format_descs, format); free(format); } DL_DELETE(info->stream_ifs, stream_if); free(stream_if); } if (info->config) libusb_free_config_descriptor(info->config); free(info); UVC_EXIT_VOID(); } /** * @brief Get a descriptor that contains the general information about * a device * @ingroup device * * Free *desc with uvc_free_device_descriptor when you're done. * * @param dev Device to fetch information about * @param[out] desc Descriptor structure * @return Error if unable to fetch information, else SUCCESS */ uvc_error_t uvc_get_device_descriptor( uvc_device_t *dev, uvc_device_descriptor_t **desc) { uvc_device_descriptor_t *desc_internal; struct libusb_device_descriptor usb_desc; struct libusb_device_handle *usb_devh; uvc_error_t ret; UVC_ENTER(); ret = libusb_get_device_descriptor(dev->usb_dev, &usb_desc); if (ret != UVC_SUCCESS) { UVC_EXIT(ret); return ret; } desc_internal = calloc(1, sizeof(*desc_internal)); desc_internal->idVendor = usb_desc.idVendor; desc_internal->idProduct = usb_desc.idProduct; if (libusb_open(dev->usb_dev, &usb_devh) == 0) { unsigned char buf[64]; int bytes = libusb_get_string_descriptor_ascii( usb_devh, usb_desc.iSerialNumber, buf, sizeof(buf)); if (bytes > 0) desc_internal->serialNumber = strdup((const char*) buf); bytes = libusb_get_string_descriptor_ascii( usb_devh, usb_desc.iManufacturer, buf, sizeof(buf)); if (bytes > 0) desc_internal->manufacturer = strdup((const char*) buf); bytes = libusb_get_string_descriptor_ascii( usb_devh, usb_desc.iProduct, buf, sizeof(buf)); if (bytes > 0) desc_internal->product = strdup((const char*) buf); libusb_close(usb_devh); } else { UVC_DEBUG("can't open device %04x:%04x, not fetching serial etc.", usb_desc.idVendor, usb_desc.idProduct); } *desc = desc_internal; UVC_EXIT(ret); return ret; } /** * @brief Frees a device descriptor created with uvc_get_device_descriptor * @ingroup device * * @param desc Descriptor to free */ void uvc_free_device_descriptor( uvc_device_descriptor_t *desc) { UVC_ENTER(); if (desc->serialNumber) free((void*) desc->serialNumber); if (desc->manufacturer) free((void*) desc->manufacturer); if (desc->product) free((void*) desc->product); free(desc); UVC_EXIT_VOID(); } /** * @brief Get a list of the UVC devices attached to the system * @ingroup device * * @note Free the list with uvc_free_device_list when you're done. * * @param ctx UVC context in which to list devices * @param list List of uvc_device structures * @return Error if unable to list devices, else SUCCESS */ uvc_error_t uvc_get_device_list( uvc_context_t *ctx, uvc_device_t ***list) { struct libusb_device **usb_dev_list; struct libusb_device *usb_dev; int num_usb_devices; uvc_device_t **list_internal; int num_uvc_devices; /* per device */ int dev_idx; struct libusb_config_descriptor *config; struct libusb_device_descriptor desc; uint8_t got_interface; /* per interface */ int interface_idx; const struct libusb_interface *interface; /* per altsetting */ int altsetting_idx; const struct libusb_interface_descriptor *if_desc; UVC_ENTER(); num_usb_devices = libusb_get_device_list(ctx->usb_ctx, &usb_dev_list); if (num_usb_devices < 0) { UVC_EXIT(UVC_ERROR_IO); return UVC_ERROR_IO; } list_internal = malloc(sizeof(*list_internal)); *list_internal = NULL; num_uvc_devices = 0; dev_idx = -1; while ((usb_dev = usb_dev_list[++dev_idx]) != NULL) { got_interface = 0; if (libusb_get_config_descriptor(usb_dev, 0, &config) != 0) continue; if ( libusb_get_device_descriptor ( usb_dev, &desc ) != LIBUSB_SUCCESS ) continue; for (interface_idx = 0; !got_interface && interface_idx < config->bNumInterfaces; ++interface_idx) { interface = &config->interface[interface_idx]; for (altsetting_idx = 0; !got_interface && altsetting_idx < interface->num_altsetting; ++altsetting_idx) { if_desc = &interface->altsetting[altsetting_idx]; // Skip TIS cameras that definitely aren't UVC even though they might // look that way if ( 0x199e == desc.idVendor && desc.idProduct >= 0x8201 && desc.idProduct <= 0x8208 ) { continue; } // Special case for Imaging Source cameras /* Video, Streaming */ if ( 0x199e == desc.idVendor && ( 0x8101 == desc.idProduct || 0x8102 == desc.idProduct ) && if_desc->bInterfaceClass == 255 && if_desc->bInterfaceSubClass == 2 ) { got_interface = 1; } /* Video, Streaming */ if (if_desc->bInterfaceClass == 14 && if_desc->bInterfaceSubClass == 2) { got_interface = 1; } } } libusb_free_config_descriptor(config); if (got_interface) { uvc_device_t *uvc_dev = malloc(sizeof(*uvc_dev)); uvc_dev->ctx = ctx; uvc_dev->ref = 0; uvc_dev->usb_dev = usb_dev; uvc_ref_device(uvc_dev); num_uvc_devices++; list_internal = realloc(list_internal, (num_uvc_devices + 1) * sizeof(*list_internal)); list_internal[num_uvc_devices - 1] = uvc_dev; list_internal[num_uvc_devices] = NULL; UVC_DEBUG(" UVC: %d", dev_idx); } else { UVC_DEBUG("non-UVC: %d", dev_idx); } } libusb_free_device_list(usb_dev_list, 1); *list = list_internal; UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } /** * @brief Frees a list of device structures created with uvc_get_device_list. * @ingroup device * * @param list Device list to free * @param unref_devices Decrement the reference counter for each device * in the list, and destroy any entries that end up with zero references */ void uvc_free_device_list(uvc_device_t **list, uint8_t unref_devices) { uvc_device_t *dev; int dev_idx = 0; UVC_ENTER(); if (unref_devices) { while ((dev = list[dev_idx++]) != NULL) { uvc_unref_device(dev); } } free(list); UVC_EXIT_VOID(); } /** * @brief Get the uvc_device_t corresponding to an open device * @ingroup device * * @note Unref the uvc_device_t when you're done with it * * @param devh Device handle to an open UVC device */ uvc_device_t *uvc_get_device(uvc_device_handle_t *devh) { uvc_ref_device(devh->dev); return devh->dev; } /** * @brief Get the underlying libusb device handle for an open device * @ingroup device * * This can be used to access other interfaces on the same device, e.g. * a webcam microphone. * * @note The libusb device handle is only valid while the UVC device is open; * it will be invalidated upon calling uvc_close. * * @param devh UVC device handle to an open device */ libusb_device_handle *uvc_get_libusb_handle(uvc_device_handle_t *devh) { return devh->usb_devh; } /** * @brief Get camera terminal descriptor for the open device. * * @note Do not modify the returned structure. * @note The returned structure is part of a linked list, but iterating through * it will make it no longer the camera terminal * * @param devh Device handle to an open UVC device */ const uvc_input_terminal_t *uvc_get_camera_terminal(uvc_device_handle_t *devh) { const uvc_input_terminal_t *term = uvc_get_input_terminals(devh); while(term != NULL) { if (term->wTerminalType == UVC_ITT_CAMERA) { break; } else { term = term->next; } } return term; } /** * @brief Get input terminal descriptors for the open device. * * @note Do not modify the returned structure. * @note The returned structure is part of a linked list. Iterate through * it by using the 'next' pointers. * * @param devh Device handle to an open UVC device */ const uvc_input_terminal_t *uvc_get_input_terminals(uvc_device_handle_t *devh) { return devh->info->ctrl_if.input_term_descs; } /** * @brief Get output terminal descriptors for the open device. * * @note Do not modify the returned structure. * @note The returned structure is part of a linked list. Iterate through * it by using the 'next' pointers. * * @param devh Device handle to an open UVC device */ const uvc_output_terminal_t *uvc_get_output_terminals(uvc_device_handle_t *devh) { return NULL; /* @todo */ } /** * @brief Get selector unit descriptors for the open device. * * @note Do not modify the returned structure. * @note The returned structure is part of a linked list. Iterate through * it by using the 'next' pointers. * * @param devh Device handle to an open UVC device */ const uvc_selector_unit_t *uvc_get_selector_units(uvc_device_handle_t *devh) { return devh->info->ctrl_if.selector_unit_descs; } /** * @brief Get processing unit descriptors for the open device. * * @note Do not modify the returned structure. * @note The returned structure is part of a linked list. Iterate through * it by using the 'next' pointers. * * @param devh Device handle to an open UVC device */ const uvc_processing_unit_t *uvc_get_processing_units(uvc_device_handle_t *devh) { return devh->info->ctrl_if.processing_unit_descs; } /** * @brief Get extension unit descriptors for the open device. * * @note Do not modify the returned structure. * @note The returned structure is part of a linked list. Iterate through * it by using the 'next' pointers. * * @param devh Device handle to an open UVC device */ const uvc_extension_unit_t *uvc_get_extension_units(uvc_device_handle_t *devh) { return devh->info->ctrl_if.extension_unit_descs; } /** * @brief Increment the reference count for a device * @ingroup device * * @param dev Device to reference */ void uvc_ref_device(uvc_device_t *dev) { UVC_ENTER(); dev->ref++; libusb_ref_device(dev->usb_dev); UVC_EXIT_VOID(); } /** * @brief Decrement the reference count for a device * @ingropu device * @note If the count reaches zero, the device will be discarded * * @param dev Device to unreference */ void uvc_unref_device(uvc_device_t *dev) { UVC_ENTER(); libusb_unref_device(dev->usb_dev); dev->ref--; if (dev->ref == 0) free(dev); UVC_EXIT_VOID(); } /** @internal * Claim a UVC interface, detaching the kernel driver if necessary. * @ingroup device * * @param devh UVC device handle * @param idx UVC interface index */ uvc_error_t uvc_claim_if(uvc_device_handle_t *devh, int idx) { int ret = UVC_SUCCESS; UVC_ENTER(); if ( devh->claimed & ( 1 << idx )) { UVC_DEBUG("attempt to claim already-claimed interface %d\n", idx ); UVC_EXIT(ret); return ret; } /* Tell libusb to detach any active kernel drivers. libusb will keep track of whether * it found a kernel driver for this interface. */ ret = libusb_detach_kernel_driver(devh->usb_devh, idx); if (ret == UVC_SUCCESS || ret == LIBUSB_ERROR_NOT_FOUND || ret == LIBUSB_ERROR_NOT_SUPPORTED) { UVC_DEBUG("claiming interface %d", idx); if (!( ret = libusb_claim_interface(devh->usb_devh, idx))) { devh->claimed |= ( 1 << idx ); } } else { UVC_DEBUG("not claiming interface %d: unable to detach kernel driver (%s)", idx, uvc_strerror(ret)); } UVC_EXIT(ret); return ret; } /** @internal * Release a UVC interface. * @ingroup device * * @param devh UVC device handle * @param idx UVC interface index */ uvc_error_t uvc_release_if(uvc_device_handle_t *devh, int idx) { int ret = UVC_SUCCESS; UVC_ENTER(); UVC_DEBUG("releasing interface %d", idx); if (!( devh->claimed & ( 1 << idx ))) { UVC_DEBUG("attempt to release unclaimed interface %d\n", idx ); UVC_EXIT(ret); return ret; } /* libusb_release_interface *should* reset the alternate setting to the first available, but sometimes (e.g. on Darwin) it doesn't. Thus, we do it explicitly here. This is needed to de-initialize certain cameras. */ libusb_set_interface_alt_setting(devh->usb_devh, idx, 0); ret = libusb_release_interface(devh->usb_devh, idx); if (UVC_SUCCESS == ret) { devh->claimed &= ~( 1 << idx ); /* Reattach any kernel drivers that were disabled when we claimed this interface */ ret = libusb_attach_kernel_driver(devh->usb_devh, idx); if (ret == UVC_SUCCESS) { UVC_DEBUG("reattached kernel driver to interface %d", idx); } else if (ret == LIBUSB_ERROR_NOT_FOUND || ret == LIBUSB_ERROR_NOT_SUPPORTED) { ret = UVC_SUCCESS; /* NOT_FOUND and NOT_SUPPORTED are OK: nothing to do */ } else { UVC_DEBUG("error reattaching kernel driver to interface %d: %s", idx, uvc_strerror(ret)); } } UVC_EXIT(ret); return ret; } /** @internal * Find a device's VideoControl interface and process its descriptor * @ingroup device */ uvc_error_t uvc_scan_control(uvc_device_t *dev, uvc_device_info_t *info) { const struct libusb_interface_descriptor *if_desc; uvc_error_t parse_ret, ret; int interface_idx; const unsigned char *buffer; size_t buffer_left, block_size; UVC_ENTER(); ret = UVC_SUCCESS; if_desc = NULL; uvc_device_descriptor_t* dev_desc; int haveTISCamera = 0; uvc_get_device_descriptor ( dev, &dev_desc ); if ( 0x199e == dev_desc->idVendor && ( 0x8101 == dev_desc->idProduct || 0x8102 == dev_desc->idProduct )) { haveTISCamera = 1; } uvc_free_device_descriptor ( dev_desc ); for (interface_idx = 0; interface_idx < info->config->bNumInterfaces; ++interface_idx) { if_desc = &info->config->interface[interface_idx].altsetting[0]; if ( haveTISCamera && if_desc->bInterfaceClass == 255 && if_desc->bInterfaceSubClass == 1) // Video, Control break; if (if_desc->bInterfaceClass == 14 && if_desc->bInterfaceSubClass == 1) // Video, Control break; if_desc = NULL; } if (if_desc == NULL) { UVC_EXIT(UVC_ERROR_INVALID_DEVICE); return UVC_ERROR_INVALID_DEVICE; } info->ctrl_if.bInterfaceNumber = interface_idx; if (if_desc->bNumEndpoints != 0) { info->ctrl_if.bEndpointAddress = if_desc->endpoint[0].bEndpointAddress; } buffer = if_desc->extra; buffer_left = if_desc->extra_length; while (buffer_left >= 3) { // parseX needs to see buf[0,2] = length,type block_size = buffer[0]; parse_ret = uvc_parse_vc(dev, info, buffer, block_size); if (parse_ret != UVC_SUCCESS) { ret = parse_ret; break; } buffer_left -= block_size; buffer += block_size; } UVC_EXIT(ret); return ret; } /** @internal * @brief Parse a VideoControl header. * @ingroup device */ uvc_error_t uvc_parse_vc_header(uvc_device_t *dev, uvc_device_info_t *info, const unsigned char *block, size_t block_size) { size_t i; uvc_error_t scan_ret, ret = UVC_SUCCESS; UVC_ENTER(); /* int uvc_version; uvc_version = (block[4] >> 4) * 1000 + (block[4] & 0x0f) * 100 + (block[3] >> 4) * 10 + (block[3] & 0x0f); */ info->ctrl_if.bcdUVC = SW_TO_SHORT(&block[3]); switch (info->ctrl_if.bcdUVC) { case 0x0100: info->ctrl_if.dwClockFrequency = DW_TO_INT(block + 7); case 0x010a: info->ctrl_if.dwClockFrequency = DW_TO_INT(block + 7); case 0x0110: break; default: UVC_EXIT(UVC_ERROR_NOT_SUPPORTED); return UVC_ERROR_NOT_SUPPORTED; } for (i = 12; i < block_size; ++i) { scan_ret = uvc_scan_streaming(dev, info, block[i]); if (scan_ret != UVC_SUCCESS) { ret = scan_ret; break; } } UVC_EXIT(ret); return ret; } /** @internal * @brief Parse a VideoControl input terminal. * @ingroup device */ uvc_error_t uvc_parse_vc_input_terminal(uvc_device_t *dev, uvc_device_info_t *info, const unsigned char *block, size_t block_size) { uvc_input_terminal_t *term; size_t i; UVC_ENTER(); /* only supporting camera-type input terminals */ if (SW_TO_SHORT(&block[4]) != UVC_ITT_CAMERA) { UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } term = calloc(1, sizeof(*term)); term->bTerminalID = block[3]; term->wTerminalType = SW_TO_SHORT(&block[4]); term->wObjectiveFocalLengthMin = SW_TO_SHORT(&block[8]); term->wObjectiveFocalLengthMax = SW_TO_SHORT(&block[10]); term->wOcularFocalLength = SW_TO_SHORT(&block[12]); for (i = 14 + block[14]; i >= 15; --i) term->bmControls = block[i] + (term->bmControls << 8); DL_APPEND(info->ctrl_if.input_term_descs, term); UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } /** @internal * @brief Parse a VideoControl processing unit. * @ingroup device */ uvc_error_t uvc_parse_vc_processing_unit(uvc_device_t *dev, uvc_device_info_t *info, const unsigned char *block, size_t block_size) { uvc_processing_unit_t *unit; size_t i; UVC_ENTER(); unit = calloc(1, sizeof(*unit)); unit->bUnitID = block[3]; unit->bSourceID = block[4]; for (i = 7 + block[7]; i >= 8; --i) unit->bmControls = block[i] + (unit->bmControls << 8); DL_APPEND(info->ctrl_if.processing_unit_descs, unit); UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } /** @internal * @brief Parse a VideoControl selector unit. * @ingroup device */ uvc_error_t uvc_parse_vc_selector_unit(uvc_device_t *dev, uvc_device_info_t *info, const unsigned char *block, size_t block_size) { uvc_selector_unit_t *unit; UVC_ENTER(); unit = calloc(1, sizeof(*unit)); unit->bUnitID = block[3]; DL_APPEND(info->ctrl_if.selector_unit_descs, unit); UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } /** @internal * @brief Parse a VideoControl extension unit. * @ingroup device */ uvc_error_t uvc_parse_vc_extension_unit(uvc_device_t *dev, uvc_device_info_t *info, const unsigned char *block, size_t block_size) { uvc_extension_unit_t *unit = calloc(1, sizeof(*unit)); const uint8_t *start_of_controls; int size_of_controls, num_in_pins; int i; UVC_ENTER(); unit->bUnitID = block[3]; memcpy(unit->guidExtensionCode, &block[4], 16); num_in_pins = block[21]; size_of_controls = block[22 + num_in_pins]; start_of_controls = &block[23 + num_in_pins]; for (i = size_of_controls - 1; i >= 0; --i) unit->bmControls = start_of_controls[i] + (unit->bmControls << 8); DL_APPEND(info->ctrl_if.extension_unit_descs, unit); UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } /** @internal * Process a single VideoControl descriptor block * @ingroup device */ uvc_error_t uvc_parse_vc( uvc_device_t *dev, uvc_device_info_t *info, const unsigned char *block, size_t block_size) { int descriptor_subtype; uvc_error_t ret = UVC_SUCCESS; UVC_ENTER(); if (block[1] != 36) { // not a CS_INTERFACE descriptor?? UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; // UVC_ERROR_INVALID_DEVICE; } descriptor_subtype = block[2]; switch (descriptor_subtype) { case UVC_VC_HEADER: ret = uvc_parse_vc_header(dev, info, block, block_size); break; case UVC_VC_INPUT_TERMINAL: ret = uvc_parse_vc_input_terminal(dev, info, block, block_size); break; case UVC_VC_OUTPUT_TERMINAL: break; case UVC_VC_SELECTOR_UNIT: ret = uvc_parse_vc_selector_unit(dev, info, block, block_size); break; case UVC_VC_PROCESSING_UNIT: ret = uvc_parse_vc_processing_unit(dev, info, block, block_size); break; case UVC_VC_EXTENSION_UNIT: ret = uvc_parse_vc_extension_unit(dev, info, block, block_size); break; default: ret = UVC_ERROR_INVALID_DEVICE; } UVC_EXIT(ret); return ret; } /** @internal * Process a VideoStreaming interface * @ingroup device */ uvc_error_t uvc_scan_streaming(uvc_device_t *dev, uvc_device_info_t *info, int interface_idx) { const struct libusb_interface_descriptor *if_desc; const unsigned char *buffer; size_t buffer_left, block_size; uvc_error_t ret, parse_ret; uvc_streaming_interface_t *stream_if; UVC_ENTER(); ret = UVC_SUCCESS; if_desc = &(info->config->interface[interface_idx].altsetting[0]); buffer = if_desc->extra; buffer_left = if_desc->extra_length; stream_if = calloc(1, sizeof(*stream_if)); stream_if->parent = info; stream_if->bInterfaceNumber = if_desc->bInterfaceNumber; DL_APPEND(info->stream_ifs, stream_if); while (buffer_left >= 3) { block_size = buffer[0]; parse_ret = uvc_parse_vs(dev, info, stream_if, buffer, block_size); if (parse_ret != UVC_SUCCESS) { ret = parse_ret; break; } buffer_left -= block_size; buffer += block_size; } UVC_EXIT(ret); return ret; } /** @internal * @brief Parse a VideoStreaming header block. * @ingroup device */ uvc_error_t uvc_parse_vs_input_header(uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size) { UVC_ENTER(); stream_if->bEndpointAddress = block[6] & 0x8f; stream_if->bTerminalLink = block[8]; stream_if->bStillCaptureMethod = block[9]; UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } /** @internal * @brief Parse a VideoStreaming uncompressed format block. * @ingroup device */ uvc_error_t uvc_parse_vs_format_uncompressed(uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size) { UVC_ENTER(); uvc_format_desc_t *format = calloc(1, sizeof(*format)); format->parent = stream_if; format->bDescriptorSubtype = block[2]; format->bFormatIndex = block[3]; //format->bmCapabilities = block[4]; //format->bmFlags = block[5]; memcpy(format->guidFormat, &block[5], 16); format->bBitsPerPixel = block[21]; format->bDefaultFrameIndex = block[22]; format->bAspectRatioX = block[23]; format->bAspectRatioY = block[24]; format->bmInterlaceFlags = block[25]; format->bCopyProtect = block[26]; DL_APPEND(stream_if->format_descs, format); UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } /** @internal * @brief Parse a VideoStreaming frame format block. * @ingroup device */ uvc_error_t uvc_parse_vs_frame_format(uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size) { UVC_ENTER(); uvc_format_desc_t *format = calloc(1, sizeof(*format)); format->parent = stream_if; format->bDescriptorSubtype = block[2]; format->bFormatIndex = block[3]; format->bNumFrameDescriptors = block[4]; memcpy(format->guidFormat, &block[5], 16); format->bBitsPerPixel = block[21]; format->bDefaultFrameIndex = block[22]; format->bAspectRatioX = block[23]; format->bAspectRatioY = block[24]; format->bmInterlaceFlags = block[25]; format->bCopyProtect = block[26]; format->bVariableSize = block[27]; DL_APPEND(stream_if->format_descs, format); UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } /** @internal * @brief Parse a VideoStreaming MJPEG format block. * @ingroup device */ uvc_error_t uvc_parse_vs_format_mjpeg(uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size) { UVC_ENTER(); uvc_format_desc_t *format = calloc(1, sizeof(*format)); format->parent = stream_if; format->bDescriptorSubtype = block[2]; format->bFormatIndex = block[3]; memcpy(format->fourccFormat, "MJPG", 4); format->bmFlags = block[5]; format->bBitsPerPixel = 0; format->bDefaultFrameIndex = block[6]; format->bAspectRatioX = block[7]; format->bAspectRatioY = block[8]; format->bmInterlaceFlags = block[9]; format->bCopyProtect = block[10]; DL_APPEND(stream_if->format_descs, format); UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } /** @internal * @brief Parse a VideoStreaming uncompressed frame block. * @ingroup device */ uvc_error_t uvc_parse_vs_frame_frame(uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size) { uvc_format_desc_t *format; uvc_frame_desc_t *frame; const unsigned char *p; int i; UVC_ENTER(); format = stream_if->format_descs->prev; frame = calloc(1, sizeof(*frame)); frame->parent = format; frame->bDescriptorSubtype = block[2]; frame->bFrameIndex = block[3]; frame->bmCapabilities = block[4]; frame->wWidth = block[5] + (block[6] << 8); frame->wHeight = block[7] + (block[8] << 8); frame->dwMinBitRate = DW_TO_INT(&block[9]); frame->dwMaxBitRate = DW_TO_INT(&block[13]); frame->dwDefaultFrameInterval = DW_TO_INT(&block[17]); frame->bFrameIntervalType = block[21]; frame->dwBytesPerLine = DW_TO_INT(&block[22]); if (block[21] == 0) { frame->dwMinFrameInterval = DW_TO_INT(&block[26]); frame->dwMaxFrameInterval = DW_TO_INT(&block[30]); frame->dwFrameIntervalStep = DW_TO_INT(&block[34]); } else { frame->intervals = calloc(block[21] + 1, sizeof(frame->intervals[0])); p = &block[26]; for (i = 0; i < block[21]; ++i) { frame->intervals[i] = DW_TO_INT(p); p += 4; } frame->intervals[block[21]] = 0; } DL_APPEND(format->frame_descs, frame); UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } /** @internal * @brief Parse a VideoStreaming uncompressed frame block. * @ingroup device */ uvc_error_t uvc_parse_vs_frame_uncompressed(uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size) { uvc_format_desc_t *format; uvc_frame_desc_t *frame; const unsigned char *p; int i; UVC_ENTER(); format = stream_if->format_descs->prev; frame = calloc(1, sizeof(*frame)); frame->parent = format; frame->bDescriptorSubtype = block[2]; frame->bFrameIndex = block[3]; frame->bmCapabilities = block[4]; frame->wWidth = block[5] + (block[6] << 8); frame->wHeight = block[7] + (block[8] << 8); frame->dwMinBitRate = DW_TO_INT(&block[9]); frame->dwMaxBitRate = DW_TO_INT(&block[13]); frame->dwMaxVideoFrameBufferSize = DW_TO_INT(&block[17]); frame->dwDefaultFrameInterval = DW_TO_INT(&block[21]); frame->bFrameIntervalType = block[25]; if (block[25] == 0) { frame->dwMinFrameInterval = DW_TO_INT(&block[26]); frame->dwMaxFrameInterval = DW_TO_INT(&block[30]); frame->dwFrameIntervalStep = DW_TO_INT(&block[34]); } else { frame->intervals = calloc(block[25] + 1, sizeof(frame->intervals[0])); p = &block[26]; for (i = 0; i < block[25]; ++i) { frame->intervals[i] = DW_TO_INT(p); p += 4; } frame->intervals[block[25]] = 0; } DL_APPEND(format->frame_descs, frame); UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } /** @internal * @brief Parse a VideoStreaming still iamge frame * @ingroup device */ uvc_error_t uvc_parse_vs_still_image_frame(uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size) { struct uvc_still_frame_desc* frame; uvc_format_desc_t *format; const unsigned char *p; int i; UVC_ENTER(); format = stream_if->format_descs->prev; frame = calloc(1, sizeof(*frame)); frame->parent = format; frame->bDescriptorSubtype = block[2]; frame->bEndPointAddress = block[3]; uint8_t numImageSizePatterns = block[4]; frame->imageSizePatterns = NULL; p = &block[5]; for (i = 1; i <= numImageSizePatterns; ++i) { uvc_still_frame_res_t* res = calloc(1, sizeof(uvc_still_frame_res_t)); res->bResolutionIndex = i; res->wWidth = SW_TO_SHORT(p); p += 2; res->wHeight = SW_TO_SHORT(p); p += 2; DL_APPEND(frame->imageSizePatterns, res); } p = &block[5+4*numImageSizePatterns]; frame->bNumCompressionPattern = *p; if(frame->bNumCompressionPattern) { frame->bCompression = calloc(frame->bNumCompressionPattern, sizeof(frame->bCompression[0])); for(i = 0; i < frame->bNumCompressionPattern; ++i) { ++p; frame->bCompression[i] = *p; } } else { frame->bCompression = NULL; } DL_APPEND(format->still_frame_desc, frame); UVC_EXIT(UVC_SUCCESS); return UVC_SUCCESS; } /** @internal * Process a single VideoStreaming descriptor block * @ingroup device */ uvc_error_t uvc_parse_vs( uvc_device_t *dev, uvc_device_info_t *info, uvc_streaming_interface_t *stream_if, const unsigned char *block, size_t block_size) { uvc_error_t ret; int descriptor_subtype; UVC_ENTER(); ret = UVC_SUCCESS; descriptor_subtype = block[2]; switch (descriptor_subtype) { case UVC_VS_INPUT_HEADER: ret = uvc_parse_vs_input_header(stream_if, block, block_size); break; case UVC_VS_OUTPUT_HEADER: UVC_DEBUG("unsupported descriptor subtype VS_OUTPUT_HEADER"); break; case UVC_VS_STILL_IMAGE_FRAME: ret = uvc_parse_vs_still_image_frame(stream_if, block, block_size); break; case UVC_VS_FORMAT_UNCOMPRESSED: ret = uvc_parse_vs_format_uncompressed(stream_if, block, block_size); break; case UVC_VS_FORMAT_MJPEG: ret = uvc_parse_vs_format_mjpeg(stream_if, block, block_size); break; case UVC_VS_FRAME_UNCOMPRESSED: case UVC_VS_FRAME_MJPEG: ret = uvc_parse_vs_frame_uncompressed(stream_if, block, block_size); break; case UVC_VS_FORMAT_MPEG2TS: UVC_DEBUG("unsupported descriptor subtype VS_FORMAT_MPEG2TS"); break; case UVC_VS_FORMAT_DV: UVC_DEBUG("unsupported descriptor subtype VS_FORMAT_DV"); break; case UVC_VS_COLORFORMAT: UVC_DEBUG("unsupported descriptor subtype VS_COLORFORMAT"); break; case UVC_VS_FORMAT_FRAME_BASED: ret = uvc_parse_vs_frame_format ( stream_if, block, block_size ); break; case UVC_VS_FRAME_FRAME_BASED: ret = uvc_parse_vs_frame_frame ( stream_if, block, block_size ); break; case UVC_VS_FORMAT_STREAM_BASED: UVC_DEBUG("unsupported descriptor subtype VS_FORMAT_STREAM_BASED"); break; default: /** @todo handle JPEG and maybe still frames or even DV... */ //UVC_DEBUG("unsupported descriptor subtype: %d",descriptor_subtype); break; } UVC_EXIT(ret); return ret; } /** @internal * @brief Free memory associated with a UVC device * @pre Streaming must be stopped, and threads must have died */ void uvc_free_devh(uvc_device_handle_t *devh) { UVC_ENTER(); if (devh->info) uvc_free_device_info(devh->info); if (devh->status_xfer) libusb_free_transfer(devh->status_xfer); free(devh); UVC_EXIT_VOID(); } /** @brief Close a device * * @ingroup device * * Ends any stream that's in progress. * * The device handle and frame structures will be invalidated. */ void uvc_close(uvc_device_handle_t *devh) { UVC_ENTER(); uvc_context_t *ctx = devh->dev->ctx; if (devh->streams) uvc_stop_streaming(devh); uvc_release_if(devh, devh->info->ctrl_if.bInterfaceNumber); /* If we are managing the libusb context and this is the last open device, * then we need to cancel the handler thread. When we call libusb_close, * it'll cause a return from the thread's libusb_handle_events call, after * which the handler thread will check the flag we set and then exit. */ if (ctx->own_usb_ctx && ctx->open_devices == devh && devh->next == NULL) { ctx->kill_handler_thread = 1; libusb_close(devh->usb_devh); pthread_join(ctx->handler_thread, NULL); } else { libusb_close(devh->usb_devh); } DL_DELETE(ctx->open_devices, devh); uvc_unref_device(devh->dev); uvc_free_devh(devh); UVC_EXIT_VOID(); } /** @internal * @brief Get number of open devices */ size_t uvc_num_devices(uvc_context_t *ctx) { size_t count = 0; uvc_device_handle_t *devh; UVC_ENTER(); DL_FOREACH(ctx->open_devices, devh) { count++; } UVC_EXIT((int) count); return count; } void uvc_process_control_status(uvc_device_handle_t *devh, unsigned char *data, int len) { enum uvc_status_class status_class; uint8_t originator = 0, selector = 0, event = 0; enum uvc_status_attribute attribute = UVC_STATUS_ATTRIBUTE_UNKNOWN; void *content = NULL; size_t content_len = 0; int found_entity = 0; struct uvc_input_terminal *input_terminal; struct uvc_processing_unit *processing_unit; UVC_ENTER(); if (len < 5) { UVC_DEBUG("Short read of VideoControl status update (%d bytes)", len); UVC_EXIT_VOID(); return; } originator = data[1]; event = data[2]; selector = data[3]; if (originator == 0) { UVC_DEBUG("Unhandled update from VC interface"); UVC_EXIT_VOID(); return; /* @todo VideoControl virtual entity interface updates */ } if (event != 0) { UVC_DEBUG("Unhandled VC event %d", (int) event); UVC_EXIT_VOID(); return; } /* printf("bSelector: %d\n", selector); */ DL_FOREACH(devh->info->ctrl_if.input_term_descs, input_terminal) { if (input_terminal->bTerminalID == originator) { status_class = UVC_STATUS_CLASS_CONTROL_CAMERA; found_entity = 1; break; } } if (!found_entity) { DL_FOREACH(devh->info->ctrl_if.processing_unit_descs, processing_unit) { if (processing_unit->bUnitID == originator) { status_class = UVC_STATUS_CLASS_CONTROL_PROCESSING; found_entity = 1; break; } } } if (!found_entity) { UVC_DEBUG("Got status update for unknown VideoControl entity %d", (int) originator); UVC_EXIT_VOID(); return; } attribute = data[4]; content = data + 5; content_len = len - 5; UVC_DEBUG("Event: class=%d, event=%d, selector=%d, attribute=%d, content_len=%zd", status_class, event, selector, attribute, content_len); if(devh->status_cb) { UVC_DEBUG("Running user-supplied status callback"); devh->status_cb(status_class, event, selector, attribute, content, content_len, devh->status_user_ptr); } UVC_EXIT_VOID(); } void uvc_process_streaming_status(uvc_device_handle_t *devh, unsigned char *data, int len) { UVC_ENTER(); if (len < 3) { UVC_DEBUG("Invalid streaming status event received.\n"); UVC_EXIT_VOID(); return; } if (data[2] == 0) { if (len < 4) { UVC_DEBUG("Short read of status update (%d bytes)", len); UVC_EXIT_VOID(); return; } UVC_DEBUG("Button (intf %u) %s len %d\n", data[1], data[3] ? "pressed" : "released", len); if(devh->button_cb) { UVC_DEBUG("Running user-supplied button callback"); devh->button_cb(data[1], data[3], devh->button_user_ptr); } } else { UVC_DEBUG("Stream %u error event %02x %02x len %d.\n", data[1], data[2], data[3], len); } UVC_EXIT_VOID(); } void uvc_process_status_xfer(uvc_device_handle_t *devh, struct libusb_transfer *transfer) { UVC_ENTER(); /* printf("Got transfer of aLen = %d\n", transfer->actual_length); */ if (transfer->actual_length > 0) { switch (transfer->buffer[0] & 0x0f) { case 1: /* VideoControl interface */ uvc_process_control_status(devh, transfer->buffer, transfer->actual_length); break; case 2: /* VideoStreaming interface */ uvc_process_streaming_status(devh, transfer->buffer, transfer->actual_length); break; } } UVC_EXIT_VOID(); } /** @internal * @brief Process asynchronous status updates from the device. */ void LIBUSB_CALL _uvc_status_callback(struct libusb_transfer *transfer) { UVC_ENTER(); uvc_device_handle_t *devh = (uvc_device_handle_t *) transfer->user_data; switch (transfer->status) { case LIBUSB_TRANSFER_ERROR: case LIBUSB_TRANSFER_CANCELLED: case LIBUSB_TRANSFER_NO_DEVICE: UVC_DEBUG("not processing/resubmitting, status = %d", transfer->status); UVC_EXIT_VOID(); return; case LIBUSB_TRANSFER_COMPLETED: uvc_process_status_xfer(devh, transfer); break; case LIBUSB_TRANSFER_TIMED_OUT: case LIBUSB_TRANSFER_STALL: case LIBUSB_TRANSFER_OVERFLOW: UVC_DEBUG("retrying transfer, status = %d", transfer->status); break; } #ifdef UVC_DEBUGGING uvc_error_t ret = #endif libusb_submit_transfer(transfer); UVC_DEBUG("libusb_submit_transfer() = %d", ret); UVC_EXIT_VOID(); } /** @brief Set a callback function to receive status updates * * @ingroup device */ void uvc_set_status_callback(uvc_device_handle_t *devh, uvc_status_callback_t cb, void *user_ptr) { UVC_ENTER(); devh->status_cb = cb; devh->status_user_ptr = user_ptr; UVC_EXIT_VOID(); } /** @brief Set a callback function to receive button events * * @ingroup device */ void uvc_set_button_callback(uvc_device_handle_t *devh, uvc_button_callback_t cb, void *user_ptr) { UVC_ENTER(); devh->button_cb = cb; devh->button_user_ptr = user_ptr; UVC_EXIT_VOID(); } /** * @brief Get format descriptions for the open device. * * @note Do not modify the returned structure. * * @param devh Device handle to an open UVC device */ const uvc_format_desc_t *uvc_get_format_descs(uvc_device_handle_t *devh) { return devh->info->stream_ifs->format_descs; }