+News for v0.1.6:
+================
+
+ * Fix some ffmpeg compile time deprecation warnings in am7xxx-play
+ * Fix some fmmpeg runtime warnings
+ * Don't dump the last frame unconditionally in am7xxx-play when in DEBUG
+ mode, add an option to enable the frame dump, but this is only active in
+ DEBUG mode
+ * Fix some compilation warnings from clang
+ * Replace deprecated FFmpeg API symbol PIX_FMT_NV12 in am7xx-play (Thanks to
+ Andreas Cadhalpun)
+ * Fix the Length field in the switch command in am7xxx-modeswitch (Thanks to
+ Balasubramanian S)
+ * More robust handling of USB configurations
+ * More robust handling of kernel driver detachment (Thanks to Andrea
+ Console)
+ * Minor documentation cleanups
+ * Minor build system improvements (am7xxx-play can now build without XCB,
+ but some functionalities will not be available)
+ * Misc code cleanups
+ * Relicense the example under GPL-3+
+
News for v0.1.5:
================
(this may not be necessary if the GStreamer sink works well enough).
- If there will ever be an API breakage, consider using more portable types
(e.g. off_t for file sizes, size_t for counters, etc.)
+- uniform the style of if() checks in cmake files
+- evaluate the use of error() instead of debug() for error paths
+- make the switch_command in am7xxx-modeswitch more readable using the
+ explicit bulk_cb_wrap structure from include/linux/usb/storage.h in the
+ linux kernel.
set -e
DISPLAY=":0"
-
+
WIN_INFO="$(xwininfo)"
-
+
X=$(echo "$WIN_INFO" | sed -n -e "/^[[:space:]]*Absolute upper-left X:[[:space:]]*/s///p")
Y=$(echo "$WIN_INFO" | sed -n -e "/^[[:space:]]*Absolute upper-left Y:[[:space:]]*/s///p")
WIDTH=$(echo "$WIN_INFO" | sed -n -e "/^[[:space:]]*Width:[[:space:]]*/s///p")
HEIGHT=$(echo "$WIN_INFO" | sed -n -e "/^[[:space:]]*Height:[[:space:]]*/s///p")
-
+
set -x
am7xxx-play -f x11grab -i "${DISPLAY}+${X},${Y}" -o video_size="${WIDTH}x${HEIGHT}"
-From dc6b216ffea1e80fd3f43d6144eb679a193f5666 Mon Sep 17 00:00:00 2001
-From: Antonio Ospite <ospite@studenti.unina.it>
-Date: Sun, 28 Jul 2013 01:06:41 +0200
+From ab3f910957638300224f1f114df6e73115ec86b7 Mon Sep 17 00:00:00 2001
+From: Antonio Ospite <ao2@ao2.it>
+Date: Tue, 17 Nov 2015 16:28:03 +0100
Subject: [PATCH] Instrument code with fps-meter
X-Face: z*RaLf`X<@C75u6Ig9}{oW$H;1_\2t5)({*|jhM<pyWR#k60!#=#>/Vb;]yA5<GWI5`6u&+
;6b'@y|8w"wB;4/e!7wYYrcqdJFY,~%Gk_4]cq$Ei/7<j&N3ah(m`ku?pX.&+~:_/wC~dwn^)MizBG
http://git.ao2.it/experiments/fps-meter.git/
---
examples/CMakeLists.txt | 2 +-
- examples/am7xxx-play.c | 4 ++++
- 2 files changed, 5 insertions(+), 1 deletion(-)
+ examples/am7xxx-play.c | 5 +++++
+ 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
-index c77a812..23af8a9 100644
+index c563f5f..4bdbdec 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,5 +1,5 @@
include(CheckSymbolExists)
-add_definitions("-D_POSIX_C_SOURCE=2") # for getopt()
-+add_definitions("-D_POSIX_C_SOURCE=200112L") # for getopt()
++add_definitions("-D_POSIX_C_SOURCE=200112L") # for clock_gettime()
add_definitions("-D_POSIX_SOURCE") # for sigaction
add_definitions("-D_BSD_SOURCE") # for strdup
diff --git a/examples/am7xxx-play.c b/examples/am7xxx-play.c
-index 3230e67..17228bf 100644
+index 6b0d206..271677b 100644
--- a/examples/am7xxx-play.c
+++ b/examples/am7xxx-play.c
@@ -34,6 +34,7 @@
/* On some systems ENOTSUP is not defined, fallback to its value on
* linux which is equal to EOPNOTSUPP which is 95
-@@ -292,6 +293,7 @@ static int am7xxx_play(const char *input_format_string,
+@@ -293,6 +294,7 @@ static int am7xxx_play(const char *input_format_string,
int got_picture;
int got_packet;
int ret;
ret = video_input_init(&input_ctx, input_format_string, input_path, input_options);
if (ret < 0) {
-@@ -354,6 +356,7 @@ static int am7xxx_play(const char *input_format_string,
+@@ -358,6 +360,8 @@ static int am7xxx_play(const char *input_format_string,
goto cleanup_out_buf;
}
+ fps_meter_init(&stats);
++
+ got_packet = 0;
while (run) {
/* read packet */
- ret = av_read_frame(input_ctx.format_ctx, &in_packet);
-@@ -438,6 +441,7 @@ static int am7xxx_play(const char *input_format_string,
+@@ -447,6 +451,7 @@ static int am7xxx_play(const char *input_format_string,
run = 0;
goto end_while;
}
end_while:
if (!output_ctx.raw_output && got_packet)
--
-1.8.3.2
+2.6.2
-# Doxyfile 1.8.4
+# Doxyfile 1.8.8
#---------------------------------------------------------------------------
# Project related configuration options
PROJECT_LOGO =
OUTPUT_DIRECTORY = @DOC_OUTPUT_PATH@
CREATE_SUBDIRS = NO
+ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = YES
+SHOW_GROUPED_MEMB_INC = NO
FORCE_LOCAL_INCLUDES = NO
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
LAYOUT_FILE =
CITE_BIB_FILES =
#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
+# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
-# configuration options related to the input files
+# Configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = @CMAKE_CURRENT_SOURCE_DIR@/ \
@CMAKE_SOURCE_DIR@/src/ \
FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
-# configuration options related to source browsing
+# Configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = YES
INLINE_SOURCES = YES
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION = NO
REFERENCES_LINK_SOURCE = YES
+SOURCE_TOOLTIPS = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
+CLANG_ASSISTED_PARSING = NO
+CLANG_OPTIONS =
#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
+# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = YES
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
-# configuration options related to the HTML output
+# Configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = html
EXTERNAL_SEARCH_ID =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
+# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_SOURCE_CODE = NO
LATEX_BIB_STYLE = plain
#---------------------------------------------------------------------------
-# configuration options related to the RTF output
+# Configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
-# configuration options related to the man page output
+# Configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
+MAN_SUBDIR =
MAN_LINKS = NO
#---------------------------------------------------------------------------
-# configuration options related to the XML output
+# Configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
-XML_SCHEMA =
-XML_DTD =
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
-# configuration options related to the DOCBOOK output
+# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
+DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
+# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
+# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
-# Configuration::additions related to external references
+# Configuration options related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = NO
MSCGEN_PATH =
+DIA_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = NO
DOT_NUM_THREADS = 0
DOT_PATH =
DOTFILE_DIRS =
MSCFILE_DIRS =
+DIAFILE_DIRS =
+PLANTUML_JAR_PATH =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
--- /dev/null
+Bus 002 Device 008: ID 21e7:000e
+Device Descriptor:
+ bLength 18
+ bDescriptorType 1
+ bcdUSB 2.00
+ bDeviceClass 255 Vendor Specific Class
+ bDeviceSubClass 0
+ bDeviceProtocol 0
+ bMaxPacketSize0 64
+ idVendor 0x21e7
+ idProduct 0x000e
+ bcdDevice 1.00
+ iManufacturer 1 actions
+ iProduct 2 Usb Device
+ iSerial 3 00000000000000000000000000000000
+ bNumConfigurations 1
+ Configuration Descriptor:
+ bLength 9
+ bDescriptorType 2
+ wTotalLength 32
+ bNumInterfaces 1
+ bConfigurationValue 2
+ iConfiguration 6 PICO PROJECTOR
+ bmAttributes 0xc0
+ Self Powered
+ MaxPower 2mA
+ Interface Descriptor:
+ bLength 9
+ bDescriptorType 4
+ bInterfaceNumber 0
+ bAlternateSetting 0
+ bNumEndpoints 2
+ bInterfaceClass 255 Vendor Specific Class
+ bInterfaceSubClass 8
+ bInterfaceProtocol 8
+ iInterface 7 USB PICO
+ Endpoint Descriptor:
+ bLength 7
+ bDescriptorType 5
+ bEndpointAddress 0x81 EP 1 IN
+ bmAttributes 2
+ Transfer Type Bulk
+ Synch Type None
+ Usage Type Data
+ wMaxPacketSize 0x0200 1x 512 bytes
+ bInterval 0
+ Endpoint Descriptor:
+ bLength 7
+ bDescriptorType 5
+ bEndpointAddress 0x01 EP 1 OUT
+ bmAttributes 2
+ Transfer Type Bulk
+ Synch Type None
+ Usage Type Data
+ wMaxPacketSize 0x0200 1x 512 bytes
+ bInterval 1
+Device Qualifier (for other device speed):
+ bLength 10
+ bDescriptorType 6
+ bcdUSB 2.00
+ bDeviceClass 0 (Defined at Interface level)
+ bDeviceSubClass 0
+ bDeviceProtocol 0
+ bMaxPacketSize0 64
+ bNumConfigurations 1
+Device Status: 0x0001
+ Self Powered
option(BUILD_AM7XXX-PLAY "Build a more complete example: am7xxx-play" TRUE)
if(BUILD_AM7XXX-PLAY)
find_package(FFmpeg REQUIRED)
- set(CMAKE_REQUIRED_LIBRARIES ${FFMPEG_LIBRARIES})
- set(CMAKE_REQUIRED_INCLUDES ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS})
+ set(CMAKE_REQUIRED_LIBRARIES ${FFMPEG_LIBRARIES})
+ set(CMAKE_REQUIRED_INCLUDES ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS})
check_symbol_exists(avformat_open_input
"${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS}/libavformat/avformat.h"
HAVE_AVFORMAT_OPEN_INPUT)
if (XCB_FOUND)
add_definitions("${LIBXCB_DEFINITIONS} -DHAVE_XCB")
include_directories(${LIBXCB_INCLUDE_DIRS})
+ set(OPTIONAL_LIBRARIES ${LIBXCB_LIBRARIES})
endif()
add_executable(am7xxx-play am7xxx-play.c)
target_link_libraries(am7xxx-play am7xxx
${FFMPEG_LIBRARIES}
${FFMPEG_LIBSWSCALE_LIBRARIES}
- ${LIBXCB_LIBRARIES})
+ ${OPTIONAL_LIBRARIES})
install(TARGETS am7xxx-play
DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
endif()
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
+ * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
*/
#include <stdio.h>
+#include <string.h>
#include <errno.h>
#include <libusb.h>
static unsigned char switch_command[] =
"\x55\x53\x42\x43\x08\x70\x52\x89\x00\x00\x00\x00\x00\x00"
- "\x0c\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
+ "\x10\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
int main(void)
{
int ret;
- int transferred;
- libusb_device_handle *usb_device = NULL;
-
+ libusb_device_handle *usb_device;
+ int current_configuration;
unsigned int len;
+ int transferred;
ret = libusb_init(NULL);
- if (ret < 0)
+ if (ret < 0) {
+ fprintf(stderr, "libusb_init failed: %s\n",
+ libusb_error_name(ret));
goto out;
+ }
- libusb_set_debug(NULL, 3);
+ libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_INFO);
usb_device = libusb_open_device_with_vid_pid(NULL,
AM7XXX_STORAGE_VID,
AM7XXX_STORAGE_PID);
if (usb_device == NULL) {
- fprintf(stderr, "cannot open the device: %d.\n", errno);
+ fprintf(stderr, "libusb_open failed: %s\n", strerror(errno));
ret = -errno;
goto out;
}
- if (libusb_kernel_driver_active(usb_device, AM7XXX_STORAGE_INTERFACE)) {
- ret = libusb_detach_kernel_driver(usb_device,
- AM7XXX_STORAGE_INTERFACE);
- if (ret < 0)
- fprintf(stderr, "Warning: cannot detach kernel driver.\n");
- } else {
- fprintf(stderr, "kernel driver not active.\n");
- }
-
- ret = libusb_set_configuration(usb_device, AM7XXX_STORAGE_CONFIGURATION);
+ current_configuration = -1;
+ ret = libusb_get_configuration(usb_device, ¤t_configuration);
if (ret < 0) {
- fprintf(stderr, "cannot set configuration.\n");
+ fprintf(stderr, "libusb_get_configuration failed: %s\n",
+ libusb_error_name(ret));
goto out_libusb_close;
}
+ if (current_configuration != AM7XXX_STORAGE_CONFIGURATION) {
+ ret = libusb_set_configuration(usb_device,
+ AM7XXX_STORAGE_CONFIGURATION);
+ if (ret < 0) {
+ fprintf(stderr, "libusb_set_configuration failed: %s\n",
+ libusb_error_name(ret));
+ fprintf(stderr, "Cannot set configuration %d\n",
+ AM7XXX_STORAGE_CONFIGURATION);
+ goto out_libusb_close;
+ }
+ }
+
+ libusb_set_auto_detach_kernel_driver(usb_device, 1);
+
ret = libusb_claim_interface(usb_device, AM7XXX_STORAGE_INTERFACE);
if (ret < 0) {
- fprintf(stderr, "cannot claim interface.\n");
+ fprintf(stderr, "libusb_claim_interface failed: %s\n",
+ libusb_error_name(ret));
+ fprintf(stderr, "Cannot claim interface %d\n",
+ AM7XXX_STORAGE_INTERFACE);
goto out_libusb_close;
}
+ /* Checking that the configuration has not changed, as suggested in
+ * http://libusb.sourceforge.net/api-1.0/caveats.html
+ */
+ current_configuration = -1;
+ ret = libusb_get_configuration(usb_device, ¤t_configuration);
+ if (ret < 0) {
+ fprintf(stderr, "libusb_get_configuration after claim failed: %s\n",
+ libusb_error_name(ret));
+ goto out_libusb_release_interface;
+ }
+
+ if (current_configuration != AM7XXX_STORAGE_CONFIGURATION) {
+ fprintf(stderr, "libusb configuration changed (expected: %d, current: %d\n",
+ AM7XXX_STORAGE_CONFIGURATION, current_configuration);
+ ret = -EINVAL;
+ goto out_libusb_release_interface;
+ }
+
len = sizeof(switch_command);
+
transferred = 0;
ret = libusb_bulk_transfer(usb_device, AM7XXX_STORAGE_OUT_EP,
switch_command, len, &transferred, 0);
*/
if (image_format == AM7XXX_IMAGE_FORMAT_NV12) {
fprintf(stdout, "using raw output format\n");
- output_codec_ctx->pix_fmt = PIX_FMT_NV12;
+ output_codec_ctx->pix_fmt = AV_PIX_FMT_NV12;
output_ctx->codec_ctx = output_codec_ctx;
output_ctx->raw_output = 1;
ret = 0;
* in particular they won't be 0, this is needed because they are used
* as divisor somewhere in the encoding process */
output_codec_ctx->qmin = output_codec_ctx->qmax = ((100 - (quality - 1)) * FF_QUALITY_SCALE) / 100;
- output_codec_ctx->mb_lmin = output_codec_ctx->lmin = output_codec_ctx->qmin * FF_QP2LAMBDA;
- output_codec_ctx->mb_lmax = output_codec_ctx->lmax = output_codec_ctx->qmax * FF_QP2LAMBDA;
+ output_codec_ctx->mb_lmin = output_codec_ctx->qmin * FF_QP2LAMBDA;
+ output_codec_ctx->mb_lmax = output_codec_ctx->qmax * FF_QP2LAMBDA;
output_codec_ctx->flags |= CODEC_FLAG_QSCALE;
output_codec_ctx->global_quality = output_codec_ctx->qmin * FF_QP2LAMBDA;
unsigned int upscale,
unsigned int quality,
am7xxx_image_format image_format,
- am7xxx_device *dev)
+ am7xxx_device *dev,
+ int dump_frame)
{
struct video_input_ctx input_ctx;
struct video_output_ctx output_ctx;
ret = -ENOMEM;
goto cleanup_picture_raw;
}
+ picture_scaled->format = (output_ctx.codec_ctx)->pix_fmt;
+ picture_scaled->width = (output_ctx.codec_ctx)->width;
+ picture_scaled->height = (output_ctx.codec_ctx)->height;
/* calculate the bytes needed for the output image and create buffer for the output image */
out_buf_size = avpicture_get_size((output_ctx.codec_ctx)->pix_fmt,
goto cleanup_out_buf;
}
+ got_packet = 0;
while (run) {
/* read packet */
ret = av_read_frame(input_ctx.format_ctx, &in_packet);
goto end_while;
}
- /* if we get the complete frame */
+ /* if we got the complete frame */
if (got_picture) {
/* convert it to YUV */
sws_scale(sw_scale_ctx,
- (const uint8_t * const*)picture_raw->data,
+ (const uint8_t * const *)picture_raw->data,
picture_raw->linesize,
0,
(input_ctx.codec_ctx)->height,
}
#ifdef DEBUG
- char filename[NAME_MAX];
- FILE *file;
- if (!output_ctx.raw_output)
- snprintf(filename, NAME_MAX, "out_q%03d.jpg", quality);
- else
- snprintf(filename, NAME_MAX, "out.raw");
- file = fopen(filename, "wb");
- fwrite(out_picture, 1, out_picture_size, file);
- fclose(file);
+ if (dump_frame) {
+ char filename[NAME_MAX];
+ FILE *file;
+ if (!output_ctx.raw_output)
+ snprintf(filename, NAME_MAX, "out_q%03d.jpg", quality);
+ else
+ snprintf(filename, NAME_MAX, "out.raw");
+ file = fopen(filename, "wb");
+ fwrite(out_picture, 1, out_picture_size, file);
+ fclose(file);
+ }
+#else
+ (void) dump_frame;
#endif
ret = am7xxx_send_image_async(dev,
out_picture,
out_picture_size);
if (ret < 0) {
- perror("am7xxx_send_image");
+ perror("am7xxx_send_image_async");
run = 0;
goto end_while;
}
printf("usage: %s [OPTIONS]\n\n", name);
printf("OPTIONS:\n");
printf("\t-d <index>\t\tthe device index (default is 0)\n");
+#ifdef DEBUG
+ printf("\t-D \t\t\tdump the last frame to a file (only active in DEBUG mode)\n");
+#endif
printf("\t-f <input format>\tthe input device format\n");
printf("\t-i <input path>\t\tthe input path\n");
printf("\t-o <options>\t\ta comma separated list of input format options\n");
int format = AM7XXX_IMAGE_FORMAT_JPEG;
am7xxx_context *ctx;
am7xxx_device *dev;
+ int dump_frame = 0;
- while ((opt = getopt(argc, argv, "d:f:i:o:s:uF:q:l:p:z:h")) != -1) {
+ while ((opt = getopt(argc, argv, "d:Df:i:o:s:uF:q:l:p:z:h")) != -1) {
switch (opt) {
case 'd':
device_index = atoi(optarg);
goto out;
}
break;
+ case 'D':
+ dump_frame = 1;
+#ifndef DEBUG
+ fprintf(stderr, "Warning: the -D option is only active in DEBUG mode.\n");
+#endif
+ break;
case 'f':
input_format_string = strdup(optarg);
break;
upscale,
quality,
format,
- dev);
+ dev,
+ dump_frame);
if (ret < 0) {
fprintf(stderr, "am7xxx_play failed\n");
goto cleanup;
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
+ * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* taken from: http://unixwiz.net/techtips/gnu-c-attributes.html)
*/
#ifndef __GNUC__
-# define __attribute__(x) /*NOTHING*/
+# define __attribute__(x) /* NOTHING */
#endif
/* Control shared library symbols visibility */
static void log_message(am7xxx_context *ctx,
int level,
- const char *function,
+ const char *function_name,
int line,
const char *fmt,
...) __attribute__ ((format (printf, 5, 6)));
{
/* The 3 below is the length of "xx " where xx is the hex string
* representation of a byte */
- return ((i+1) % (80/3));
+ return ((i + 1) % (80 / 3));
}
static void trace_dump_buffer(am7xxx_context *ctx, const char *message,
static int read_data(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
{
int ret;
- int transferred = 0;
+ int transferred;
+ transferred = 0;
ret = libusb_bulk_transfer(dev->usb_device, 0x81, buffer, len, &transferred, 0);
if (ret != 0 || (unsigned int)transferred != len) {
- error(dev->ctx, "ret: %d\ttransferred: %d (expected %u)\n",
- ret, transferred, len);
+ error(dev->ctx, "%s. Transferred: %d (expected %u)\n",
+ libusb_error_name(ret), transferred, len);
return ret;
}
static int send_data(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
{
int ret;
- int transferred = 0;
+ int transferred;
trace_dump_buffer(dev->ctx, "sending -->", buffer, len);
+ transferred = 0;
ret = libusb_bulk_transfer(dev->usb_device, 0x1, buffer, len, &transferred, 0);
if (ret != 0 || (unsigned int)transferred != len) {
- error(dev->ctx, "ret: %d\ttransferred: %d (expected %u)\n",
- ret, transferred, len);
+ error(dev->ctx, "%s. Transferred: %d (expected %u)\n",
+ libusb_error_name(ret), transferred, len);
return ret;
}
/* Make a copy of the buffer so the caller can safely reuse it just
* after libusb_submit_transfer() has returned. This technique
- * requires more allocations than a proper double-buffering approach
- * but it takes a lot less code. */
+ * requires more dynamic allocations compared to a proper
+ * double-buffering approach but it takes a lot less code. */
transfer_buffer = malloc(len);
if (transfer_buffer == NULL) {
error(dev->ctx, "cannot allocate transfer buffer (%s)\n",
* set up */
static void log_message(am7xxx_context *ctx,
int level,
- const char *function,
+ const char *function_name,
int line,
const char *fmt,
...)
va_list ap;
if (level == AM7XXX_LOG_FATAL || (ctx && level <= ctx->log_level)) {
- if (function) {
- fprintf(stderr, "%s", function);
+ if (function_name) {
+ fprintf(stderr, "%s", function_name);
if (line)
fprintf(stderr, "[%d]", line);
fprintf(stderr, ": ");
new_device = malloc(sizeof(*new_device));
if (new_device == NULL) {
- fatal("cannot allocate a new device (%s)\n", strerror(errno));
+ debug(ctx, "cannot allocate a new device (%s)\n", strerror(errno));
return NULL;
}
memset(new_device, 0, sizeof(*new_device));
return current;
}
+static int open_device(am7xxx_context *ctx,
+ unsigned int device_index,
+ libusb_device *usb_dev,
+ am7xxx_device **dev)
+{
+ int ret;
+ int current_configuration;
+
+ *dev = find_device(ctx, device_index);
+ if (*dev == NULL) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ /* the usb device has already been opened */
+ if ((*dev)->usb_device) {
+ ret = 1;
+ goto out;
+ }
+
+ ret = libusb_open(usb_dev, &((*dev)->usb_device));
+ if (ret < 0) {
+ debug(ctx, "libusb_open failed: %s\n", libusb_error_name(ret));
+ goto out;
+ }
+
+ /* XXX, the device is now open, if any of the calls below fail we need
+ * to close it again before bailing out.
+ */
+
+ current_configuration = -1;
+ ret = libusb_get_configuration((*dev)->usb_device,
+ ¤t_configuration);
+ if (ret < 0) {
+ debug(ctx, "libusb_get_configuration failed: %s\n",
+ libusb_error_name(ret));
+ goto out_libusb_close;
+ }
+
+ if (current_configuration != (*dev)->desc->configuration) {
+ /*
+ * In principle, before setting a new configuration, kernel
+ * drivers should be detached from _all_ interfaces; for
+ * example calling something like the following "invented"
+ * function _before_ setting the new configuration:
+ *
+ * libusb_detach_all_kernel_drivers((*dev)->usb_device);
+ *
+ * However, in practice, this is not necessary for most
+ * devices as they have only one configuration.
+ *
+ * When a device only has one configuration:
+ *
+ * - if there was a kernel driver bound to the device, it
+ * had already set the configuration and the call below
+ * will be skipped;
+ *
+ * - if no kernel driver was bound to the device, the call
+ * below will suceed.
+ */
+ ret = libusb_set_configuration((*dev)->usb_device,
+ (*dev)->desc->configuration);
+ if (ret < 0) {
+ debug(ctx, "libusb_set_configuration failed: %s\n",
+ libusb_error_name(ret));
+ debug(ctx, "Cannot set configuration %hhu\n",
+ (*dev)->desc->configuration);
+ goto out_libusb_close;
+ }
+ }
+
+ libusb_set_auto_detach_kernel_driver((*dev)->usb_device, 1);
+
+ ret = libusb_claim_interface((*dev)->usb_device,
+ (*dev)->desc->interface_number);
+ if (ret < 0) {
+ debug(ctx, "libusb_claim_interface failed: %s\n",
+ libusb_error_name(ret));
+ debug(ctx, "Cannot claim interface %hhu\n",
+ (*dev)->desc->interface_number);
+ goto out_libusb_close;
+ }
+
+ /* Checking that the configuration has not changed, as suggested in
+ * http://libusb.sourceforge.net/api-1.0/caveats.html
+ */
+ current_configuration = -1;
+ ret = libusb_get_configuration((*dev)->usb_device,
+ ¤t_configuration);
+ if (ret < 0) {
+ debug(ctx, "libusb_get_configuration after claim failed: %s\n",
+ libusb_error_name(ret));
+ goto out_libusb_release_interface;
+ }
+
+ if (current_configuration != (*dev)->desc->configuration) {
+ debug(ctx, "libusb configuration changed (expected: %hhu, current: %d\n",
+ (*dev)->desc->configuration, current_configuration);
+ ret = -EINVAL;
+ goto out_libusb_release_interface;
+ }
+out:
+ return ret;
+
+out_libusb_release_interface:
+ libusb_release_interface((*dev)->usb_device,
+ (*dev)->desc->interface_number);
+out_libusb_close:
+ libusb_close((*dev)->usb_device);
+ (*dev)->usb_device = NULL;
+ return ret;
+}
+
typedef enum {
SCAN_OP_BUILD_DEVLIST,
SCAN_OP_OPEN_DEVICE,
* This is where the central logic of multi-device support is.
*
* When 'op' == SCAN_OP_BUILD_DEVLIST the parameters 'open_device_index' and
- * 'dev' are ignored; the function returns 0 on success and a negative value
+ * 'dev' are ignored; the function returns 0 on success or a negative value
* on error.
*
* When 'op' == SCAN_OP_OPEN_DEVICE the function opens the supported USB
* device with index 'open_device_index' and returns the correspondent
- * am7xxx_device in the 'dev' parameter; the function returns 0 on success,
- * 1 if the device was already open and a negative value on error.
+ * am7xxx_device in the 'dev' parameter; the function returns the value from
+ * open_device(), which is 0 on success, 1 if the device was already open or
+ * a negative value on error.
*
* NOTES:
* if scan_devices() fails when called with 'op' == SCAN_OP_BUILD_DEVLIST,
unsigned int open_device_index, am7xxx_device **dev)
{
ssize_t num_devices;
- libusb_device** list;
+ libusb_device **list;
unsigned int current_index;
int i;
int ret;
} else if (op == SCAN_OP_OPEN_DEVICE &&
current_index == open_device_index) {
- *dev = find_device(ctx, open_device_index);
- if (*dev == NULL) {
- ret = -ENODEV;
- goto out;
- }
-
- /* the usb device has already been opened */
- if ((*dev)->usb_device) {
- debug(ctx, "(*dev)->usb_device already set\n");
- ret = 1;
- goto out;
- }
-
- ret = libusb_open(list[i], &((*dev)->usb_device));
- if (ret < 0) {
- debug(ctx, "libusb_open failed\n");
- goto out;
- }
-
- /* XXX, the device is now open, if any
- * of the calls below fail we need to
- * close it again before bailing out.
- */
-
- ret = libusb_set_configuration((*dev)->usb_device,
- (*dev)->desc->configuration);
- if (ret < 0) {
- debug(ctx, "libusb_set_configuration failed\n");
- debug(ctx, "Cannot set configuration %hhu\n",
- (*dev)->desc->configuration);
- goto out_libusb_close;
- }
-
- ret = libusb_claim_interface((*dev)->usb_device,
- (*dev)->desc->interface_number);
- if (ret < 0) {
- debug(ctx, "libusb_claim_interface failed\n");
- debug(ctx, "Cannot claim interface %hhu\n",
- (*dev)->desc->interface_number);
-out_libusb_close:
- libusb_close((*dev)->usb_device);
- (*dev)->usb_device = NULL;
- goto out;
- }
+ ret = open_device(ctx,
+ open_device_index,
+ list[i],
+ dev);
+ if (ret < 0)
+ debug(ctx, "open_device failed\n");
+ /* exit the loop unconditionally after
+ * attempting to open the device
+ * requested by the user */
goto out;
}
current_index++;
}
}
- /* if we made it up to here we didn't find any device to open */
+ /* if we made it up to here when op == SCAN_OP_OPEN_DEVICE,
+ * no devices to open had been found. */
if (op == SCAN_OP_OPEN_DEVICE) {
error(ctx, "Cannot find any device to open\n");
ret = -ENODEV;
(*ctx)->log_level = AM7XXX_LOG_TRACE;
ret = libusb_init(&((*ctx)->usb_context));
- if (ret < 0)
+ if (ret < 0) {
+ error(*ctx, "libusb_init failed: %s\n", libusb_error_name(ret));
goto out_free_context;
+ }
libusb_set_debug((*ctx)->usb_context, LIBUSB_LOG_LEVEL_INFO);
* This is the function that actually makes the device display something.
* Static pictures can be sent just once and the device will keep showing them
* until another image get sent or some command resets or turns off the display.
- *
+ *
* @note This _async() variant makes a copy of the image buffer, so the caller
* is free to reuse the buffer just after the function returns.
*
-/*
+/*
* Public domain, stripped down version of:
* https://gist.github.com/panzi/6856583
*/