text: /*********************************************************************
  * 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 diag Diagnostics
   */
  
  #include "libuvc/libuvc.h"
  #include "libuvc/libuvc_internal.h"
  
  /** @internal */
  typedef struct _uvc_error_msg {
    uvc_error_t err;
    const char *msg;
  } _uvc_error_msg_t;
  
  static const _uvc_error_msg_t uvc_error_msgs[] = {
    {UVC_SUCCESS, "Success"},
    {UVC_ERROR_IO, "I/O error"},
    {UVC_ERROR_INVALID_PARAM, "Invalid parameter"},
    {UVC_ERROR_ACCESS, "Access denied"},
    {UVC_ERROR_NO_DEVICE, "No such device"},
    {UVC_ERROR_NOT_FOUND, "Not found"},
    {UVC_ERROR_BUSY, "Busy"},
    {UVC_ERROR_TIMEOUT, "Timeout"},
    {UVC_ERROR_OVERFLOW, "Overflow"},
    {UVC_ERROR_PIPE, "Pipe"},
    {UVC_ERROR_INTERRUPTED, "Interrupted"},
    {UVC_ERROR_NO_MEM, "Out of memory"},
    {UVC_ERROR_NOT_SUPPORTED, "Not supported"},
    {UVC_ERROR_INVALID_DEVICE, "Invalid device"},
    {UVC_ERROR_INVALID_MODE, "Invalid mode"},
    {UVC_ERROR_CALLBACK_EXISTS, "Callback exists"}
  };
  
  /** @brief Print a message explaining an error in the UVC driver
   * @ingroup diag
   *
   * @param err UVC error code
   * @param msg Optional custom message, prepended to output
   */
  void uvc_perror(uvc_error_t err, const char *msg) {
    if (msg && *msg) {
      fputs(msg, stderr);
      fputs(": ", stderr);
    }
  
    fprintf(stderr, "%s (%d)\n", uvc_strerror(err), err);
  }
  
  /** @brief Return a string explaining an error in the UVC driver
   * @ingroup diag
   *
   * @param err UVC error code
   * @return error message
   */
  const char* uvc_strerror(uvc_error_t err) {
    size_t idx;
  
    for (idx = 0; idx < sizeof(uvc_error_msgs) / sizeof(*uvc_error_msgs); ++idx) {
      if (uvc_error_msgs[idx].err == err) {
        return uvc_error_msgs[idx].msg;
      }
    }
  
    return "Unknown error";
  }
  
  /** @brief Print the values in a stream control block
   * @ingroup diag
   *
   * @param devh UVC device
   * @param stream Output stream (stderr if NULL)
   */
  void uvc_print_stream_ctrl(uvc_stream_ctrl_t *ctrl, FILE *stream) {
    if (stream == NULL)
      stream = stderr;
  
    fprintf(stream, "bmHint: %04x\n", ctrl->bmHint);
    fprintf(stream, "bFormatIndex: %d\n", ctrl->bFormatIndex);
    fprintf(stream, "bFrameIndex: %d\n", ctrl->bFrameIndex);
    fprintf(stream, "dwFrameInterval: %u\n", ctrl->dwFrameInterval);
    fprintf(stream, "wKeyFrameRate: %d\n", ctrl->wKeyFrameRate);
    fprintf(stream, "wPFrameRate: %d\n", ctrl->wPFrameRate);
    fprintf(stream, "wCompQuality: %d\n", ctrl->wCompQuality);
    fprintf(stream, "wCompWindowSize: %d\n", ctrl->wCompWindowSize);
    fprintf(stream, "wDelay: %d\n", ctrl->wDelay);
    fprintf(stream, "dwMaxVideoFrameSize: %u\n", ctrl->dwMaxVideoFrameSize);
    fprintf(stream, "dwMaxPayloadTransferSize: %u\n", ctrl->dwMaxPayloadTransferSize);
    fprintf(stream, "bInterfaceNumber: %d\n", ctrl->bInterfaceNumber);
  }
  
  static const char *_uvc_name_for_format_subtype(uint8_t subtype) {
    switch (subtype) {
    case UVC_VS_FORMAT_UNCOMPRESSED:
      return "UncompressedFormat";
    case UVC_VS_FORMAT_MJPEG:
      return "MJPEGFormat";
    default:
      return "Unknown";
    }
  }
  
  /** @brief Print camera capabilities and configuration.
   * @ingroup diag
   *
   * @param devh UVC device
   * @param stream Output stream (stderr if NULL)
   */
  void uvc_print_diag(uvc_device_handle_t *devh, FILE *stream) {
    if (stream == NULL)
      stream = stderr;
  
    if (devh->info->ctrl_if.bcdUVC) {
      uvc_streaming_interface_t *stream_if;
      int stream_idx = 0;
  
      uvc_device_descriptor_t *desc;
      uvc_get_device_descriptor(devh->dev, &desc);
  
      fprintf(stream, "DEVICE CONFIGURATION (%04x:%04x/%s) ---\n",
          desc->idVendor, desc->idProduct,
          desc->serialNumber ? desc->serialNumber : "[none]");
  
      uvc_free_device_descriptor(desc);
  
      fprintf(stream, "Status: %s\n", devh->streams ? "streaming" : "idle");
  
      fprintf(stream, "VideoControl:\n"
          "\tbcdUVC: 0x%04x\n",
          devh->info->ctrl_if.bcdUVC);
  
      DL_FOREACH(devh->info->stream_ifs, stream_if) {
        uvc_format_desc_t *fmt_desc;
  
        ++stream_idx;
  
        fprintf(stream, "VideoStreaming(%d):\n"
            "\tbEndpointAddress: %d\n\tFormats:\n",
            stream_idx, stream_if->bEndpointAddress);
  
        DL_FOREACH(stream_if->format_descs, fmt_desc) {
          uvc_frame_desc_t *frame_desc;
          int i;
  
          switch (fmt_desc->bDescriptorSubtype) {
            case UVC_VS_FORMAT_UNCOMPRESSED:
            case UVC_VS_FORMAT_MJPEG:
              fprintf(stream,
                  "\t\%s(%d)\n"
                  "\t\t  bits per pixel: %d\n"
                  "\t\t  GUID: ",
                  _uvc_name_for_format_subtype(fmt_desc->bDescriptorSubtype),
                  fmt_desc->bFormatIndex,
                  fmt_desc->bBitsPerPixel);
  
              for (i = 0; i < 16; ++i)
                fprintf(stream, "%02x", fmt_desc->guidFormat[i]);
  
              fprintf(stream, "\n");
  
              fprintf(stream,
                  "\t\t  default frame: %d\n"
                  "\t\t  aspect ration: %dx%d\n"
                  "\t\t  interlace flags: %02x\n"
                  "\t\t  copy protect: %02x\n",
                  fmt_desc->bDefaultFrameIndex,
                  fmt_desc->bAspectRatioX,
                  fmt_desc->bAspectRatioY,
                  fmt_desc->bmInterlaceFlags,
                  fmt_desc->bCopyProtect);
  
              DL_FOREACH(fmt_desc->frame_descs, frame_desc) {
                uint32_t *interval_ptr;
  
                fprintf(stream,
                    "\t\t\tFrameDescriptor(%d)\n"
                    "\t\t\t  capabilities: %02x\n"
                    "\t\t\t  size: %dx%d\n"
                    "\t\t\t  bit rate: %d-%d\n"
                    "\t\t\t  max frame size: %d\n"
                    "\t\t\t  default interval: 1/%d\n",
                    frame_desc->bFrameIndex,
                    frame_desc->bmCapabilities,
                    frame_desc->wWidth,
                    frame_desc->wHeight,
                    frame_desc->dwMinBitRate,
                    frame_desc->dwMaxBitRate,
                    frame_desc->dwMaxVideoFrameBufferSize,
                    10000000 / frame_desc->dwDefaultFrameInterval);
                if (frame_desc->intervals) {
                  for (interval_ptr = frame_desc->intervals;
                       *interval_ptr;
                       ++interval_ptr) {
                    fprintf(stream,
                        "\t\t\t  interval[%d]: 1/%d\n",
  		      (int) (interval_ptr - frame_desc->intervals),
  		      10000000 / *interval_ptr);
                  }
                } else {
                  fprintf(stream,
                      "\t\t\t  min interval[%d] = 1/%d\n"
                      "\t\t\t  max interval[%d] = 1/%d\n",
                      frame_desc->dwMinFrameInterval,
                      10000000 / frame_desc->dwMinFrameInterval,
                      frame_desc->dwMaxFrameInterval,
                      10000000 / frame_desc->dwMaxFrameInterval);
                  if (frame_desc->dwFrameIntervalStep)
                    fprintf(stream,
                        "\t\t\t  interval step[%d] = 1/%d\n",
                        frame_desc->dwFrameIntervalStep,
                        10000000 / frame_desc->dwFrameIntervalStep);
                }
              }
              break;
            default:
              fprintf(stream, "\t-UnknownFormat\n");
          }
        }
      }
  
      fprintf(stream, "END DEVICE CONFIGURATION\n");
    } else {
      fprintf(stream, "uvc_print_diag: Device not configured!\n");
    }
  }
  
  