Merge tag 'v0.1.4' into debian
authorAntonio Ospite <ospite@studenti.unina.it>
Sun, 28 Jul 2013 16:20:15 +0000 (18:20 +0200)
committerAntonio Ospite <ospite@studenti.unina.it>
Sun, 28 Jul 2013 16:20:15 +0000 (18:20 +0200)
Release version 0.1.4

26 files changed:
CMakeLists.txt
ChangeLog
HACKING.asciidoc
NEWS [new file with mode: 0644]
README.asciidoc
TODO
contrib/50-am7xxx_mode_switch.rules [new file with mode: 0644]
contrib/55-am7xxx.rules
contrib/performance/0001-Instrument-code-with-fps-meter.patch [new file with mode: 0644]
contrib/performance/1024x768_am7xxx_send_image.log [new file with mode: 0644]
contrib/performance/1024x768_am7xxx_send_image_async.log [new file with mode: 0644]
contrib/performance/README [new file with mode: 0644]
contrib/performance/ministat_report_1024x768.log [new file with mode: 0644]
doc/Doxyfile.in
doc/DoxygenMainpage.dox
doc/lsusb_dumps/lsusb_Acer-C110.log [new file with mode: 0644]
doc/lsusb_dumps/lsusb_Philips-PicoPix-2330.log [new file with mode: 0644]
doc/man/CMakeLists.txt
doc/man/am7xxx-modeswitch.1.txt [new file with mode: 0644]
examples/CMakeLists.txt
examples/am7xxx-modeswitch.c [new file with mode: 0644]
examples/am7xxx-play.c
examples/am7xxx_mode_switch.c [deleted file]
examples/picoproj.c
src/am7xxx.c
src/am7xxx.h

index 89f0437..d643ed9 100644 (file)
@@ -6,7 +6,7 @@ set(PROJECT_DESCRIPTION
 
 set(PROJECT_VER_MAJOR 0)
 set(PROJECT_VER_MINOR 1)
-set(PROJECT_VER_PATCH 3)
+set(PROJECT_VER_PATCH 4)
 set(PROJECT_VER_EXTRA "")
 set(PROJECT_VER
   "${PROJECT_VER_MAJOR}.${PROJECT_VER_MINOR}.${PROJECT_VER_PATCH}${PROJECT_VER_EXTRA}")
@@ -31,7 +31,9 @@ macro(add_flags var)
   set(${var} "${${var}} ${_flags}")
 endmacro(add_flags)
 
-if (CMAKE_COMPILER_IS_GNUCC)
+string(REGEX MATCH "clang" CMAKE_COMPILER_IS_CLANG "${CMAKE_C_COMPILER}")
+
+if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG)
   add_definitions(-Wall)
 
   # let CFLAGS env override this
@@ -63,13 +65,12 @@ if (CMAKE_COMPILER_IS_GNUCC)
     -Wswitch-enum
     -Wundef
     -Wunreachable-code
-    -Wunsafe-loop-optimizations
     -Wwrite-strings
-    -fstack-protector
-    --param=ssp-buffer-size=4)
+    -fstack-protector)
 
   add_flags(DEBUG_FLAGS
-    -ggdb)
+    -ggdb
+    -DDEBUG=1)
 
   add_flags(RELEASE_FLAGS
     -Wp,-D_FORTIFY_SOURCE=2)
@@ -77,11 +78,30 @@ if (CMAKE_COMPILER_IS_GNUCC)
   if (STRICT_COMPILATION_CHECKS)
     add_flags(STRICT_FLAGS
       -Werror
+      # sign conversion warnings can be very noisy for a very little gain
+      #-Wsign-conversion
       # NOTE: Vanilla libusb-1.0.8 can't live with -pedantic-errors
-      -pedantic-errors
+      -pedantic-errors)
+
+  endif()
+endif()
+
+if (CMAKE_COMPILER_IS_GNUCC)
+  add_flags(CMAKE_C_FLAGS
+    -Wunsafe-loop-optimizations
+    --param=ssp-buffer-size=4)
+
+  if (STRICT_COMPILATION_CHECKS)
+    add_flags(STRICT_FLAGS
       # NOTE: GCC >= 4.6 is needed for -Wunused-but-set-variable
       -Wunused-but-set-variable)
+  endif()
+endif()
 
+if (CMAKE_COMPILER_IS_CLANG)
+  if (STRICT_COMPILATION_CHECKS)
+    add_flags(STRICT_FLAGS
+      -Wshorten-64-to-32)
   endif()
 endif()
 
index 340d105..89d4427 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
+2013-07-28 11:15:18 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * Release version 0.1.4 (HEAD, master)
+
+2013-07-28 01:11:42 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * contrib: add some benchmarking data about am7xxx_send_image_async (origin/master)
+
+2013-07-28 00:50:30 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx-play: fix a crash when a packet cannot be encoded
+
+2013-07-28 00:38:13 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx-play: don't initialize variables when not needed
+
+2013-07-28 00:19:04 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * TODO: mention that atoi() must go away
+
+2013-07-28 00:10:08 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoproj: get rid of exit(), return more meaningful values to userspace
+
+2013-07-27 23:47:26 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * HACKING.asciidoc: add commands to compile with clang
+
+2013-07-27 23:44:45 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * HACKING.asciidoc: add an example of testing am7xxx-play with valgrind
+
+2013-07-27 23:36:08 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * doc: update Doxyfile.in
+
+2013-07-27 23:27:01 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * contrib: add a udev rule to invoke am7xxx-modeswitch
+
+2013-07-27 23:25:34 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * Rename am7xxx_mode_switch to am7xxx-modeswitch
+
+2013-07-27 23:02:34 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * TODO: mention that data types could be improved in the API
+
+2013-07-27 23:01:16 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoporj: fix another -Wshorten-64-to-32 warning from clang
+
+2013-07-27 22:55:53 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoproj: silence a -Wshorten-64-to-32 warning from clang
+
+2013-07-27 22:53:19 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * CMakeLists.txt: disable -Wsign-conversion warnings
+
+2013-07-27 21:33:28 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: fix a clang warning
+
+2013-07-27 20:55:48 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * examples: silence a couple of clang warnings
+
+2013-07-27 20:26:06 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * doc: mention the Top-Height/TEC PP700 in the Doxygen documentation
+
+2013-07-27 20:23:30 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * doc: add some lsusb dumps for reference
+
+2013-07-21 00:13:33 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx-play: use am7xxx_send_image_async() (local-ao2)
+
+2013-07-21 00:10:28 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: implement am7xxx_send_image_async()
+
+2013-07-14 13:25:25 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: fix a typo in a comment s/a am7xxx device/an am7xxx device/
+
+2013-07-13 11:05:00 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * CMakeLists.txt: enable two new compiler warnings
+
+2013-06-30 00:22:07 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: add quirks for devices not supporting some operations
+
+2013-06-30 00:20:51 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoproj: remove an unreachable break statement
+
+2013-06-30 00:15:30 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx-play: uniform coding style
+
+2013-06-30 00:12:59 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * CMakeLists.txt: add support for clang and isolate gcc-only options
+
+2013-06-29 23:11:57 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * HACKING.asciidoc: mention the patch needed for older libav/ffmpeg
+
+2013-06-29 23:02:13 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * README.asciidoc: mention the TEC PP700 projector as supported
+
+2013-05-27 00:06:23 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoproj: remove an unneeded blank line
+
+2013-05-27 00:05:00 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * examples: print the usage message when a required option is missing
+
+2013-05-26 23:53:13 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * CMakeLists.txt: fix enabling verbose debug output
+
+2013-04-05 23:35:34 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: use the symbolic constant for libusb log level
+
+2013-04-05 23:29:58 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoproj: show the image resolution when image does not fit the native one
+
+2013-04-05 23:28:24 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoproj: remove one of two consecutive blank lines
+
+2013-03-25 23:04:03 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * doc, contrib: add PicoPix 2330 to the list of supported devices
+
+2013-03-25 22:47:21 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: add support for Philips/Sagemcom PicoPix 2330
+
+2013-03-25 22:44:35 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * Merge branch 'per-device-usb-config'
+
+2013-03-23 23:30:54 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: make the supported_device array const
+
+2013-03-23 22:55:04 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: improve setting USB configuration and interface_number
+
+2012-11-14 15:41:48 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: reference am7xxx_usb_device_descriptor in struct _am7xxx_device
+
+2013-03-23 22:40:25 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: fail if USB configuration or interface are not right
+
+2013-03-23 23:03:48 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx_mode_switch: release interface only if claimed
+
+2013-03-14 23:04:20 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * doc: add a man page for am7xxx_mode_switch
+
+2013-03-15 00:13:21 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * Add a NEWS file
+
 2013-03-14 20:23:49 +0100 Antonio Ospite <ospite@studenti.unina.it>
 
-       * Release version 0.1.3 (HEAD, master)
+       * Release version 0.1.3 (tag: v0.1.3)
 
 2013-03-14 19:48:14 +0100 Antonio Ospite <ospite@studenti.unina.it>
 
-       * am7xxx-play: switch to avcodec_encode_video2() (origin/master)
+       * am7xxx-play: switch to avcodec_encode_video2()
 
 2013-03-14 19:30:07 +0100 Antonio Ospite <ospite@studenti.unina.it>
 
 
 2012-03-28 13:37:00 +0200 Antonio Ospite <ospite@studenti.unina.it>
 
-       * Release version 0.1.2 (v0.1.2)
+       * Release version 0.1.2 (tag: v0.1.2)
 
 2012-03-28 13:08:23 +0200 Antonio Ospite <ospite@studenti.unina.it>
 
 
 2012-03-28 10:55:14 +0200 Antonio Ospite <ospite@studenti.unina.it>
 
-       * Release version 0.1.1 (v0.1.1)
+       * Release version 0.1.1 (tag: v0.1.1)
 
 2012-03-28 10:43:04 +0200 Antonio Ospite <ospite@studenti.unina.it>
 
 
 2012-03-26 13:50:05 +0200 Antonio Ospite <ospite@studenti.unina.it>
 
-       * Increase project number to 0.1.0 (v0.1.0)
+       * Increase project number to 0.1.0 (tag: v0.1.0)
 
 2012-03-26 13:49:31 +0200 Antonio Ospite <ospite@studenti.unina.it>
 
index 3c1bc74..20c4aa2 100644 (file)
@@ -19,6 +19,9 @@ On a Debian based system, the dependencies can be installed with this command:
                           libavdevice-dev \
                           libswscale-dev
 
+With libav/ffmpeg version previous than 0.9 this patch is needed:
+http://git.ao2.it/libam7xxx.git/blob_plain/refs/heads/debian:/debian/patches/0002-Revert-am7xxx-play-switch-to-avcodec_encode_video2.patch
+
 The library and the example programs can be compiled following these steps:
 
   $ git clone git://git.ao2.it/libam7xxx.git
@@ -47,6 +50,13 @@ can run:
   $ cmake -D CMAKE_C_COMPILER=cgcc ../
   $ make
 
+And for a pre-release check with a different compiler, which never hurts:
+
+  $ mkdir build
+  $ cd build
+  $ cmake -D CMAKE_C_COMPILER=clang -D CMAKE_BUILD_TYPE=debug -D STRICT_COMPILATION_CHECKS=ON ../
+  $ make
+
 === Cross Builds
 
 If you want to build for MS Windows:
@@ -78,3 +88,8 @@ dynamic analyzer by using a command like:
 
   $ valgrind --leak-check=full --show-reachable=yes --track-origins=yes \
     ./bin/picoproj -W 800 -H 480 -f my_image.jpg
+
+or, for am7xxx-play:
+
+  $ valgrind --leak-check=full --show-reachable=yes --track-origins=yes \
+    ./bin/am7xxx-play -f x11grab -i :0
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..7f38105
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,31 @@
+News for v0.1.4:
+================
+
+  * Improved USB device configuration in order to support mode devices
+  * Added support for Philips/Sagemcom PicoPix 2330 (Thanks to GrĂ©gory
+    Lemesre)
+  * Fixed verbose debug output
+  * Confirmed that libam7xxx works with the Top-Height/TEC PP700 projector
+  * Implemented am7xxx_send_image_async()
+  * Made am7xxx-play almost twice faster by using am7xxx_send_image_async()
+  * Improved documentation
+  * Added support for compiling with clang
+  * A lot of little fixes for correctness, robustness and portability
+  * Renamed am7xxx_mode_switch to am7xxx-modeswitch, added an udev rule for it
+
+News for v0.1.3:
+================
+
+  * Better documentation
+  * Ported to Windows (compiles with MinGW)
+  * Added a minimal replacement of usb-modeswitch to use on systems where the
+    latter is not available
+  * Added support for Acer C112 (Thanks to Richard Wisenoecker)
+  * Added support for Aiptek PocketCinema T25 (Thanks to Matti Koskinen)
+  * Added some contrib scripts
+  * Added support for setting the projectors zoom mode
+  * Added multi-device support, now more than one projector can be used at the
+    same time on the same system (Tested by Konstantin Lohmann)
+  * Added support for Philips/SagemCom PicoPix PPX 2055
+  * Fixed some problems with the supported Philips/SagemCom PicoPix devices,
+    now these devices are fully working (Thanks to the Certik family)
index 7ad02de..d11a92f 100644 (file)
@@ -44,8 +44,8 @@ manually with the command:
    --default-product 0x1101 \
    --message-content 55534243087052890000000000000cff020000000000000000000000000000
 
-Alternatively, on systems where libusb works but 'usb_mode_switch' is not
-easily available, the switch can be performed using the 'am7xxx_mode_switch'
+Alternatively, on systems where libusb works but 'usb-modeswitch' is not
+easily available, the switch can be performed using the 'am7xxx-modeswitch'
 example program from libam7xxx.
 
 Examples of devices based on AM7XXX are:
@@ -55,13 +55,16 @@ Examples of devices based on AM7XXX are:
       * http://support.acer.com/product/default.aspx?modelId=3888
 
   - Philips/SagemCom PicoPix projectors (PPX 1020, PPX 1230, PPX 1430, PPX
-    1630, PPX 2055):
+    1630, PPX 2055, PPX 2330):
       * http://www.philips.co.uk/c/pocket-projector/179840/cat/
       * http://www.sagemcom.com/EN/products/image-sound/pico-video-projectors.html
 
   - CEL-TEC MP-01:
       * http://www.kabelmanie.cz/miniprojektor-cel-tec-mp-01/
 
+  - Top-Height/TEC PP700
+      * http://www.ishopiwin.com/en/appliances-electronics/electronics/projectors/pico-projector-pp-700.html
+
   - Royaltek PJU-2100:
       * http://www.royaltek.com/index.php/pju-2100-pico-projector
   
@@ -88,7 +91,7 @@ All the needed files need to be in the same location:
 
   - 'libssp-0.dll' from MinGW;
 
-  - 'am7xxx_mode_switch.exe', 'libam7xxx.dll' and 'picoproj.exe' which can all
+  - 'am7xxx-modeswitch.exe', 'libam7xxx.dll' and 'picoproj.exe' which can all
     be built by following the instructions in the HACKING.asciidoc document
     from libam7xxx.
 
@@ -106,7 +109,7 @@ for both the mass storage device and the display device:
     from now on the virtual CD-ROM can't be accessed anymore until the
     +USBSTOR+ Driver is restored.
 
-  - Run 'am7xxx_mode_switch.exe'
+  - Run 'am7xxx-modeswitch.exe'
 
   - When the new (display) device shows up, run Zadig and install the +WinUSB+
     driver for it too.
diff --git a/TODO b/TODO
index 3db48f2..1ce3068 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,3 +1,6 @@
+- Get rid of atoi()
 - Write a GStreamer sink element based on libam7xxx.
 - Generate language bindings in order to use libam7xxx from other languages
   (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.)
diff --git a/contrib/50-am7xxx_mode_switch.rules b/contrib/50-am7xxx_mode_switch.rules
new file mode 100644 (file)
index 0000000..4210346
--- /dev/null
@@ -0,0 +1,3 @@
+# Rule to call am7xxx_mode_switch, useful when usb-modeswitch is not available.
+# Actions Microelectronics Co. Generic Display Device (Mass storage mode)
+ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="1de1", ATTRS{idProduct}=="1101", MODE="0660", GROUP="plugdev", RUN+="am7xxx-modeswitch"
index 53643ac..6dc7ccd 100644 (file)
@@ -8,3 +8,5 @@ ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="08ca", ATTRS{idProduct}=="214
 ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="21e7", ATTRS{idProduct}=="000e", MODE="0660", GROUP="plugdev"
 # Philips/Sagemcom PicoPix 2055
 ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="21e7", ATTRS{idProduct}=="0016", MODE="0660", GROUP="plugdev"
+# Philips/Sagemcom PicoPix 2330
+ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="21e7", ATTRS{idProduct}=="0019", MODE="0660", GROUP="plugdev"
diff --git a/contrib/performance/0001-Instrument-code-with-fps-meter.patch b/contrib/performance/0001-Instrument-code-with-fps-meter.patch
new file mode 100644 (file)
index 0000000..5c133ee
--- /dev/null
@@ -0,0 +1,65 @@
+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
+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
+ !pE^+iDQQ1yC6^,)YDKkxDd!T>\I~93>J<_`<4)A{':UrE
+
+Instrument code with fps-meter:
+http://git.ao2.it/experiments/fps-meter.git/
+---
+ examples/CMakeLists.txt | 2 +-
+ examples/am7xxx-play.c  | 4 ++++
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
+index c77a812..23af8a9 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_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
+--- a/examples/am7xxx-play.c
++++ b/examples/am7xxx-play.c
+@@ -34,6 +34,7 @@
+ #include <libswscale/swscale.h>
+ #include <am7xxx.h>
++#include "fps-meter.h"
+ /* 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,
+       int got_picture;
+       int got_packet;
+       int ret;
++      struct fps_meter_stats stats;
+       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,
+               goto cleanup_out_buf;
+       }
++      fps_meter_init(&stats);
+       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,
+                               run = 0;
+                               goto end_while;
+                       }
++                      fps_meter_update(&stats);
+               }
+ end_while:
+               if (!output_ctx.raw_output && got_packet)
+-- 
+1.8.3.2
+
diff --git a/contrib/performance/1024x768_am7xxx_send_image.log b/contrib/performance/1024x768_am7xxx_send_image.log
new file mode 100644 (file)
index 0000000..e47ce9e
--- /dev/null
@@ -0,0 +1,51 @@
+14.95
+27.28
+26.73
+23.77
+25.37
+25.30
+25.36
+27.28
+27.73
+27.28
+27.28
+27.28
+27.28
+26.74
+28.26
+26.66
+27.83
+28.26
+27.73
+28.72
+27.71
+28.27
+28.22
+28.31
+28.72
+28.26
+27.81
+26.75
+28.74
+27.73
+27.73
+28.72
+28.72
+28.72
+29.71
+28.26
+29.23
+29.23
+28.27
+27.73
+26.30
+25.91
+23.39
+21.78
+22.77
+27.37
+29.24
+24.38
+27.73
+28.72
+29.23
diff --git a/contrib/performance/1024x768_am7xxx_send_image_async.log b/contrib/performance/1024x768_am7xxx_send_image_async.log
new file mode 100644 (file)
index 0000000..a2286cd
--- /dev/null
@@ -0,0 +1,51 @@
+46.89
+48.35
+48.85
+48.95
+48.78
+49.18
+49.48
+49.61
+49.85
+50.15
+50.24
+49.53
+51.15
+48.98
+47.42
+48.27
+49.07
+49.97
+49.38
+47.96
+49.70
+49.00
+42.21
+45.65
+49.18
+50.22
+49.65
+49.12
+50.50
+53.49
+53.22
+52.99
+53.95
+53.62
+53.98
+53.60
+54.64
+53.96
+54.92
+54.42
+53.65
+54.68
+52.16
+52.81
+53.74
+55.24
+55.50
+56.83
+56.43
+56.39
+57.70
diff --git a/contrib/performance/README b/contrib/performance/README
new file mode 100644 (file)
index 0000000..c00b8db
--- /dev/null
@@ -0,0 +1,7 @@
+Menchmark methodology
+
+  - Code instrumented with fps-meter
+  - Data acquired with this commdn line:
+    am7xxx-play -f x11grab -i :0 -o video_size=1024x768
+  - Sampling repeated with either am7xxx_send_image or am7xx_send_mage_async
+  - Results compared with ministat
diff --git a/contrib/performance/ministat_report_1024x768.log b/contrib/performance/ministat_report_1024x768.log
new file mode 100644 (file)
index 0000000..f3ab0a0
--- /dev/null
@@ -0,0 +1,21 @@
+x 1024x768_am7xxx_send_image.log
++ 1024x768_am7xxx_send_image_async.log
++--------------------------------------------------------------------------+
+|                      x x                                 +               |
+|                      xxx                                 +               |
+|                     xxxx                                 +               |
+|                     xxxx                                 ++              |
+|                     xxxx                                 +++     +       |
+|                    xxxxx                                 +++     ++      |
+|                  x xxxxx                                 +++    ++++     |
+|                  xxxxxxx                              + ++++    +++++ +  |
+|x           xxxxx xxxxxxxx                     +    +  ++++++++ ++++++ +++|
+|                 |___AM__|                               |__M_A____|      |
++--------------------------------------------------------------------------+
+    N           Min           Max        Median           Avg        Stddev
+x  51         14.95         29.71         27.73     27.073529     2.4377185
++  51         42.21          57.7         50.22     51.278627     3.1996344
+Difference at 95.0% confidence
+       24.2051 +/- 1.1175
+       89.405% +/- 4.12765%
+       (Student's t, pooled s = 2.8443)
index fface99..e44ed39 100644 (file)
@@ -1,4 +1,4 @@
-# Doxyfile 1.8.1.2
+# Doxyfile 1.8.4
 
 #---------------------------------------------------------------------------
 # Project related configuration options
@@ -34,6 +34,7 @@ OPTIMIZE_FOR_FORTRAN   = NO
 OPTIMIZE_OUTPUT_VHDL   = NO
 EXTENSION_MAPPING      =
 MARKDOWN_SUPPORT       = YES
+AUTOLINK_SUPPORT       = YES
 BUILTIN_STL_SUPPORT    = NO
 CPP_CLI_SUPPORT        = NO
 SIP_SUPPORT            = NO
@@ -43,7 +44,6 @@ SUBGROUPING            = YES
 INLINE_GROUPED_CLASSES = NO
 INLINE_SIMPLE_STRUCTS  = NO
 TYPEDEF_HIDES_STRUCT   = NO
-SYMBOL_CACHE_SIZE      = 0
 LOOKUP_CACHE_SIZE      = 0
 #---------------------------------------------------------------------------
 # Build related configuration options
@@ -116,6 +116,7 @@ INPUT_FILTER           =
 FILTER_PATTERNS        =
 FILTER_SOURCE_FILES    = NO
 FILTER_SOURCE_PATTERNS =
+USE_MDFILE_AS_MAINPAGE =
 #---------------------------------------------------------------------------
 # configuration options related to source browsing
 #---------------------------------------------------------------------------
@@ -142,6 +143,7 @@ HTML_FILE_EXTENSION    = .html
 HTML_HEADER            =
 HTML_FOOTER            =
 HTML_STYLESHEET        =
+HTML_EXTRA_STYLESHEET  =
 HTML_EXTRA_FILES       =
 HTML_COLORSTYLE_HUE    = 220
 HTML_COLORSTYLE_SAT    = 100
@@ -179,10 +181,17 @@ EXT_LINKS_IN_WINDOW    = NO
 FORMULA_FONTSIZE       = 10
 FORMULA_TRANSPARENT    = YES
 USE_MATHJAX            = NO
+MATHJAX_FORMAT         = HTML-CSS
 MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
 MATHJAX_EXTENSIONS     =
+MATHJAX_CODEFILE       =
 SEARCHENGINE           = YES
 SERVER_BASED_SEARCH    = NO
+EXTERNAL_SEARCH        = NO
+SEARCHENGINE_URL       =
+SEARCHDATA_FILE        = searchdata.xml
+EXTERNAL_SEARCH_ID     =
+EXTRA_SEARCH_MAPPINGS  =
 #---------------------------------------------------------------------------
 # configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
@@ -195,6 +204,7 @@ PAPER_TYPE             = a4
 EXTRA_PACKAGES         =
 LATEX_HEADER           =
 LATEX_FOOTER           =
+LATEX_EXTRA_FILES      =
 PDF_HYPERLINKS         = YES
 USE_PDFLATEX           = YES
 LATEX_BATCHMODE        = NO
@@ -226,6 +236,11 @@ XML_SCHEMA             =
 XML_DTD                =
 XML_PROGRAMLISTING     = YES
 #---------------------------------------------------------------------------
+# configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+GENERATE_DOCBOOK       = NO
+DOCBOOK_OUTPUT         = docbook
+#---------------------------------------------------------------------------
 # configuration options for the AutoGen Definitions output
 #---------------------------------------------------------------------------
 GENERATE_AUTOGEN_DEF   = NO
@@ -255,6 +270,7 @@ TAGFILES               =
 GENERATE_TAGFILE       =
 ALLEXTERNALS           = NO
 EXTERNAL_GROUPS        = YES
+EXTERNAL_PAGES         = YES
 PERL_PATH              = /usr/bin/perl
 #---------------------------------------------------------------------------
 # Configuration options related to the dot tool
index 1754ea9..407cf1d 100644 (file)
@@ -25,6 +25,8 @@ Check @link am7xxx.h @endlink for the public API documentation.
 - Aiptek PocketCinema T25
 - Philips/SagemCom PicoPix 1020
 - Philips/SagemCom PicoPix 2055
+- Philips/SagemCom PicoPix 2330
+- Top-Height/TEC PP700
 
 @section libam7xxxDesignOverview Design Overview
 
diff --git a/doc/lsusb_dumps/lsusb_Acer-C110.log b/doc/lsusb_dumps/lsusb_Acer-C110.log
new file mode 100644 (file)
index 0000000..53f74e2
--- /dev/null
@@ -0,0 +1,68 @@
+
+Bus 004 Device 012: ID 1de1:c101 Actions Microelectronics Co. Generic Display Device
+Device Descriptor:
+  bLength                18
+  bDescriptorType         1
+  bcdUSB               2.00
+  bDeviceClass          255 Vendor Specific Class
+  bDeviceSubClass         0 
+  bDeviceProtocol         0 
+  bMaxPacketSize0        64
+  idVendor           0x1de1 Actions Microelectronics Co.
+  idProduct          0xc101 Generic Display Device
+  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
diff --git a/doc/lsusb_dumps/lsusb_Philips-PicoPix-2330.log b/doc/lsusb_dumps/lsusb_Philips-PicoPix-2330.log
new file mode 100644 (file)
index 0000000..fc57c0d
--- /dev/null
@@ -0,0 +1,68 @@
+
+Bus 002 Device 004: ID 21e7:0019  
+Device Descriptor:
+  bLength                18
+  bDescriptorType         1
+  bcdUSB               2.00
+  bDeviceClass          255 Vendor Specific Class
+  bDeviceSubClass         0 
+  bDeviceProtocol         0 
+  bMaxPacketSize0        64
+  idVendor           0x21e7 
+  idProduct          0x0019 
+  bcdDevice            0.00
+  iManufacturer           1 actions-micro
+  iProduct                2 actions-subdisplay
+  iSerial                 3 00000000000000000000000000000000
+  bNumConfigurations      1
+  Configuration Descriptor:
+    bLength                 9
+    bDescriptorType         2
+    wTotalLength           32
+    bNumInterfaces          1
+    bConfigurationValue     1
+    iConfiguration          4 Self-powered
+    bmAttributes         0xc0
+      Self Powered
+    MaxPower                2mA
+    Interface Descriptor:
+      bLength                 9
+      bDescriptorType         4
+      bInterfaceNumber        0
+      bAlternateSetting       0
+      bNumEndpoints           2
+      bInterfaceClass       255 Vendor Specific Class
+      bInterfaceSubClass      0 
+      bInterfaceProtocol      0 
+      iInterface              5 vendor subdisp
+      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
index ed46999..c7ce4bb 100644 (file)
@@ -3,6 +3,7 @@ find_package(Asciidoc)
 if(ASCIIDOC_FOUND)
   add_custom_target(manpages
     ${ASCIIDOC_A2X_EXECUTABLE} -f manpage ${CMAKE_CURRENT_SOURCE_DIR}/am7xxx-play.1.txt -D ${DOC_OUTPUT_PATH}/man
+    COMMAND ${ASCIIDOC_A2X_EXECUTABLE} -f manpage ${CMAKE_CURRENT_SOURCE_DIR}/am7xxx-modeswitch.1.txt -D ${DOC_OUTPUT_PATH}/man
     COMMAND ${ASCIIDOC_A2X_EXECUTABLE} -f manpage ${CMAKE_CURRENT_SOURCE_DIR}/picoproj.1.txt -D ${DOC_OUTPUT_PATH}/man
     WORKING_DIRECTORY ${DOC_OUTPUT_PATH}/man
     COMMENT "Generating man pages with Asciidoc" VERBATIM
@@ -17,6 +18,7 @@ if(ASCIIDOC_FOUND)
 
   install(FILES
     ${DOC_OUTPUT_PATH}/man/am7xxx-play.1
+    ${DOC_OUTPUT_PATH}/man/am7xxx-modeswitch.1
     ${DOC_OUTPUT_PATH}/man/picoproj.1
     DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man1/"
     COMPONENT manpages)
diff --git a/doc/man/am7xxx-modeswitch.1.txt b/doc/man/am7xxx-modeswitch.1.txt
new file mode 100644 (file)
index 0000000..20dfd29
--- /dev/null
@@ -0,0 +1,57 @@
+AM7XXX-MODESWITCH(1)
+=====================
+:doctype: manpage
+
+
+NAME
+----
+am7xxx-modeswitch - change the operational mode of am7xxx based devices
+
+
+SYNOPSIS
+--------
+*am7xxx-modeswitch*
+
+
+DESCRIPTION
+-----------
+am7xxx-modeswitch(1) is a minimal replacement of usb-modeswitch to use with
+am7xxx devices (e.g. Acer C110 or Philips PPX projectors) to switch from the
+mass storage device mode to the generic display mode.
+
+It is handy on systems where usb-modeswitch is not available, like Windows.
+
+
+EXAMPLE OF USE
+--------------
+
+am7xxx-modeswitch
+
+
+EXIT STATUS
+-----------
+*0*::
+    Success
+
+*!0*::
+    Failure (libusb error)
+
+
+AUTHORS
+-------
+Antonio Ospite
+
+
+RESOURCES
+---------
+Main web site: <http://git.ao2.it/libam7xxx.git>
+
+
+COPYING
+-------
+Copyright \(C) 2012  Antonio Ospite <ospite@studenti.unina.it>
+
+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 3 of the License, or
+(at your option) any later version.
index 64f15c0..c77a812 100644 (file)
@@ -62,16 +62,16 @@ if(BUILD_AM7XXX-PLAY)
     DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
 endif()
 
-# Build a simple usb_mode_switch clone for am7xxx devices
-option(BUILD_AM7XXX_MODE_SWITCH "Build a simple usb_mode_switch clone for am7xxx devices" TRUE)
-if(BUILD_AM7XXX_MODE_SWITCH)
+# Build a simple usb-modeswitch clone for am7xxx devices
+option(BUILD_am7xxx-modeswitch "Build a simple usbmode-switch clone for am7xxx devices" TRUE)
+if(BUILD_am7xxx-modeswitch)
 
   find_package(libusb-1.0 REQUIRED)
   include_directories(${LIBUSB_1_INCLUDE_DIRS})
 
-  add_executable(am7xxx_mode_switch am7xxx_mode_switch.c)
-  target_link_libraries(am7xxx_mode_switch ${LIBUSB_1_LIBRARIES})
-  install(TARGETS am7xxx_mode_switch
+  add_executable(am7xxx-modeswitch am7xxx-modeswitch.c)
+  target_link_libraries(am7xxx-modeswitch ${LIBUSB_1_LIBRARIES})
+  install(TARGETS am7xxx-modeswitch
     DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
 endif()
 
diff --git a/examples/am7xxx-modeswitch.c b/examples/am7xxx-modeswitch.c
new file mode 100644 (file)
index 0000000..9942d81
--- /dev/null
@@ -0,0 +1,97 @@
+/* am7xxx-modeswitch - a simple usb-modeswitch for am7xxx devices
+ *
+ * Copyright (C) 2012  Antonio Ospite <ospite@studenti.unina.it>
+ *
+ * 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
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <libusb.h>
+
+#define AM7XXX_STORAGE_VID           0x1de1
+#define AM7XXX_STORAGE_PID           0x1101
+#define AM7XXX_STORAGE_CONFIGURATION 1
+#define AM7XXX_STORAGE_INTERFACE     0
+#define AM7XXX_STORAGE_OUT_EP        0x01
+
+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";
+
+int main(void)
+{
+       int ret;
+       int transferred;
+       libusb_device_handle *usb_device = NULL;
+
+       unsigned int len;
+
+       ret = libusb_init(NULL);
+       if (ret < 0)
+               goto out;
+
+       libusb_set_debug(NULL, 3);
+
+       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);
+               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);
+       if (ret < 0) {
+               fprintf(stderr, "cannot set configuration.\n");
+               goto out_libusb_close;
+       }
+
+       ret = libusb_claim_interface(usb_device, AM7XXX_STORAGE_INTERFACE);
+       if (ret < 0) {
+               fprintf(stderr, "cannot claim interface.\n");
+               goto out_libusb_close;
+       }
+
+       len = sizeof(switch_command);
+       transferred = 0;
+       ret = libusb_bulk_transfer(usb_device, AM7XXX_STORAGE_OUT_EP,
+                                  switch_command, len, &transferred, 0);
+       if (ret != 0 || (unsigned int)transferred != len) {
+               fprintf(stderr, "ret: %d\ttransferred: %d (expected %u)\n",
+                     ret, transferred, len);
+               goto out_libusb_release_interface;
+       }
+
+       fprintf(stderr, "OK, command sent!\n");
+
+out_libusb_release_interface:
+       libusb_release_interface(usb_device, AM7XXX_STORAGE_INTERFACE);
+out_libusb_close:
+       libusb_close(usb_device);
+       usb_device = NULL;
+out:
+       libusb_exit(NULL);
+       return ret;
+}
index 38812a4..3230e67 100644 (file)
@@ -291,7 +291,7 @@ static int am7xxx_play(const char *input_format_string,
        AVPacket out_packet;
        int got_picture;
        int got_packet;
-       int ret = 0;
+       int ret;
 
        ret = video_input_init(&input_ctx, input_format_string, input_path, input_options);
        if (ret < 0) {
@@ -427,7 +427,7 @@ static int am7xxx_play(const char *input_format_string,
                        fclose(file);
 #endif
 
-                       ret = am7xxx_send_image(dev,
+                       ret = am7xxx_send_image_async(dev,
                                                image_format,
                                                (output_ctx.codec_ctx)->width,
                                                (output_ctx.codec_ctx)->height,
@@ -440,7 +440,7 @@ static int am7xxx_play(const char *input_format_string,
                        }
                }
 end_while:
-               if (!output_ctx.raw_output)
+               if (!output_ctx.raw_output && got_packet)
                        av_free_packet(&out_packet);
                av_free_packet(&in_packet);
        }
@@ -555,7 +555,7 @@ static int set_signal_handler(void (*signal_handler)(int))
 {
        struct sigaction new_action;
        struct sigaction old_action;
-       int ret = 0;
+       int ret;
 
        new_action.sa_handler = signal_handler;
        sigemptyset(&new_action.sa_mask);
@@ -635,8 +635,8 @@ int main(int argc, char *argv[])
        unsigned int quality = 95;
        int log_level = AM7XXX_LOG_INFO;
        int device_index = 0;
-       am7xxx_power_mode power_mode = AM7XXX_POWER_LOW;
-       am7xxx_zoom_mode zoom = AM7XXX_ZOOM_ORIGINAL;
+       int power_mode = AM7XXX_POWER_LOW;
+       int zoom = AM7XXX_ZOOM_ORIGINAL;
        int format = AM7XXX_IMAGE_FORMAT_JPEG;
        am7xxx_context *ctx;
        am7xxx_device *dev;
@@ -762,7 +762,8 @@ int main(int argc, char *argv[])
                        default:
                                fprintf(stderr, "Invalid zoom mode value, must be between %d and %d\n",
                                        AM7XXX_ZOOM_ORIGINAL, AM7XXX_ZOOM_TEST);
-                               exit(EXIT_FAILURE);
+                               ret = -EINVAL;
+                               goto out;
                        }
                        break;
                case 'h':
@@ -777,7 +778,8 @@ int main(int argc, char *argv[])
        }
 
        if (input_path == NULL) {
-               fprintf(stderr, "The -i option must always be passed\n");
+               fprintf(stderr, "The -i option must always be passed\n\n");
+               usage(argv[0]);
                ret = -EINVAL;
                goto out;
        }
diff --git a/examples/am7xxx_mode_switch.c b/examples/am7xxx_mode_switch.c
deleted file mode 100644 (file)
index 7509981..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/* am7xxx_mode_switch - a simple usb_mode_switch for am7xxx devices
- *
- * Copyright (C) 2012  Antonio Ospite <ospite@studenti.unina.it>
- *
- * 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
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-#include <errno.h>
-#include <libusb.h>
-
-#define AM7XXX_STORAGE_VID           0x1de1
-#define AM7XXX_STORAGE_PID           0x1101
-#define AM7XXX_STORAGE_CONFIGURATION 1
-#define AM7XXX_STORAGE_INTERFACE     0
-#define AM7XXX_STORAGE_OUT_EP        0x01
-
-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";
-
-int main(void)
-{
-       int ret;
-       int transferred;
-       libusb_device_handle *usb_device = NULL;
-
-       unsigned int len;
-
-       ret = libusb_init(NULL);
-       if (ret < 0)
-               goto out;
-
-       libusb_set_debug(NULL, 3);
-
-       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);
-               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);
-       if (ret < 0) {
-               fprintf(stderr, "cannot set configuration.\n");
-               goto out_libusb_close;
-       }
-
-       ret = libusb_claim_interface(usb_device, AM7XXX_STORAGE_INTERFACE);
-       if (ret < 0) {
-               fprintf(stderr, "cannot claim interface.\n");
-               goto out_libusb_close;
-       }
-
-       len = sizeof(switch_command);
-       transferred = 0;
-       ret = libusb_bulk_transfer(usb_device, AM7XXX_STORAGE_OUT_EP,
-                                  switch_command, len, &transferred, 0);
-       if (ret != 0 || (unsigned int)transferred != len) {
-               fprintf(stderr, "ret: %d\ttransferred: %d (expected %u)\n",
-                     ret, transferred, len);
-               goto out_libusb_close;
-       }
-
-       fprintf(stderr, "OK, command sent!\n");
-
-out_libusb_close:
-       libusb_release_interface(usb_device, AM7XXX_STORAGE_INTERFACE);
-       libusb_close(usb_device);
-       usb_device = NULL;
-
-out:
-       libusb_exit(NULL);
-       return ret;
-}
index 045f386..9128538 100644 (file)
@@ -28,6 +28,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <errno.h>
 
 #include "am7xxx.h"
 
@@ -58,7 +59,6 @@ static void usage(char *name)
 int main(int argc, char *argv[])
 {
        int ret;
-       int exit_code = EXIT_SUCCESS;
        int opt;
 
        char filename[FILENAME_MAX] = {0};
@@ -68,13 +68,13 @@ int main(int argc, char *argv[])
        am7xxx_device *dev;
        int log_level = AM7XXX_LOG_INFO;
        int device_index = 0;
-       am7xxx_power_mode power_mode = AM7XXX_POWER_LOW;
-       am7xxx_zoom_mode zoom = AM7XXX_ZOOM_ORIGINAL;
+       int power_mode = AM7XXX_POWER_LOW;
+       int zoom = AM7XXX_ZOOM_ORIGINAL;
        int format = AM7XXX_IMAGE_FORMAT_JPEG;
        int width = 800;
        int height = 480;
        unsigned char *image;
-       unsigned int size;
+       off_t size;
        am7xxx_device_info device_info;
 
        while ((opt = getopt(argc, argv, "d:f:F:l:p:z:W:H:h")) != -1) {
@@ -83,7 +83,8 @@ int main(int argc, char *argv[])
                        device_index = atoi(optarg);
                        if (device_index < 0) {
                                fprintf(stderr, "Unsupported device index\n");
-                               exit(EXIT_FAILURE);
+                               ret = -EINVAL;
+                               goto out;
                        }
                        break;
                case 'f':
@@ -102,7 +103,8 @@ int main(int argc, char *argv[])
                                break;
                        default:
                                fprintf(stderr, "Unsupported format\n");
-                               exit(EXIT_FAILURE);
+                               ret = -EINVAL;
+                               goto out;
                        }
                        break;
                case 'l':
@@ -125,7 +127,8 @@ int main(int argc, char *argv[])
                        default:
                                fprintf(stderr, "Invalid power mode value, must be between %d and %d\n",
                                        AM7XXX_POWER_OFF, AM7XXX_POWER_TURBO);
-                               exit(EXIT_FAILURE);
+                               ret = -EINVAL;
+                               goto out;
                        }
                        break;
                case 'z':
@@ -140,48 +143,53 @@ int main(int argc, char *argv[])
                        default:
                                fprintf(stderr, "Invalid zoom mode value, must be between %d and %d\n",
                                        AM7XXX_ZOOM_ORIGINAL, AM7XXX_ZOOM_TEST);
-                               exit(EXIT_FAILURE);
+                               ret = -EINVAL;
+                               goto out;
                        }
                        break;
                case 'W':
                        width = atoi(optarg);
                        if (width < 0) {
                                fprintf(stderr, "Unsupported width\n");
-                               exit(EXIT_FAILURE);
+                               ret = -EINVAL;
+                               goto out;
                        }
                        break;
                case 'H':
                        height = atoi(optarg);
                        if (height < 0) {
                                fprintf(stderr, "Unsupported height\n");
-                               exit(EXIT_FAILURE);
+                               ret = -EINVAL;
+                               goto out;
                        }
                        break;
                case 'h':
                        usage(argv[0]);
-                       exit(EXIT_SUCCESS);
-                       break;
+                       ret = 0;
+                       goto out;
                default: /* '?' */
                        usage(argv[0]);
-                       exit(EXIT_FAILURE);
+                       ret = -EINVAL;
+                       goto out;
                }
        }
 
        if (filename[0] == '\0') {
-               fprintf(stderr, "An image file MUST be specified.\n");
-               exit_code = EXIT_FAILURE;
+               fprintf(stderr, "An image file MUST be specified with the -f option.\n\n");
+               usage(argv[0]);
+               ret = -EINVAL;
                goto out;
        }
 
        image_fp = fopen(filename, "rb");
        if (image_fp == NULL) {
                perror("fopen");
-               exit_code = EXIT_FAILURE;
+               ret = -EINVAL;
                goto out;
        }
-       if (fstat(fileno(image_fp), &st) < 0) {
+       ret = fstat(fileno(image_fp), &st);
+       if (ret < 0) {
                perror("fstat");
-               exit_code = EXIT_FAILURE;
                goto out_close_image_fp;
        }
        size = st.st_size;
@@ -189,11 +197,11 @@ int main(int argc, char *argv[])
        image = malloc(size * sizeof(unsigned char));
        if (image == NULL) {
                perror("malloc");
-               exit_code = EXIT_FAILURE;
+               ret = -ENOMEM;
                goto out_close_image_fp;
        }
 
-       ret = fread(image, size, 1, image_fp);
+       ret = (int)fread(image, size, 1, image_fp);
        if (ret != 1) {
                if (feof(image_fp))
                        fprintf(stderr, "Unexpected end of file.\n");
@@ -202,13 +210,14 @@ int main(int argc, char *argv[])
                else
                        fprintf(stderr, "Unexpected error condition.\n");
 
+               if (ret >= 0)
+                       ret = -EINVAL;
                goto out_free_image;
        }
 
        ret = am7xxx_init(&ctx);
        if (ret < 0) {
                perror("am7xxx_init");
-               exit_code = EXIT_FAILURE;
                goto out_free_image;
        }
 
@@ -217,29 +226,24 @@ int main(int argc, char *argv[])
        ret = am7xxx_open_device(ctx, &dev, 0);
        if (ret < 0) {
                perror("am7xxx_open_device");
-               exit_code = EXIT_FAILURE;
                goto cleanup;
        }
 
-
        ret = am7xxx_close_device(dev);
        if (ret < 0) {
                perror("am7xxx_close_device");
-               exit_code = EXIT_FAILURE;
                goto cleanup;
        }
 
        ret = am7xxx_open_device(ctx, &dev, device_index);
        if (ret < 0) {
                perror("am7xxx_open_device");
-               exit_code = EXIT_FAILURE;
                goto cleanup;
        }
 
        ret = am7xxx_get_device_info(dev, &device_info);
        if (ret < 0) {
                perror("am7xxx_get_device_info");
-               exit_code = EXIT_FAILURE;
                goto cleanup;
        }
        printf("Native resolution: %dx%d\n",
@@ -248,14 +252,12 @@ int main(int argc, char *argv[])
        ret = am7xxx_set_zoom_mode(dev, zoom);
        if (ret < 0) {
                perror("am7xxx_set_zoom_mode");
-               exit_code = EXIT_FAILURE;
                goto cleanup;
        }
 
        ret = am7xxx_set_power_mode(dev, power_mode);
        if (ret < 0) {
                perror("am7xxx_set_power_mode");
-               exit_code = EXIT_FAILURE;
                goto cleanup;
        }
 
@@ -265,19 +267,19 @@ int main(int argc, char *argv[])
                goto cleanup;
        }
 
-
        if ((unsigned int)width > device_info.native_width ||
            (unsigned int)height > device_info.native_height)
-               fprintf(stderr, "WARNING: image not fitting the native resolution, it may be displayed wrongly!\n");
+               fprintf(stderr,
+                       "WARNING: image is %dx%d, not fitting the native resolution, it may be displayed wrongly!\n",
+                       width, height);
 
-       ret = am7xxx_send_image(dev, format, width, height, image, size);
+       ret = am7xxx_send_image(dev, format, width, height, image, (unsigned int)size);
        if (ret < 0) {
                perror("am7xxx_send_image");
-               exit_code = EXIT_FAILURE;
                goto cleanup;
        }
 
-       exit_code = EXIT_SUCCESS;
+       ret = 0;
 
 cleanup:
        am7xxx_shutdown(ctx);
@@ -286,10 +288,9 @@ out_free_image:
        free(image);
 
 out_close_image_fp:
-       ret = fclose(image_fp);
-       if (ret == EOF)
+       if (fclose(image_fp) == EOF)
                perror("fclose");
 
 out:
-       exit(exit_code);
+       return ret;
 }
index 83d9a91..5b1e35a 100644 (file)
@@ -64,37 +64,61 @@ static void log_message(am7xxx_context *ctx,
 #define debug(ctx, ...)   log_message(ctx,  AM7XXX_LOG_DEBUG,   __func__, 0,        __VA_ARGS__)
 #define trace(ctx, ...)   log_message(ctx,  AM7XXX_LOG_TRACE,   NULL,     0,        __VA_ARGS__)
 
+#define AM7XXX_QUIRK_NO_POWER_MODE (1 << 0)
+#define AM7XXX_QUIRK_NO_ZOOM_MODE  (1 << 1)
+
 struct am7xxx_usb_device_descriptor {
        const char *name;
        uint16_t vendor_id;
        uint16_t product_id;
+       uint8_t configuration;    /* The bConfigurationValue of the device */
+       uint8_t interface_number; /* The bInterfaceNumber of the device */
+       unsigned long quirks;
 };
 
-static struct am7xxx_usb_device_descriptor supported_devices[] = {
+static const struct am7xxx_usb_device_descriptor supported_devices[] = {
        {
                .name       = "Acer C110",
                .vendor_id  = 0x1de1,
                .product_id = 0xc101,
+               .configuration    = 2,
+               .interface_number = 0,
        },
        {
                .name       = "Acer C112",
                .vendor_id  = 0x1de1,
                .product_id = 0x5501,
+               .configuration    = 2,
+               .interface_number = 0,
        },
        {
                .name       ="Aiptek PocketCinema T25",
                .vendor_id  = 0x08ca,
                .product_id = 0x2144,
+               .configuration    = 2,
+               .interface_number = 0,
        },
        {
                .name       = "Philips/Sagemcom PicoPix 1020",
                .vendor_id  = 0x21e7,
                .product_id = 0x000e,
+               .configuration    = 2,
+               .interface_number = 0,
        },
        {
                .name       = "Philips/Sagemcom PicoPix 2055",
                .vendor_id  = 0x21e7,
                .product_id = 0x0016,
+               .configuration    = 2,
+               .interface_number = 0,
+       },
+       {
+               .name       = "Philips/Sagemcom PicoPix 2330",
+               .vendor_id  = 0x21e7,
+               .product_id = 0x0019,
+               .configuration    = 1,
+               .interface_number = 0,
+               .quirks = AM7XXX_QUIRK_NO_POWER_MODE | AM7XXX_QUIRK_NO_ZOOM_MODE,
        },
 };
 
@@ -106,9 +130,12 @@ static struct am7xxx_usb_device_descriptor supported_devices[] = {
 
 struct _am7xxx_device {
        libusb_device_handle *usb_device;
+       struct libusb_transfer *transfer;
+       int transfer_completed;
        uint8_t buffer[AM7XXX_HEADER_WIRE_SIZE];
        am7xxx_device_info *device_info;
        am7xxx_context *ctx;
+       const struct am7xxx_usb_device_descriptor *desc;
        am7xxx_device *next;
 };
 
@@ -347,6 +374,117 @@ static int send_data(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
        return 0;
 }
 
+static void send_data_async_complete_cb(struct libusb_transfer *transfer)
+{
+       am7xxx_device *dev = (am7xxx_device *)(transfer->user_data);
+       int *completed = &(dev->transfer_completed);
+       int transferred = transfer->actual_length;
+       int ret;
+
+       if (transferred != transfer->length) {
+               error(dev->ctx, "transferred: %d (expected %u)\n",
+                     transferred, transfer->length);
+       }
+
+       switch (transfer->status) {
+       case LIBUSB_TRANSFER_COMPLETED:
+               ret = 0;
+               break;
+       case LIBUSB_TRANSFER_TIMED_OUT:
+               ret = LIBUSB_ERROR_TIMEOUT;
+               break;
+       case LIBUSB_TRANSFER_STALL:
+               ret = LIBUSB_ERROR_PIPE;
+               break;
+       case LIBUSB_TRANSFER_OVERFLOW:
+               ret = LIBUSB_ERROR_OVERFLOW;
+               break;
+       case LIBUSB_TRANSFER_NO_DEVICE:
+               ret = LIBUSB_ERROR_NO_DEVICE;
+               break;
+       case LIBUSB_TRANSFER_ERROR:
+       case LIBUSB_TRANSFER_CANCELLED:
+               ret = LIBUSB_ERROR_IO;
+               break;
+       default:
+               error(dev->ctx, "unrecognised status code %d", transfer->status);
+               ret = LIBUSB_ERROR_OTHER;
+       }
+
+       if (ret < 0)
+               error(dev->ctx, "libusb transfer failed: %s",
+                     libusb_error_name(ret));
+
+       libusb_free_transfer(transfer);
+       transfer = NULL;
+
+       *completed = 1;
+}
+
+static inline void wait_for_trasfer_completed(am7xxx_device *dev)
+{
+       while (!dev->transfer_completed) {
+               int ret = libusb_handle_events_completed(dev->ctx->usb_context,
+                                                        &(dev->transfer_completed));
+               if (ret < 0) {
+                       if (ret == LIBUSB_ERROR_INTERRUPTED)
+                               continue;
+                       error(dev->ctx, "libusb_handle_events failed: %s, cancelling transfer and retrying",
+                             libusb_error_name(ret));
+                       libusb_cancel_transfer(dev->transfer);
+                       continue;
+               }
+       }
+}
+
+static int send_data_async(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
+{
+       int ret;
+       uint8_t *transfer_buffer;
+
+       dev->transfer = libusb_alloc_transfer(0);
+       if (dev->transfer == NULL) {
+               error(dev->ctx, "cannot allocate transfer (%s)\n",
+                     strerror(errno));
+               return -ENOMEM;
+       }
+
+       /* 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. */
+       transfer_buffer = malloc(len);
+       if (transfer_buffer == NULL) {
+               error(dev->ctx, "cannot allocate transfer buffer (%s)\n",
+                     strerror(errno));
+               ret = -ENOMEM;
+               goto err;
+       }
+       memcpy(transfer_buffer, buffer, len);
+
+       dev->transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
+       libusb_fill_bulk_transfer(dev->transfer, dev->usb_device, 0x1,
+                                 transfer_buffer, len,
+                                 send_data_async_complete_cb, dev, 0);
+
+       /* wait for the previous transfer to complete */
+       wait_for_trasfer_completed(dev);
+
+       trace_dump_buffer(dev->ctx, "sending -->", buffer, len);
+
+       dev->transfer_completed = 0;
+       ret = libusb_submit_transfer(dev->transfer);
+       if (ret < 0)
+               goto err;
+
+       return 0;
+
+err:
+       libusb_free_transfer(dev->transfer);
+       dev->transfer = NULL;
+       return ret;
+}
+
 static void serialize_header(struct am7xxx_header *h, uint8_t *buffer)
 {
        uint8_t **buffer_iterator = &buffer;
@@ -451,7 +589,8 @@ static void log_message(am7xxx_context *ctx,
        return;
 }
 
-static am7xxx_device *add_new_device(am7xxx_context *ctx)
+static am7xxx_device *add_new_device(am7xxx_context *ctx,
+                                    const struct am7xxx_usb_device_descriptor *desc)
 {
        am7xxx_device **devices_list;
        am7xxx_device *new_device;
@@ -469,6 +608,8 @@ static am7xxx_device *add_new_device(am7xxx_context *ctx)
        memset(new_device, 0, sizeof(*new_device));
 
        new_device->ctx = ctx;
+       new_device->desc = desc;
+       new_device->transfer_completed = 1;
 
        devices_list = &(ctx->devices_list);
 
@@ -526,7 +667,7 @@ typedef enum {
 static int scan_devices(am7xxx_context *ctx, scan_op op,
                        unsigned int open_device_index, am7xxx_device **dev)
 {
-       int num_devices;
+       ssize_t num_devices;
        libusb_device** list;
        unsigned int current_index;
        int i;
@@ -565,7 +706,7 @@ static int scan_devices(am7xxx_context *ctx, scan_op op,
                                        info(ctx, "am7xxx device found, index: %d, name: %s\n",
                                             current_index,
                                             supported_devices[j].name);
-                                       new_device = add_new_device(ctx);
+                                       new_device = add_new_device(ctx, &supported_devices[j]);
                                        if (new_device == NULL) {
                                                /* XXX, the caller may want
                                                 * to call am7xxx_shutdown() if
@@ -598,8 +739,32 @@ static int scan_devices(am7xxx_context *ctx, scan_op op,
                                                goto out;
                                        }
 
-                                       libusb_set_configuration((*dev)->usb_device, 2);
-                                       libusb_claim_interface((*dev)->usb_device, 0);
+                                       /* 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;
+                                       }
+
                                        goto out;
                                }
                                current_index++;
@@ -625,7 +790,7 @@ out:
 
 AM7XXX_PUBLIC int am7xxx_init(am7xxx_context **ctx)
 {
-       int ret = 0;
+       int ret;
 
        *ctx = malloc(sizeof(**ctx));
        if (*ctx == NULL) {
@@ -642,7 +807,7 @@ AM7XXX_PUBLIC int am7xxx_init(am7xxx_context **ctx)
        if (ret < 0)
                goto out_free_context;
 
-       libusb_set_debug((*ctx)->usb_context, 3);
+       libusb_set_debug((*ctx)->usb_context, LIBUSB_LOG_LEVEL_INFO);
 
        ret = scan_devices(*ctx, SCAN_OP_BUILD_DEVLIST , 0, NULL);
        if (ret < 0) {
@@ -735,7 +900,8 @@ AM7XXX_PUBLIC int am7xxx_close_device(am7xxx_device *dev)
                return -EINVAL;
        }
        if (dev->usb_device) {
-               libusb_release_interface(dev->usb_device, 0);
+               wait_for_trasfer_completed(dev);
+               libusb_release_interface(dev->usb_device, dev->desc->interface_number);
                libusb_close(dev->usb_device);
                dev->usb_device = NULL;
        }
@@ -899,6 +1065,42 @@ AM7XXX_PUBLIC int am7xxx_send_image(am7xxx_device *dev,
        return send_data(dev, image, image_size);
 }
 
+AM7XXX_PUBLIC int am7xxx_send_image_async(am7xxx_device *dev,
+                                         am7xxx_image_format format,
+                                         unsigned int width,
+                                         unsigned int height,
+                                         uint8_t *image,
+                                         unsigned int image_size)
+{
+       int ret;
+       struct am7xxx_header h = {
+               .packet_type     = AM7XXX_PACKET_TYPE_IMAGE,
+               .direction       = AM7XXX_DIRECTION_OUT,
+               .header_data_len = sizeof(struct am7xxx_image_header),
+               .unknown2        = 0x3e,
+               .unknown3        = 0x10,
+               .header_data = {
+                       .image = {
+                               .format     = format,
+                               .width      = width,
+                               .height     = height,
+                               .image_size = image_size,
+                       },
+               },
+       };
+
+       ret = send_header(dev, &h);
+       if (ret < 0)
+               return ret;
+
+       if (image == NULL || image_size == 0) {
+               warning(dev->ctx, "Not sending any data, check the 'image' or 'image_size' parameters\n");
+               return 0;
+       }
+
+       return send_data_async(dev, image, image_size);
+}
+
 AM7XXX_PUBLIC int am7xxx_set_power_mode(am7xxx_device *dev, am7xxx_power_mode power)
 {
        int ret;
@@ -910,6 +1112,12 @@ AM7XXX_PUBLIC int am7xxx_set_power_mode(am7xxx_device *dev, am7xxx_power_mode po
                .unknown3        = 0x10,
        };
 
+       if (dev->desc->quirks & AM7XXX_QUIRK_NO_POWER_MODE) {
+               debug(dev->ctx,
+                     "setting power mode is unsupported on this device\n");
+               return 0;
+       }
+
        switch(power) {
        case AM7XXX_POWER_OFF:
                h.header_data.power.bit2 = 0;
@@ -964,6 +1172,12 @@ AM7XXX_PUBLIC int am7xxx_set_zoom_mode(am7xxx_device *dev, am7xxx_zoom_mode zoom
                .unknown3        = 0x10,
        };
 
+       if (dev->desc->quirks & AM7XXX_QUIRK_NO_ZOOM_MODE) {
+               debug(dev->ctx,
+                     "setting zoom mode is unsupported on this device\n");
+               return 0;
+       }
+
        switch(zoom) {
        case AM7XXX_ZOOM_ORIGINAL:
                h.header_data.zoom.bit1 = 0;
index 24636c3..ec9869d 100644 (file)
@@ -219,7 +219,7 @@ int am7xxx_calc_scaled_image_dimensions(am7xxx_device *dev,
                                        unsigned int *scaled_width,
                                        unsigned int *scaled_height);
 /**
- * Send an image for display on a am7xxx device.
+ * Send an image for display on an am7xxx device.
  *
  * 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
@@ -242,6 +242,32 @@ int am7xxx_send_image(am7xxx_device *dev,
                      unsigned int image_size);
 
 /**
+ * Queue transfer of an image for display on an am7xxx device and return immediately.
+ *
+ * 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.
+ *
+ * @param[in] dev A pointer to the structure representing the device to get info of
+ * @param[in] format The format the image is in (see @link am7xxx_image_format @endlink enum)
+ * @param[in] width The width of the image
+ * @param[in] height The height of the image
+ * @param[in] image A buffer holding data in the format specified by the format parameter
+ * @param[in] image_size The size in bytes of the image buffer
+ *
+ * @return 0 on success, a negative value on error
+ */
+int am7xxx_send_image_async(am7xxx_device *dev,
+                           am7xxx_image_format format,
+                           unsigned int width,
+                           unsigned int height,
+                           unsigned char *image,
+                           unsigned int image_size);
+
+/**
  * Set the power mode of an am7xxx device.
  *
  * @note When setting the mode to AM7XXX_POWER_OFF the display can't be turned