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