Merge tag 'v0.1.3' into debian
authorAntonio Ospite <ospite@studenti.unina.it>
Thu, 14 Mar 2013 21:07:07 +0000 (22:07 +0100)
committerAntonio Ospite <ospite@studenti.unina.it>
Thu, 14 Mar 2013 21:07:07 +0000 (22:07 +0100)
Release version 0.1.3

25 files changed:
CMakeLists.txt
ChangeLog
HACKING.asciidoc
README.asciidoc
TODO
cmake_modules/FindFFmpeg.cmake
cmake_modules/mingw_cross_toolchain.cmake [new file with mode: 0644]
contrib/55-am7xxx.rules
contrib/99-am7xxx-autodisplay.rules [new file with mode: 0644]
contrib/am7xxx-autodisplay.sh [new file with mode: 0755]
contrib/am7xxx-play-window.sh [new file with mode: 0755]
contrib/libam7xxx_test_image_800x480.svg [new file with mode: 0644]
doc/Doxyfile.in
doc/DoxygenMainpage.dox
doc/man/am7xxx-play.1.txt
doc/man/picoproj.1.txt
examples/CMakeLists.txt
examples/am7xxx-play.c
examples/am7xxx_mode_switch.c [new file with mode: 0644]
examples/picoproj.c
src/CMakeLists.txt
src/am7xxx.c
src/am7xxx.h
src/serialize.c
src/serialize.h

index 23627fa..89f0437 100644 (file)
@@ -6,7 +6,7 @@ set(PROJECT_DESCRIPTION
 
 set(PROJECT_VER_MAJOR 0)
 set(PROJECT_VER_MINOR 1)
-set(PROJECT_VER_PATCH 2)
+set(PROJECT_VER_PATCH 3)
 set(PROJECT_VER_EXTRA "")
 set(PROJECT_VER
   "${PROJECT_VER_MAJOR}.${PROJECT_VER_MINOR}.${PROJECT_VER_PATCH}${PROJECT_VER_EXTRA}")
@@ -48,7 +48,6 @@ if (CMAKE_COMPILER_IS_GNUCC)
     -Winit-self
     -Winline
     -Wpacked
-    -Wp,-D_FORTIFY_SOURCE=2
     -Wpointer-arith
     -Wlarger-than-65500
     -Wmissing-declarations
@@ -69,19 +68,26 @@ if (CMAKE_COMPILER_IS_GNUCC)
     -fstack-protector
     --param=ssp-buffer-size=4)
 
+  add_flags(DEBUG_FLAGS
+    -ggdb)
+
+  add_flags(RELEASE_FLAGS
+    -Wp,-D_FORTIFY_SOURCE=2)
+
   if (STRICT_COMPILATION_CHECKS)
-    add_flags(CMAKE_C_FLAGS
+    add_flags(STRICT_FLAGS
       -Werror
       # NOTE: Vanilla libusb-1.0.8 can't live with -pedantic-errors
       -pedantic-errors
       # NOTE: GCC >= 4.6 is needed for -Wunused-but-set-variable
       -Wunused-but-set-variable)
+
   endif()
 endif()
 
-set(CMAKE_C_FLAGS_DEBUG "-O0 -ggdb -DDEBUG=1")
-set(CMAKE_C_FLAGS_RELEASE "-O2")
-set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g")
+set(CMAKE_C_FLAGS_DEBUG "-O0 ${DEBUG_FLAGS} ${STRICT_FLAGS}")
+set(CMAKE_C_FLAGS_RELEASE "-O2 ${RELEASE_FLAGS} ${STRICT_FLAGS}")
+set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 ${RELEASE_FLAGS} ${DEBUG_FLAGS} ${STRICT_FLAGS}")
 
 # Add library project
 add_subdirectory(src)
index 9baa19c..340d105 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,274 @@
+2013-03-14 20:23:49 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * Release version 0.1.3 (HEAD, master)
+
+2013-03-14 19:48:14 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx-play: switch to avcodec_encode_video2() (origin/master)
+
+2013-03-14 19:30:07 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx-play: remove an unreachable break
+
+2013-03-14 12:28:32 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx-play: rename 'packet' to 'in_packet'
+
+2013-03-14 11:22:05 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: fix coding style
+
+2013-03-14 11:13:16 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * contrib: add the am7xxx-play-window.sh script
+
+2012-12-17 23:54:51 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * Merge branch 'fix-devinfo-for-PicoPix'
+
+2012-12-07 11:59:32 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: make libam7xxx work with Philips/Sagemcom PPX projectors
+
+2012-12-07 11:51:16 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: cache device info in am7xxx_get_device_info()
+
+2012-12-07 12:22:45 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: detect unexpected responses to AM7XXX_PACKET_TYPE_DEVINFO requests
+
+2012-12-04 11:46:12 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * Fix an error when compiling with both -O0 and -Wp,-D_FORTIFY_SOURCE=2
+
+2012-11-14 12:31:01 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: don't mention AM7XXX_DIRECTION_OUT in read_header()
+
+2012-11-14 12:08:07 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: add a note on the symmetry of read_header() and send_header()
+
+2012-11-14 12:01:34 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: assign device_list next to its first use
+
+2012-11-14 11:00:58 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: print text description of the 'direction' field
+
+2012-10-14 18:23:04 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoproj: clarify that when AM7XXX_ZOOM_TEST is set no image gets sent
+
+2012-10-14 18:03:54 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: add support for Philips/SagemCom PicoPix PPX 2055
+
+2012-09-17 10:07:55 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * examples: support multiple devices
+
+2012-09-17 10:10:07 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * doc: update Doxygen configuration
+
+2012-07-27 12:57:44 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * Merge branch 'am7xxx_set_zoom_mode'
+
+2012-06-21 10:12:14 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * contrib: add a test image to show how zoom modes work
+
+2012-06-21 10:41:42 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx-play: make the help about power mode more consistent
+
+2012-06-20 15:32:13 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoproj: make the help about power mode more consistent
+
+2012-06-20 13:13:20 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: update signature and documentation of am7xxx_set_power_mode()
+
+2012-06-21 10:32:32 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx-play: support setting the zoom mode
+
+2012-02-20 13:43:03 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoproj: support setting the zoom mode
+
+2012-02-20 13:37:20 +0100 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: implement support for the AM7XXX_PACKET_TYPE_ZOOM
+
+2012-07-08 23:17:20 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * Merge remote-tracking branch 'origin/rettichschnidi'
+
+2012-07-08 22:56:08 +0200 Reto Schneider <github@reto-schneider.ch>
+
+       * Add missing break.
+
+2012-07-06 00:48:31 +0200 Reto Schneider <github@reto-schneider.ch>
+
+       * Fix typo.
+
+2012-06-29 13:22:55 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * contrib: add an example of how to start displaying images automatically
+
+2012-06-21 10:23:28 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * contrib: add other supported devices to 55-am7xxx.rules
+
+2012-06-20 15:01:02 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * doc: update the list of supported devices
+
+2012-06-12 12:48:28 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: rename am7xxx_header.unknown0 to am7xxx_header.direction
+
+2012-06-09 12:21:36 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * README.asciidoc: add Aiptek PocketCinema T25 to the AM7XXX devices list
+
+2012-06-09 12:19:28 +0200 Matti Koskinen <mjkoskin@kolumbus.fi>
+
+       * am7xxx: add support for Aiptek PocketCinema T25
+
+2012-05-22 16:34:29 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * README.asciidoc: add info about running am7xxx-play.exe on Windows
+
+2012-05-22 16:33:44 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * HACKING.asciidoc: add info about compiling am7xxx-play for Windows
+
+2012-05-22 16:23:10 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx-play: check if strtok_r is available
+
+2012-05-22 16:21:25 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx-play: check if sigaction is available
+
+2012-05-22 16:16:15 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx-play: add a fallback definition for ENOTSUP
+
+2012-05-22 15:42:26 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx-play: get the framerate from the video stream
+
+2012-05-22 15:29:55 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * mingw_cross_toolchain.cmake: set the MINGW variable to True
+
+2012-05-15 10:35:05 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * HACKING.asciidoc: add info about getting and building libam7xxx
+
+2012-05-14 14:42:44 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * README.asciidoc: add Royaltek PJU-2100 to the AM7XXX based devices list
+
+2012-05-13 10:25:52 +0200 Richard Wisenoecker <richard47@gmx.at>
+
+       * am7xxx: add support for Acer C112
+
+2012-05-11 21:52:29 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * README.asciidoc document how to get libam7xx running on MS Windows
+
+2012-05-11 21:50:36 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * Add a simple usb_mode_switch clone for am7xxx devices
+
+2012-05-10 17:00:27 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * TODO: mention the plan about GStreamer
+
+2012-05-10 16:02:16 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * Merge branch 'mingw-port'
+
+2012-05-10 15:46:47 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * HACKING.asciidoc: add a section to explain Windows cross compilation
+
+2012-05-10 15:29:56 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * Add a CMAKE_TOOLCHAIN_FILE to compile with MinGW
+
+2012-05-10 14:58:26 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * Don't set -pedantic-errors in CMAKE_C_FLAGS, it breaks check_symbol_exists()
+
+2012-05-10 12:13:03 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoproj: replace mmap() with more portable file stream operations
+
+2012-05-10 12:11:33 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoproj: fix a typo
+
+2012-05-10 12:10:45 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoproj: issue a warning when passing "-f" more than once
+
+2012-05-10 09:34:01 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: don't look for the math library when compiling for Windows
+
+2012-05-10 09:01:40 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: MinGW does not have endian.h, provide fallbacks
+
+2012-05-10 08:16:04 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: fix setting the USB configuration
+
+2012-05-10 08:11:14 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * am7xxx: use hex notation for USB endpoints
+
+2012-05-10 07:57:01 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoproj: use MAP_PRIVATE in the mmap call
+
+2012-05-10 07:49:46 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * HACKING.asciidoc: fix cmake invocation examples
+
+2012-05-10 15:57:19 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * Merge remote-tracking branch 'origin/rettichschnidi' into mingw-port
+
+2012-05-06 23:24:40 +0200 Antonio Ospite <ospite@studenti.unina.it>
+
+       * picoproj: add a note about image dimensions and native resolution
+
+2012-04-08 14:24:43 +0200 Reto Schneider <github@reto-schneider.ch>
+
+       * Fix usage of FIND_PATH, allow $FFMPEG_DIR to be used.
+
+2012-04-07 12:39:43 +0200 Reto Schneider <github@reto-schneider.ch>
+
+       * Stop CMake if function avformat_open_input not available, print an error message. Prevents compile errors later on.
+
+2012-04-07 08:39:33 +0200 Reto Schneider <github@reto-schneider.ch>
+
+       * Make example programs optional.
+
 2012-03-28 13:37:00 +0200 Antonio Ospite <ospite@studenti.unina.it>
 
-       * Release version 0.1.2 (HEAD, master)
+       * Release version 0.1.2 (v0.1.2)
 
 2012-03-28 13:08:23 +0200 Antonio Ospite <ospite@studenti.unina.it>
 
@@ -8,7 +276,7 @@
 
 2012-03-28 10:55:14 +0200 Antonio Ospite <ospite@studenti.unina.it>
 
-       * Release version 0.1.1 (v0.1.1, origin/master)
+       * Release version 0.1.1 (v0.1.1)
 
 2012-03-28 10:43:04 +0200 Antonio Ospite <ospite@studenti.unina.it>
 
 
 2012-03-26 13:27:43 +0200 Antonio Ospite <ospite@studenti.unina.it>
 
-       * TODO: remove the entry about documenting the API with Doxygen (origin/unstable, unstable)
+       * TODO: remove the entry about documenting the API with Doxygen (origin/unstable)
 
 2012-03-24 00:25:57 +0100 Antonio Ospite <ospite@studenti.unina.it>
 
 
 2012-03-20 23:39:05 +0100 Reto Schneider <github@reto-schneider.ch>
 
-       * Fix typo: dimesions -> dimensions (origin/rettichschnidi)
+       * Fix typo: dimesions -> dimensions
 
 2012-03-20 23:36:20 +0100 Reto Schneider <github@reto-schneider.ch>
 
index 99dc15e..3c1bc74 100644 (file)
@@ -5,13 +5,38 @@
 libam7xxx uses the linux kernel coding style:
 http://kernel.org/doc/Documentation/CodingStyle
 
+=== Getting and compiling libam7xxx
+
+libam7xxx depends on 'libusb-1.0' and optionally on 'libav' or 'ffmpeg' for
+its example programs, the build system used is 'cmake'.
+
+On a Debian based system, the dependencies can be installed with this command:
+
+  $ sudo aptitude install cmake \
+                          libusb-1.0-0-dev \
+                          libavformat-dev \
+                          libavcodec-dev \
+                          libavdevice-dev \
+                          libswscale-dev
+
+The library and the example programs can be compiled following these steps:
+
+  $ git clone git://git.ao2.it/libam7xxx.git
+  $ cd libam7xxx
+  $ mkdir build
+  $ cd build
+  $ cmake ../
+  $ make
+
+After that the example programs can be found in the +bin/+ subdirectory.
+
 === Debug builds
 
 The suggested way to hack on the project is:
 
   $ mkdir build
   $ cd build
-  $ cmake ../ -DCMAKE_BUILD_TYPE=debug -DSTRICT_COMPILATION_CHECKS=ON
+  $ cmake -D CMAKE_BUILD_TYPE=debug -D STRICT_COMPILATION_CHECKS=ON ../
   $ make
 
 If you want to check the code with the ''sparse'' static analysis tool you
@@ -19,9 +44,33 @@ can run:
 
   $ mkdir build
   $ cd build
-  $ cmake ../ -DCMAKE_C_COMPILER=cgcc
+  $ cmake -D CMAKE_C_COMPILER=cgcc ../
+  $ make
+
+=== Cross Builds
+
+If you want to build for MS Windows:
+
+  $ sudo aptitude install mingw-w64
+  $ mkdir build
+  $ cd build
+  $ wget -nv http://sourceforge.net/projects/libusbx/files/releases/1.0.11/Windows/libusbx-1.0.11-win.7z
+  $ 7z -olibusbx-1.0.11-win x libusbx-1.0.11-win.7z
+  $ wget -nv http://win32.libav.org/win32/libav-win32-20120521.7z
+  $ 7z x libav-win32-20120521.7z
+  $ cmake  \
+          -D GNU_HOST=i686-w64-mingw32 \
+          -D CMAKE_TOOLCHAIN_FILE=../cmake_modules/mingw_cross_toolchain.cmake \
+          -D CMAKE_INSTALL_PREFIX=libam7xxx-win/ \
+          -D LIBUSB_1_INCLUDE_DIR=libusbx-1.0.11-win/include/libusbx-1.0 \
+          -D LIBUSB_1_LIBRARY=libusbx-1.0.11-win/MinGW32/dll/libusb-1.0.dll \
+          -D FFMPEG_ROOT=$(pwd)/libav-win32-20120521/usr \
+          ../
   $ make
 
+After that you will find libam7xxx.dll in lib/ and picoproj.exe in the bin/
+directory.
+
 === Valgrind
 
 You can run the test program under the http://valgrind.org/[valgrind]
index 7ccabf6..7ad02de 100644 (file)
@@ -44,6 +44,10 @@ 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'
+example program from libam7xxx.
+
 Examples of devices based on AM7XXX are:
 
   - Acer Series C pico projectors (C20, C110, C112):
@@ -51,13 +55,19 @@ 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):
+    1630, PPX 2055):
       * 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/
 
+  - Royaltek PJU-2100:
+      * http://www.royaltek.com/index.php/pju-2100-pico-projector
+  
+  - Aiptek PocketCinema T25:
+      * http://www.aiptek.eu/index.php/en/products/pico-projectors/pocketcinema-t25
+
   - Other unbranded projectors:
       * http://www.dealextreme.com/p/portable-home-office-mini-usb-2-0-lcos-projector-16-9-45019
 
@@ -69,3 +79,36 @@ Examples of devices based on AM7XXX are:
 Maybe other devices reported as supporting "Display over USB (DoUSB)" like
 Acer K330 or some Optoma projectors could be used with this library, but
 this needs still needs to be verified.
+
+== Testing libam7xxx on MS Windows
+
+All the needed files need to be in the same location:
+  
+  - 'libusb-1.0.dll' from http://sourceforge.net/projects/libusbx/files/releases/1.0.11/Windows/libusbx-1.0.11-win.7z
+
+  - 'libssp-0.dll' from MinGW;
+
+  - 'am7xxx_mode_switch.exe', 'libam7xxx.dll' and 'picoproj.exe' which can all
+    be built by following the instructions in the HACKING.asciidoc document
+    from libam7xxx.
+
+  - 'am7xxx-play.exe' and the '.dll' files from the 'usr/bin/' dir in libav-win32:
+    http://win32.libav.org/win32/libav-win32-20120521.7z
+
+In order to use the device on MS Windows the WinUSB drivers must be installed
+for both the mass storage device and the display device:
+
+  - Download http://sourceforge.net/projects/libwdi/files/zadig/[Zadig], it is
+    a tool to install and replace USB devices filter drivers on MS Windows.
+
+  - From Zadig, select the USB Mass Storage Device relative to the projector
+    and replace the +USBSTOR+ driver with the +WinUSB+ one; keep in mind that
+    from now on the virtual CD-ROM can't be accessed anymore until the
+    +USBSTOR+ Driver is restored.
+
+  - Run 'am7xxx_mode_switch.exe'
+
+  - When the new (display) device shows up, run Zadig and install the +WinUSB+
+    driver for it too.
+
+Now it is possible to run 'picoproj.exe' or 'am7xxx-play.exe' on Windows.
diff --git a/TODO b/TODO
index 63245b0..3db48f2 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1 +1,3 @@
-- Generate language bindings in order to use libam7xxx from other languages.
+- 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).
index 1dc76d5..c88d694 100644 (file)
@@ -27,11 +27,12 @@ MACRO(FFMPEG_FIND varname shortname headername)
     # old version of ffmpeg put header in $prefix/include/[ffmpeg]
     # so try to find header in include directory
     FIND_PATH(FFMPEG_${varname}_INCLUDE_DIRS ${headername}
-        PATHS
+        HINTS
         ${FFMPEG_ROOT}/include
         $ENV{FFMPEG_DIR}/include
         $ENV{OSGDIR}/include
         $ENV{OSG_ROOT}/include
+        PATHS
         ~/Library/Frameworks
         /Library/Frameworks
         /usr/local/include
@@ -49,10 +50,12 @@ MACRO(FFMPEG_FIND varname shortname headername)
     # so try to find lib${shortname}/header in include directory
     IF(NOT FFMPEG_${varname}_INCLUDE_DIRS)
         FIND_PATH(FFMPEG_${varname}_INCLUDE_DIRS lib${shortname}/${headername}
+            HINTS
             ${FFMPEG_ROOT}/include
             $ENV{FFMPEG_DIR}/include
             $ENV{OSGDIR}/include
             $ENV{OSG_ROOT}/include
+            PATHS
             ~/Library/Frameworks
             /Library/Frameworks
             /usr/local/include
@@ -69,11 +72,12 @@ MACRO(FFMPEG_FIND varname shortname headername)
 
     FIND_LIBRARY(FFMPEG_${varname}_LIBRARIES
         NAMES ${shortname}
-        PATHS
+        HINTS
         ${FFMPEG_ROOT}/lib
         $ENV{FFMPEG_DIR}/lib
         $ENV{OSGDIR}/lib
         $ENV{OSG_ROOT}/lib
+        PATHS
         ~/Library/Frameworks
         /Library/Frameworks
         /usr/local/lib
diff --git a/cmake_modules/mingw_cross_toolchain.cmake b/cmake_modules/mingw_cross_toolchain.cmake
new file mode 100644 (file)
index 0000000..e3ae052
--- /dev/null
@@ -0,0 +1,14 @@
+SET(CMAKE_SYSTEM_NAME Windows)
+include(CMakeForceCompiler)
+IF("${GNU_HOST}" STREQUAL "")
+    SET(GNU_HOST i586-mingw32msvc)
+ENDIF()
+# Prefix detection only works with compiler id "GNU"
+CMAKE_FORCE_C_COMPILER(${GNU_HOST}-gcc GNU)
+# CMake doesn't automatically look for prefixed 'windres', do it manually:
+SET(CMAKE_RC_COMPILER ${GNU_HOST}-windres)
+
+# The following is important to let cmake set some variables such as
+# CMAKE_FIND_LIBRARY_PREFIXES and CMAKE_FIND_LIBRARY_SUFFIXES from
+# Platform/Windows-GNU.cmake
+SET(MINGW True)
index 1b7d266..53643ac 100644 (file)
@@ -1,4 +1,10 @@
 # Acer C110
 ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="1de1", ATTRS{idProduct}=="c101", MODE="0660", GROUP="plugdev"
+# Acer C112
+ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="1de1", ATTRS{idProduct}=="5501", MODE="0660", GROUP="plugdev"
+# Aiptek PocketCinema T25
+ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="08ca", ATTRS{idProduct}=="2144", MODE="0660", GROUP="plugdev"
 # Philips/Sagemcom PicoPix 1020
 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"
diff --git a/contrib/99-am7xxx-autodisplay.rules b/contrib/99-am7xxx-autodisplay.rules
new file mode 100644 (file)
index 0000000..fc58472
--- /dev/null
@@ -0,0 +1,3 @@
+# Example rules to show how to run a program when the device is plugged in or out
+ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="1de1", ATTRS{idProduct}=="c101", MODE="0660", GROUP="plugdev", RUN+="am7xxx-autodisplay.sh start"
+ACTION=="remove", SUBSYSTEM=="usb", ENV{ID_VENDOR_ID}=="1de1", ENV{ID_MODEL_ID}=="c101", RUN+="am7xxx-autodisplay.sh stop"
diff --git a/contrib/am7xxx-autodisplay.sh b/contrib/am7xxx-autodisplay.sh
new file mode 100755 (executable)
index 0000000..9d5199d
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# am7xxx-autodisplay - resize the screen and run am7xxx-play
+#
+# Copyright (C) 2012  Antonio Ospite <ospite@studenti.unina.it>
+#
+# This program is free software. It comes without any warranty, to
+# the extent permitted by applicable law. You can redistribute it
+# and/or modify it under the terms of the Do What The Fuck You Want
+# To Public License, Version 2, as published by Sam Hocevar. See
+# http://sam.zoy.org/wtfpl/COPYING for more details.
+
+# This is just an example script to show how to resize the screen before
+# running am7xxx-play, this can be called also from a udev script.
+#
+# Resizing the screen may be needed if the am7xxx device has problems
+# displaying a certain resolution.
+#
+# For example on some devices the firmware fails to display images of
+# resolution 800x469 resulting from scaled down 1024x600 screens, in
+# cases like this, resizing the screen can be a viable workaround.
+
+USER=ao2
+
+RESOLUTION_PROJECTOR=800x600
+RESOLUTION_ORIGINAL=1024x600
+
+AM7XXX_PLAY=am7xxx-play
+
+# needed when running xrandr as root from udev rules,
+# see https://bugs.launchpad.net/ubuntu/+source/xserver-xorg-video-intel/+bug/660901
+export XAUTHORITY=$(find /var/run/gdm3/ -type f -path "*${USER}*" 2> /dev/null)
+
+export DISPLAY=:0.0
+
+case $1 in
+  start)
+    xrandr --size $RESOLUTION_PROJECTOR && \
+      $AM7XXX_PLAY -f x11grab -i $DISPLAY
+    ;;
+
+  stop)
+    xrandr --size $RESOLUTION_ORIGINAL
+    ;;
+
+  *)
+    { echo "usage: $(basename $0) <start|stop>" 1>&2; exit 1; }
+    ;;
+esac
diff --git a/contrib/am7xxx-play-window.sh b/contrib/am7xxx-play-window.sh
new file mode 100755 (executable)
index 0000000..729a72e
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# am7xxx-play-window - show only a given window with am7xxx-play
+#
+# Copyright (C) 2013  Antonio Ospite <ospite@studenti.unina.it>
+#
+# This program is free software. It comes without any warranty, to
+# the extent permitted by applicable law. You can redistribute it
+# and/or modify it under the terms of the Do What The Fuck You Want
+# To Public License, Version 2, as published by Sam Hocevar. See
+# http://sam.zoy.org/wtfpl/COPYING for more details.
+
+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}"
diff --git a/contrib/libam7xxx_test_image_800x480.svg b/contrib/libam7xxx_test_image_800x480.svg
new file mode 100644 (file)
index 0000000..4591d08
--- /dev/null
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="800"
+   height="480"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.3.1 r9886"
+   sodipodi:docname="libam7xxx_test_image_800x480.svg"
+   inkscape:export-filename="/home/ao2/libam7xxx_test_image_800x480.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.98994949"
+     inkscape:cx="350.20761"
+     inkscape:cy="242.81028"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1152"
+     inkscape:window-height="756"
+     inkscape:window-x="0"
+     inkscape:window-y="29"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Background"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-572.36218)"
+     style="display:inline"
+     sodipodi:insensitive="true">
+    <rect
+       style="color:#000000;fill:#000400;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect2982"
+       width="800"
+       height="480"
+       x="0"
+       y="572.36218" />
+    <text
+       xml:space="preserve"
+       style="font-size:24px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0.09px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans Bold"
+       x="274.20743"
+       y="663.45349"
+       id="text3782"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3784"
+         x="274.20743"
+         y="663.45349">libam7xxx test screen</tspan></text>
+    <path
+       sodipodi:type="arc"
+       style="color:#000000;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:5;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="path3778"
+       sodipodi:cx="452.54834"
+       sodipodi:cy="296.15222"
+       sodipodi:rx="88.893425"
+       sodipodi:ry="76.771591"
+       d="m 541.44176,296.15222 a 88.893425,76.771591 0 1 1 -177.78685,0 88.893425,76.771591 0 1 1 177.78685,0 z"
+       transform="matrix(1.091899,0,0,1.2643041,-94.13708,437.93571)" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Original Size"
+     sodipodi:insensitive="true">
+    <rect
+       style="color:#000000;fill:none;stroke:#00ffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect3752"
+       width="799"
+       height="479"
+       x="0.5"
+       y="0.5" />
+    <text
+       xml:space="preserve"
+       style="font-size:24px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0.09px;word-spacing:0px;fill:#00ffff;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans Bold"
+       x="124.24876"
+       y="390.09644"
+       id="text3756"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3758"
+         x="124.24876"
+         y="390.09644">Original Size: 800x480 (aspect ratio 5:3)</tspan></text>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="Zoom 1: H Scale"
+     sodipodi:insensitive="true">
+    <rect
+       style="color:#000000;fill:none;stroke:#ffff00;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect3762"
+       width="589"
+       height="479"
+       x="105.5"
+       y="0.5"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <text
+       xml:space="preserve"
+       style="font-size:24px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0.09px;word-spacing:0px;fill:#ffff00;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans Bold"
+       x="124.5183"
+       y="419.9223"
+       id="text3768"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3770"
+         x="124.5183"
+         y="419.9223">Zoom 1 (H scale): 590x480</tspan></text>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer4"
+     inkscape:label="Zoom 2: H/V Scale"
+     style="display:inline"
+     sodipodi:insensitive="true">
+    <rect
+       style="color:#000000;fill:none;stroke:#ff00ff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       id="rect3772"
+       width="799"
+       height="459"
+       x="0.5"
+       y="10.5" />
+    <text
+       xml:space="preserve"
+       style="font-size:24px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0.09px;word-spacing:0px;fill:#ff00ff;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans Bold"
+       x="124.5183"
+       y="449.69543"
+       id="text3774"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3776"
+         x="124.5183"
+         y="449.69543">Zoom 2 (H/V Scale): 800x460</tspan></text>
+  </g>
+</svg>
index 4e6a877..fface99 100644 (file)
@@ -1,4 +1,4 @@
-# Doxyfile 1.7.6.1
+# Doxyfile 1.8.1.2
 
 #---------------------------------------------------------------------------
 # Project related configuration options
@@ -33,6 +33,7 @@ OPTIMIZE_OUTPUT_JAVA   = NO
 OPTIMIZE_FOR_FORTRAN   = NO
 OPTIMIZE_OUTPUT_VHDL   = NO
 EXTENSION_MAPPING      =
+MARKDOWN_SUPPORT       = YES
 BUILTIN_STL_SUPPORT    = NO
 CPP_CLI_SUPPORT        = NO
 SIP_SUPPORT            = NO
@@ -49,6 +50,7 @@ LOOKUP_CACHE_SIZE      = 0
 #---------------------------------------------------------------------------
 EXTRACT_ALL            = NO
 EXTRACT_PRIVATE        = NO
+EXTRACT_PACKAGE        = NO
 EXTRACT_STATIC         = NO
 EXTRACT_LOCAL_CLASSES  = NO
 EXTRACT_LOCAL_METHODS  = NO
@@ -76,7 +78,6 @@ GENERATE_DEPRECATEDLIST= YES
 ENABLED_SECTIONS       =
 MAX_INITIALIZER_LINES  = 30
 SHOW_USED_FILES        = YES
-SHOW_DIRECTORIES       = NO
 SHOW_FILES             = YES
 SHOW_NAMESPACES        = YES
 FILE_VERSION_FILTER    =
@@ -146,8 +147,8 @@ HTML_COLORSTYLE_HUE    = 220
 HTML_COLORSTYLE_SAT    = 100
 HTML_COLORSTYLE_GAMMA  = 80
 HTML_TIMESTAMP         = YES
-HTML_ALIGN_MEMBERS     = YES
 HTML_DYNAMIC_SECTIONS  = NO
+HTML_INDEX_NUM_ENTRIES = 100
 GENERATE_DOCSET        = NO
 DOCSET_FEEDNAME        = "Doxygen generated docs"
 DOCSET_BUNDLE_ID       = org.doxygen.Project
@@ -173,7 +174,6 @@ ECLIPSE_DOC_ID         = org.doxygen.Project
 DISABLE_INDEX          = NO
 GENERATE_TREEVIEW      = NO
 ENUM_VALUES_PER_LINE   = 4
-USE_INLINE_TREES       = NO
 TREEVIEW_WIDTH         = 250
 EXT_LINKS_IN_WINDOW    = NO
 FORMULA_FONTSIZE       = 10
@@ -271,6 +271,7 @@ CLASS_GRAPH            = YES
 COLLABORATION_GRAPH    = YES
 GROUP_GRAPHS           = YES
 UML_LOOK               = NO
+UML_LIMIT_NUM_FIELDS   = 10
 TEMPLATE_RELATIONS     = NO
 INCLUDE_GRAPH          = YES
 INCLUDED_BY_GRAPH      = YES
index 2453dc2..1754ea9 100644 (file)
@@ -21,7 +21,10 @@ Check @link am7xxx.h @endlink for the public API documentation.
 @section libam7xxxSupportedDevices Supported Devices
 
 - Acer C110
+- Acer C112
+- Aiptek PocketCinema T25
 - Philips/SagemCom PicoPix 1020
+- Philips/SagemCom PicoPix 2055
 
 @section libam7xxxDesignOverview Design Overview
 
index 5054da4..eabdb9c 100644 (file)
@@ -21,6 +21,10 @@ decode the input, encode it to jpeg and display it with libam7xxx.
 
 OPTIONS
 -------
+
+*-d* '<index>'::
+    the device index (default is 0)
+
 *-f* '<input format>'::
     the input device format
 
@@ -53,11 +57,14 @@ EXAMPLE:
 *-l* '<log level>'::
     the verbosity level of libam7xxx output (0-5)
 
-*-p* '<power level>'::
-    power level of device, between 0 (off) and 4 (maximum) +
+*-p* '<power mode>'::
+    the power mode of device, between 0 (off) and 4 (turbo) +
     WARNING: Level 2 and greater require the master AND
              the slave connector to be plugged in.
 
+*-z* '<zoom mode>'::
+    the display zoom mode, between 0 (original) and 3 (test)
+
 *-h*::
     this help message
 
index c60fe16..0fea153 100644 (file)
@@ -15,12 +15,17 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-picoproj(1) is a minimal example to show how to use libam7xxx to display a static image.
+picoproj(1) is a minimal example to show how to use libam7xxx to display
+a static image; it will not perform any image rescaling or conversion, images
+larger than the device native resolution can be wrongly displayed.
 
 
 OPTIONS
 -------
 
+*-d* '<index>'::
+    the device index (default is 0)
+
 *-f* '<filename>'::
     the image file to upload
 
@@ -35,11 +40,14 @@ OPTIONS
 *-l* '<log level>'::
     the verbosity level of libam7xxx output (0-5)
 
-*-p* '<power level>'::
-    power level of device, between 0 (off) and 4 (maximum) +
+*-p* '<power mode>'::
+    the power mode of device, between 0 (off) and 4 (turbo) +
     WARNING: Level 2 and greater require the master AND
              the slave connector to be plugged in.
 
+*-z* '<zoom mode>'::
+    the display zoom mode, between 0 (original) and 3 (test)
+
 *-W* '<image width>'::
     the width of the image to upload
 
index 234e666..64f15c0 100644 (file)
@@ -1,3 +1,4 @@
+include(CheckSymbolExists)
 add_definitions("-D_POSIX_C_SOURCE=2") # for getopt()
 add_definitions("-D_POSIX_SOURCE") # for sigaction
 add_definitions("-D_BSD_SOURCE") # for strdup
@@ -5,31 +6,72 @@ add_definitions("-D_BSD_SOURCE") # for strdup
 include_directories(${CMAKE_SOURCE_DIR}/src/)
 
 # Build a test app that sends a single picture
-add_executable(picoproj picoproj.c)
-target_link_libraries(picoproj am7xxx)
-install(TARGETS picoproj
-  DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
+option(BUILD_PICOPROJ "Build a test app that sends a single picture" TRUE)
+if(BUILD_PICOPROJ)
+  add_executable(picoproj picoproj.c)
+  target_link_libraries(picoproj am7xxx)
+  install(TARGETS picoproj
+    DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
+endif()
 
 # Build a more complete example
-find_package(FFmpeg REQUIRED)
-
-include_directories(${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS})
-include_directories(${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS})
-include_directories(${FFMPEG_LIBSWSCALE_INCLUDE_DIRS})
-
-# xcb is used to retrieve the full screen dimensions when using x11grab
-# as input format
-find_package(XCB)
-if (XCB_FOUND)
-  add_definitions("${LIBXCB_DEFINITIONS} -DHAVE_XCB")
-  include_directories(${LIBXCB_INCLUDE_DIRS})
+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}) 
+  check_symbol_exists(avformat_open_input
+    "${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS}/libavformat/avformat.h"
+    HAVE_AVFORMAT_OPEN_INPUT)
+  if(NOT HAVE_AVFORMAT_OPEN_INPUT)
+    message(FATAL_ERROR
+      "Function avformat_open_input missing. Please use a newer FFmpeg release.")
+  endif()
+
+  include_directories(${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS})
+  include_directories(${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS})
+  include_directories(${FFMPEG_LIBSWSCALE_INCLUDE_DIRS})
+
+  set(CMAKE_REQUIRED_DEFINITIONS -D_POSIX_SOURCE)
+  check_symbol_exists(sigaction "signal.h" HAVE_SIGACTION)
+  if (HAVE_SIGACTION)
+    add_definitions("-DHAVE_SIGACTION")
+  endif()
+
+  check_symbol_exists(strtok_r "string.h" HAVE_STRTOK_R)
+  if (HAVE_STRTOK_R)
+    add_definitions("-DHAVE_STRTOK_R")
+  endif()
+  set(CMAKE_REQUIRED_DEFINITIONS)
+
+  # xcb is used to retrieve the full screen dimensions when using x11grab
+  # as input format
+  find_package(XCB)
+  if (XCB_FOUND)
+    add_definitions("${LIBXCB_DEFINITIONS} -DHAVE_XCB")
+    include_directories(${LIBXCB_INCLUDE_DIRS})
+  endif()
+
+  add_executable(am7xxx-play am7xxx-play.c)
+
+  target_link_libraries(am7xxx-play am7xxx
+    ${FFMPEG_LIBRARIES}
+    ${FFMPEG_LIBSWSCALE_LIBRARIES}
+    ${LIBXCB_LIBRARIES})
+  install(TARGETS am7xxx-play
+    DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
 endif()
 
-add_executable(am7xxx-play am7xxx-play.c)
+# 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)
+
+  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
+    DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
+endif()
 
-target_link_libraries(am7xxx-play am7xxx
-  ${FFMPEG_LIBRARIES}
-  ${FFMPEG_LIBSWSCALE_LIBRARIES}
-  ${LIBXCB_LIBRARIES})
-install(TARGETS am7xxx-play
-  DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
index 2fc8763..38812a4 100644 (file)
 
 #include <am7xxx.h>
 
+/* On some systems ENOTSUP is not defined, fallback to its value on
+ * linux which is equal to EOPNOTSUPP which is 95
+ */
+#ifndef ENOTSUP
+#define ENOTSUP 95
+#endif
+
 static unsigned int run = 1;
 
 struct video_input_ctx {
@@ -199,8 +206,10 @@ static int video_output_init(struct video_output_ctx *output_ctx,
        output_codec_ctx->bit_rate   = (input_ctx->codec_ctx)->bit_rate;
        output_codec_ctx->width      = new_output_width;
        output_codec_ctx->height     = new_output_height;
-       output_codec_ctx->time_base.num  = (input_ctx->codec_ctx)->time_base.num;
-       output_codec_ctx->time_base.den  = (input_ctx->codec_ctx)->time_base.den;
+       output_codec_ctx->time_base.num  =
+               (input_ctx->format_ctx)->streams[input_ctx->video_stream_index]->time_base.num;
+       output_codec_ctx->time_base.den  =
+               (input_ctx->format_ctx)->streams[input_ctx->video_stream_index]->time_base.den;
 
        /* When the raw format is requested we don't actually need to setup
         * and open a decoder
@@ -276,9 +285,12 @@ static int am7xxx_play(const char *input_format_string,
        int out_buf_size;
        uint8_t *out_buf;
        int out_picture_size;
+       uint8_t *out_picture;
        struct SwsContext *sw_scale_ctx;
-       AVPacket packet;
+       AVPacket in_packet;
+       AVPacket out_packet;
        int got_picture;
+       int got_packet;
        int ret = 0;
 
        ret = video_input_init(&input_ctx, input_format_string, input_path, input_options);
@@ -344,7 +356,7 @@ static int am7xxx_play(const char *input_format_string,
 
        while (run) {
                /* read packet */
-               ret = av_read_frame(input_ctx.format_ctx, &packet);
+               ret = av_read_frame(input_ctx.format_ctx, &in_packet);
                if (ret < 0) {
                        if (ret == (int)AVERROR_EOF || input_ctx.format_ctx->pb->eof_reached)
                                ret = 0;
@@ -354,7 +366,7 @@ static int am7xxx_play(const char *input_format_string,
                        goto end_while;
                }
 
-               if (packet.stream_index != input_ctx.video_stream_index) {
+               if (in_packet.stream_index != input_ctx.video_stream_index) {
                        /* that is more or less a "continue", but there is
                         * still the packet to free */
                        goto end_while;
@@ -362,7 +374,7 @@ static int am7xxx_play(const char *input_format_string,
 
                /* decode */
                got_picture = 0;
-               ret = avcodec_decode_video2(input_ctx.codec_ctx, picture_raw, &got_picture, &packet);
+               ret = avcodec_decode_video2(input_ctx.codec_ctx, picture_raw, &got_picture, &in_packet);
                if (ret < 0) {
                        fprintf(stderr, "cannot decode video\n");
                        run = 0;
@@ -381,20 +393,26 @@ static int am7xxx_play(const char *input_format_string,
                                  picture_scaled->linesize);
 
                        if (output_ctx.raw_output) {
+                               out_picture = out_buf;
                                out_picture_size = out_buf_size;
                        } else {
                                picture_scaled->quality = (output_ctx.codec_ctx)->global_quality;
-                               /* TODO: switch to avcodec_encode_video2() eventually */
-                               out_picture_size = avcodec_encode_video(output_ctx.codec_ctx,
-                                                                       out_buf,
-                                                                       out_buf_size,
-                                                                       picture_scaled);
-                               if (out_picture_size < 0) {
+                               av_init_packet(&out_packet);
+                               out_packet.data = NULL;
+                               out_packet.size = 0;
+                               got_packet = 0;
+                               ret = avcodec_encode_video2(output_ctx.codec_ctx,
+                                                           &out_packet,
+                                                           picture_scaled,
+                                                           &got_packet);
+                               if (ret < 0 || !got_packet) {
                                        fprintf(stderr, "cannot encode video\n");
-                                       ret = out_picture_size;
                                        run = 0;
                                        goto end_while;
                                }
+
+                               out_picture = out_packet.data;
+                               out_picture_size = out_packet.size;
                        }
 
 #ifdef DEBUG
@@ -405,7 +423,7 @@ static int am7xxx_play(const char *input_format_string,
                        else
                                snprintf(filename, NAME_MAX, "out.raw");
                        file = fopen(filename, "wb");
-                       fwrite(out_buf, 1, out_picture_size, file);
+                       fwrite(out_picture, 1, out_picture_size, file);
                        fclose(file);
 #endif
 
@@ -413,7 +431,7 @@ static int am7xxx_play(const char *input_format_string,
                                                image_format,
                                                (output_ctx.codec_ctx)->width,
                                                (output_ctx.codec_ctx)->height,
-                                               out_buf,
+                                               out_picture,
                                                out_picture_size);
                        if (ret < 0) {
                                perror("am7xxx_send_image");
@@ -422,7 +440,9 @@ static int am7xxx_play(const char *input_format_string,
                        }
                }
 end_while:
-               av_free_packet(&packet);
+               if (!output_ctx.raw_output)
+                       av_free_packet(&out_packet);
+               av_free_packet(&in_packet);
        }
 
        sws_freeContext(sw_scale_ctx);
@@ -530,6 +550,7 @@ static void unset_run(int signo)
        run = 0;
 }
 
+#ifdef HAVE_SIGACTION
 static int set_signal_handler(void (*signal_handler)(int))
 {
        struct sigaction new_action;
@@ -557,11 +578,21 @@ static int set_signal_handler(void (*signal_handler)(int))
 out:
        return ret;
 }
+#else
+static int set_signal_handler(void (*signal_handler)(int))
+{
+       (void)signal_handler;
+       fprintf(stderr, "set_signal_handler() not implemented, sigaction not available\n");
+       return 0;
+}
+#endif
+
 
 static void usage(char *name)
 {
        printf("usage: %s [OPTIONS]\n\n", name);
        printf("OPTIONS:\n");
+       printf("\t-d <index>\t\tthe device index (default is 0)\n");
        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");
@@ -575,8 +606,12 @@ static void usage(char *name)
        printf("\t\t\t\t\t2 - NV12\n");
        printf("\t-q <quality>\t\tquality of jpeg sent to the device, between 1 and 100\n");
        printf("\t-l <log level>\t\tthe verbosity level of libam7xxx output (0-5)\n");
-       printf("\t-p <power level>\tpower level of device, between %x (off) and %x (maximum)\n", AM7XXX_POWER_OFF, AM7XXX_POWER_TURBO);
-       printf("\t\t\t\tWARNING: Level 2 and greater require the master AND\n\t\t\t\t\t the slave connector to be plugged in.\n");
+       printf("\t-p <power mode>\t\tthe power mode of device, between %d (off) and %d (turbo)\n",
+              AM7XXX_POWER_OFF, AM7XXX_POWER_TURBO);
+       printf("\t\t\t\tWARNING: Level 2 and greater require the master AND\n");
+       printf("\t\t\t\t         the slave connector to be plugged in.\n");
+       printf("\t-z <zoom mode>\t\tthe display zoom mode, between %d (original) and %d (test)\n",
+              AM7XXX_ZOOM_ORIGINAL, AM7XXX_ZOOM_TEST);
        printf("\t-h \t\t\tthis help message\n");
        printf("\n\nEXAMPLES OF USE:\n");
        printf("\t%s -f x11grab -i :0.0 -o video_size=800x480\n", name);
@@ -599,13 +634,23 @@ int main(int argc, char *argv[])
        unsigned int upscale = 0;
        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 format = AM7XXX_IMAGE_FORMAT_JPEG;
        am7xxx_context *ctx;
        am7xxx_device *dev;
 
-       while ((opt = getopt(argc, argv, "f:i:o:s:uF:q:l:p:h")) != -1) {
+       while ((opt = getopt(argc, argv, "d:f:i:o:s:uF:q:l:p:z:h")) != -1) {
                switch (opt) {
+               case 'd':
+                       device_index = atoi(optarg);
+                       if (device_index < 0) {
+                               fprintf(stderr, "Unsupported device index\n");
+                               ret = -EINVAL;
+                               goto out;
+                       }
+                       break;
                case 'f':
                        input_format_string = strdup(optarg);
                        break;
@@ -613,6 +658,7 @@ int main(int argc, char *argv[])
                        input_path = strdup(optarg);
                        break;
                case 'o':
+#ifdef HAVE_STRTOK_R
                        /*
                         * parse suboptions, the expected format is something
                         * like:
@@ -629,6 +675,9 @@ int main(int argc, char *argv[])
                                av_dict_set(&options, subopt_name, subopt_value, 0);
                        }
                        free(subopts_saved);
+#else
+                       fprintf(stderr, "Option '-o' not implemented\n");
+#endif
                        break;
                case 's':
                        rescale_method = atoi(optarg);
@@ -692,19 +741,34 @@ int main(int argc, char *argv[])
                        case AM7XXX_POWER_MIDDLE:
                        case AM7XXX_POWER_HIGH:
                        case AM7XXX_POWER_TURBO:
-                               fprintf(stdout, "Power mode: %x\n", power_mode);
+                               fprintf(stdout, "Power mode: %d\n", power_mode);
                                break;
                        default:
-                               fprintf(stderr, "Invalid power mode value, must be between %x and %x\n", AM7XXX_POWER_OFF, AM7XXX_POWER_TURBO);
+                               fprintf(stderr, "Invalid power mode value, must be between %d and %d\n",
+                                       AM7XXX_POWER_OFF, AM7XXX_POWER_TURBO);
                                ret = -EINVAL;
                                goto out;
                        }
                        break;
+               case 'z':
+                       zoom = atoi(optarg);
+                       switch(zoom) {
+                       case AM7XXX_ZOOM_ORIGINAL:
+                       case AM7XXX_ZOOM_H:
+                       case AM7XXX_ZOOM_H_V:
+                       case AM7XXX_ZOOM_TEST:
+                               fprintf(stdout, "Zoom: %d\n", zoom);
+                               break;
+                       default:
+                               fprintf(stderr, "Invalid zoom mode value, must be between %d and %d\n",
+                                       AM7XXX_ZOOM_ORIGINAL, AM7XXX_ZOOM_TEST);
+                               exit(EXIT_FAILURE);
+                       }
+                       break;
                case 'h':
                        usage(argv[0]);
                        ret = 0;
                        goto out;
-                       break;
                default: /* '?' */
                        usage(argv[0]);
                        ret = -EINVAL;
@@ -753,18 +817,28 @@ int main(int argc, char *argv[])
 
        am7xxx_set_log_level(ctx, log_level);
 
-       ret = am7xxx_open_device(ctx, &dev, 0);
+       ret = am7xxx_open_device(ctx, &dev, device_index);
        if (ret < 0) {
                perror("am7xxx_open_device");
                goto cleanup;
        }
 
+       ret = am7xxx_set_zoom_mode(dev, zoom);
+       if (ret < 0) {
+               perror("am7xxx_set_zoom_mode");
+               goto cleanup;
+       }
+
        ret = am7xxx_set_power_mode(dev, power_mode);
        if (ret < 0) {
                perror("am7xxx_set_power_mode");
                goto cleanup;
        }
 
+       /* When setting AM7XXX_ZOOM_TEST don't display the actual image */
+       if (zoom == AM7XXX_ZOOM_TEST)
+               goto cleanup;
+
        ret = am7xxx_play(input_format_string,
                          &options,
                          input_path,
diff --git a/examples/am7xxx_mode_switch.c b/examples/am7xxx_mode_switch.c
new file mode 100644 (file)
index 0000000..7509981
--- /dev/null
@@ -0,0 +1,97 @@
+/* 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 105169c..045f386 100644 (file)
@@ -24,7 +24,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -36,14 +35,19 @@ static void usage(char *name)
 {
        printf("usage: %s [OPTIONS]\n\n", name);
        printf("OPTIONS:\n");
+       printf("\t-d <index>\t\tthe device index (default is 0)\n");
        printf("\t-f <filename>\t\tthe image file to upload\n");
        printf("\t-F <format>\t\tthe image format to use (default is JPEG)\n");
        printf("\t\t\t\tSUPPORTED FORMATS:\n");
        printf("\t\t\t\t\t1 - JPEG\n");
        printf("\t\t\t\t\t2 - NV12\n");
        printf("\t-l <log level>\t\tthe verbosity level of libam7xxx output (0-5)\n");
-       printf("\t-p <power level>\tpower level of device, between %x (off) and %x (maximum)\n", AM7XXX_POWER_OFF, AM7XXX_POWER_TURBO);
-       printf("\t\t\t\tWARNING: Level 2 and greater require the master AND\n\t\t\t\t\t the slave connector to be plugged in.\n");
+       printf("\t-p <power mode>\t\tthe power mode of device, between %d (off) and %d (turbo)\n",
+              AM7XXX_POWER_OFF, AM7XXX_POWER_TURBO);
+       printf("\t\t\t\tWARNING: Level 2 and greater require the master AND\n");
+       printf("\t\t\t\t         the slave connector to be plugged in.\n");
+       printf("\t-z <zoom mode>\t\tthe display zoom mode, between %d (original) and %d (test)\n",
+              AM7XXX_ZOOM_ORIGINAL, AM7XXX_ZOOM_TEST);
        printf("\t-W <image width>\tthe width of the image to upload\n");
        printf("\t-H <image height>\tthe height of the image to upload\n");
        printf("\t-h \t\t\tthis help message\n");
@@ -58,12 +62,14 @@ int main(int argc, char *argv[])
        int opt;
 
        char filename[FILENAME_MAX] = {0};
-       int image_fd;
+       FILE *image_fp;
        struct stat st;
        am7xxx_context *ctx;
        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 format = AM7XXX_IMAGE_FORMAT_JPEG;
        int width = 800;
        int height = 480;
@@ -71,9 +77,18 @@ int main(int argc, char *argv[])
        unsigned int size;
        am7xxx_device_info device_info;
 
-       while ((opt = getopt(argc, argv, "f:F:l:p:W:H:h")) != -1) {
+       while ((opt = getopt(argc, argv, "d:f:F:l:p:z:W:H:h")) != -1) {
                switch (opt) {
+               case 'd':
+                       device_index = atoi(optarg);
+                       if (device_index < 0) {
+                               fprintf(stderr, "Unsupported device index\n");
+                               exit(EXIT_FAILURE);
+                       }
+                       break;
                case 'f':
+                       if (filename[0] != '\0')
+                               fprintf(stderr, "Warning: image file already specified\n");
                        strncpy(filename, optarg, FILENAME_MAX);
                        break;
                case 'F':
@@ -105,10 +120,26 @@ int main(int argc, char *argv[])
                        case AM7XXX_POWER_MIDDLE:
                        case AM7XXX_POWER_HIGH:
                        case AM7XXX_POWER_TURBO:
-                               fprintf(stdout, "Power mode: %x\n", power_mode);
+                               fprintf(stdout, "Power mode: %d\n", power_mode);
                                break;
                        default:
-                               fprintf(stderr, "Invalid power mode value, must be between %x and %x\n", AM7XXX_POWER_OFF, AM7XXX_POWER_TURBO);
+                               fprintf(stderr, "Invalid power mode value, must be between %d and %d\n",
+                                       AM7XXX_POWER_OFF, AM7XXX_POWER_TURBO);
+                               exit(EXIT_FAILURE);
+                       }
+                       break;
+               case 'z':
+                       zoom = atoi(optarg);
+                       switch(zoom) {
+                       case AM7XXX_ZOOM_ORIGINAL:
+                       case AM7XXX_ZOOM_H:
+                       case AM7XXX_ZOOM_H_V:
+                       case AM7XXX_ZOOM_TEST:
+                               fprintf(stdout, "Zoom: %d\n", zoom);
+                               break;
+                       default:
+                               fprintf(stderr, "Invalid zoom mode value, must be between %d and %d\n",
+                                       AM7XXX_ZOOM_ORIGINAL, AM7XXX_ZOOM_TEST);
                                exit(EXIT_FAILURE);
                        }
                        break;
@@ -142,31 +173,43 @@ int main(int argc, char *argv[])
                goto out;
        }
 
-       image_fd = open(filename, O_RDONLY);
-       if (image_fd < 0) {
-               perror("open");
+       image_fp = fopen(filename, "rb");
+       if (image_fp == NULL) {
+               perror("fopen");
                exit_code = EXIT_FAILURE;
                goto out;
        }
-       if (fstat(image_fd, &st) < 0) {
+       if (fstat(fileno(image_fp), &st) < 0) {
                perror("fstat");
                exit_code = EXIT_FAILURE;
-               goto out_close_image_fd;
+               goto out_close_image_fp;
        }
        size = st.st_size;
 
-       image = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, image_fd, 0);
+       image = malloc(size * sizeof(unsigned char));
        if (image == NULL) {
-               perror("mmap");
+               perror("malloc");
                exit_code = EXIT_FAILURE;
-               goto out_close_image_fd;
+               goto out_close_image_fp;
+       }
+
+       ret = fread(image, size, 1, image_fp);
+       if (ret != 1) {
+               if (feof(image_fp))
+                       fprintf(stderr, "Unexpected end of file.\n");
+               else if (ferror(image_fp))
+                       perror("fread");
+               else
+                       fprintf(stderr, "Unexpected error condition.\n");
+
+               goto out_free_image;
        }
 
        ret = am7xxx_init(&ctx);
        if (ret < 0) {
                perror("am7xxx_init");
                exit_code = EXIT_FAILURE;
-               goto out_munmap;
+               goto out_free_image;
        }
 
        am7xxx_set_log_level(ctx, log_level);
@@ -186,7 +229,7 @@ int main(int argc, char *argv[])
                goto cleanup;
        }
 
-       ret = am7xxx_open_device(ctx, &dev, 0);
+       ret = am7xxx_open_device(ctx, &dev, device_index);
        if (ret < 0) {
                perror("am7xxx_open_device");
                exit_code = EXIT_FAILURE;
@@ -195,13 +238,20 @@ int main(int argc, char *argv[])
 
        ret = am7xxx_get_device_info(dev, &device_info);
        if (ret < 0) {
-               perror("am7xxx_get_info");
+               perror("am7xxx_get_device_info");
                exit_code = EXIT_FAILURE;
                goto cleanup;
        }
        printf("Native resolution: %dx%d\n",
               device_info.native_width, device_info.native_height);
 
+       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");
@@ -209,6 +259,17 @@ int main(int argc, char *argv[])
                goto cleanup;
        }
 
+       /* When setting AM7XXX_ZOOM_TEST don't display the actual image */
+       if (zoom == AM7XXX_ZOOM_TEST) {
+               printf("AM7XXX_ZOOM_TEST requested, not sending actual image.\n");
+               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");
+
        ret = am7xxx_send_image(dev, format, width, height, image, size);
        if (ret < 0) {
                perror("am7xxx_send_image");
@@ -221,15 +282,13 @@ int main(int argc, char *argv[])
 cleanup:
        am7xxx_shutdown(ctx);
 
-out_munmap:
-       ret = munmap(image, size);
-       if (ret < 0)
-               perror("munmap");
+out_free_image:
+       free(image);
 
-out_close_image_fd:
-       ret = close(image_fd);
-       if (ret < 0)
-               perror("close");
+out_close_image_fp:
+       ret = fclose(image_fp);
+       if (ret == EOF)
+               perror("fclose");
 
 out:
        exit(exit_code);
index e09a282..0817374 100644 (file)
@@ -22,7 +22,12 @@ endif()
 install(TARGETS am7xxx-static
    DESTINATION "${CMAKE_INSTALL_PREFIX}/lib")
 
-find_library(MATH_LIB m)
+if(NOT WIN32)
+  find_library(MATH_LIB m)
+else()
+  # not needed on windows
+  set(MATH_LIB "")
+endif()
 
 target_link_libraries(am7xxx ${MATH_LIB} ${LIBUSB_1_LIBRARIES})
 target_link_libraries(am7xxx-static ${MATH_LIB} ${LIBUSB_1_LIBRARIES})
index 8087f89..83d9a91 100644 (file)
@@ -77,10 +77,25 @@ static struct am7xxx_usb_device_descriptor supported_devices[] = {
                .product_id = 0xc101,
        },
        {
+               .name       = "Acer C112",
+               .vendor_id  = 0x1de1,
+               .product_id = 0x5501,
+       },
+       {
+               .name       ="Aiptek PocketCinema T25",
+               .vendor_id  = 0x08ca,
+               .product_id = 0x2144,
+       },
+       {
                .name       = "Philips/Sagemcom PicoPix 1020",
                .vendor_id  = 0x21e7,
                .product_id = 0x000e,
        },
+       {
+               .name       = "Philips/Sagemcom PicoPix 2055",
+               .vendor_id  = 0x21e7,
+               .product_id = 0x0016,
+       },
 };
 
 /* The header size on the wire is known to be always 24 bytes, regardless of
@@ -92,6 +107,7 @@ static struct am7xxx_usb_device_descriptor supported_devices[] = {
 struct _am7xxx_device {
        libusb_device_handle *usb_device;
        uint8_t buffer[AM7XXX_HEADER_WIRE_SIZE];
+       am7xxx_device_info *device_info;
        am7xxx_context *ctx;
        am7xxx_device *next;
 };
@@ -106,7 +122,7 @@ typedef enum {
        AM7XXX_PACKET_TYPE_DEVINFO = 0x01,
        AM7XXX_PACKET_TYPE_IMAGE   = 0x02,
        AM7XXX_PACKET_TYPE_POWER   = 0x04,
-       AM7XXX_PACKET_TYPE_UNKNOWN = 0x05,
+       AM7XXX_PACKET_TYPE_ZOOM    = 0x05,
 } am7xxx_packet_type;
 
 struct am7xxx_generic_header {
@@ -136,6 +152,11 @@ struct am7xxx_power_header {
        uint32_t bit0;
 };
 
+struct am7xxx_zoom_header {
+       uint32_t bit1;
+       uint32_t bit0;
+};
+
 /*
  * Examples of packet headers:
  *
@@ -146,9 +167,13 @@ struct am7xxx_power_header {
  * 04 00 00 00 00 0c ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  */
 
+/* Direction of the communication from the host point of view */
+#define AM7XXX_DIRECTION_OUT 0 /* host -> device */
+#define AM7XXX_DIRECTION_IN  1 /* host <- device */
+
 struct am7xxx_header {
        uint32_t packet_type;
-       uint8_t unknown0;
+       uint8_t direction;
        uint8_t header_data_len;
        uint8_t unknown2;
        uint8_t unknown3;
@@ -157,6 +182,7 @@ struct am7xxx_header {
                struct am7xxx_devinfo_header devinfo;
                struct am7xxx_image_header image;
                struct am7xxx_power_header power;
+               struct am7xxx_zoom_header zoom;
        } header_data;
 };
 
@@ -197,6 +223,16 @@ static void debug_dump_power_header(am7xxx_context *ctx, struct am7xxx_power_hea
        debug(ctx, "\tbit0: 0x%08x (%u)\n", p->bit0, p->bit0);
 }
 
+static void debug_dump_zoom_header(am7xxx_context *ctx, struct am7xxx_zoom_header *z)
+{
+       if (ctx == NULL || z == NULL)
+               return;
+
+       debug(ctx, "Zoom header:\n");
+       debug(ctx, "\tbit1: 0x%08x (%u)\n", z->bit1, z->bit1);
+       debug(ctx, "\tbit0: 0x%08x (%u)\n", z->bit0, z->bit0);
+}
+
 static void debug_dump_header(am7xxx_context *ctx, struct am7xxx_header *h)
 {
        if (ctx == NULL || h == NULL)
@@ -204,7 +240,10 @@ static void debug_dump_header(am7xxx_context *ctx, struct am7xxx_header *h)
 
        debug(ctx, "BEGIN\n");
        debug(ctx, "packet_type:     0x%08x (%u)\n", h->packet_type, h->packet_type);
-       debug(ctx, "unknown0:        0x%02hhx (%hhu)\n", h->unknown0, h->unknown0);
+       debug(ctx, "direction:       0x%02hhx (%hhu) (%s)\n", h->direction, h->direction,
+             h->direction == AM7XXX_DIRECTION_IN ? "IN" :
+             h->direction == AM7XXX_DIRECTION_OUT ? "OUT" :
+             "UNKNOWN");
        debug(ctx, "header_data_len: 0x%02hhx (%hhu)\n", h->header_data_len, h->header_data_len);
        debug(ctx, "unknown2:        0x%02hhx (%hhu)\n", h->unknown2, h->unknown2);
        debug(ctx, "unknown3:        0x%02hhx (%hhu)\n", h->unknown3, h->unknown3);
@@ -222,6 +261,10 @@ static void debug_dump_header(am7xxx_context *ctx, struct am7xxx_header *h)
                debug_dump_power_header(ctx, &(h->header_data.power));
                break;
 
+       case AM7XXX_PACKET_TYPE_ZOOM:
+               debug_dump_zoom_header(ctx, &(h->header_data.zoom));
+               break;
+
        default:
                debug(ctx, "Packet type not supported!\n");
                break;
@@ -294,7 +337,7 @@ static int send_data(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
 
        trace_dump_buffer(dev->ctx, "sending -->", buffer, len);
 
-       ret = libusb_bulk_transfer(dev->usb_device, 1, 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);
@@ -309,7 +352,7 @@ static void serialize_header(struct am7xxx_header *h, uint8_t *buffer)
        uint8_t **buffer_iterator = &buffer;
 
        put_le32(h->packet_type, buffer_iterator);
-       put_8(h->unknown0, buffer_iterator);
+       put_8(h->direction, buffer_iterator);
        put_8(h->header_data_len, buffer_iterator);
        put_8(h->unknown2, buffer_iterator);
        put_8(h->unknown3, buffer_iterator);
@@ -324,7 +367,7 @@ static void unserialize_header(uint8_t *buffer, struct am7xxx_header *h)
        uint8_t **buffer_iterator = &buffer;
 
        h->packet_type = get_le32(buffer_iterator);
-       h->unknown0 = get_8(buffer_iterator);
+       h->direction = get_8(buffer_iterator);
        h->header_data_len = get_8(buffer_iterator);
        h->unknown2 = get_8(buffer_iterator);
        h->unknown3 = get_8(buffer_iterator);
@@ -344,9 +387,16 @@ static int read_header(am7xxx_device *dev, struct am7xxx_header *h)
 
        unserialize_header(dev->buffer, h);
 
-       debug_dump_header(dev->ctx, h);
+       if (h->direction == AM7XXX_DIRECTION_IN) {
+               ret = 0;
+       } else {
+               error(dev->ctx,
+                     "Expected an AM7XXX_DIRECTION_IN packet, got one with direction = %d. Weird!\n",
+                     h->direction);
+               ret = -EINVAL;
+       }
 
-       ret = 0;
+       debug_dump_header(dev->ctx, h);
 
 out:
        return ret;
@@ -358,7 +408,13 @@ static int send_header(am7xxx_device *dev, struct am7xxx_header *h)
 
        debug_dump_header(dev->ctx, h);
 
+       /* For symmetry with read_header() we should check here for
+        * h->direction == AM7XXX_DIRECTION_OUT but we just ensure that in all
+        * the callers and save some cycles here.
+        */
+
        serialize_header(h, dev->buffer);
+
        ret = send_data(dev, dev->buffer, AM7XXX_HEADER_WIRE_SIZE);
        if (ret < 0)
                error(dev->ctx, "failed to send data\n");
@@ -405,8 +461,6 @@ static am7xxx_device *add_new_device(am7xxx_context *ctx)
                return NULL;
        }
 
-       devices_list = &(ctx->devices_list);
-
        new_device = malloc(sizeof(*new_device));
        if (new_device == NULL) {
                fatal("cannot allocate a new device (%s)\n", strerror(errno));
@@ -416,6 +470,8 @@ static am7xxx_device *add_new_device(am7xxx_context *ctx)
 
        new_device->ctx = ctx;
 
+       devices_list = &(ctx->devices_list);
+
        if (*devices_list == NULL) {
                *devices_list = new_device;
        } else {
@@ -501,8 +557,8 @@ static int scan_devices(am7xxx_context *ctx, scan_op op,
                        continue;
 
                for (j = 0; j < ARRAY_SIZE(supported_devices); j++) {
-                       if (desc.idVendor == supported_devices[j].vendor_id
-                           && desc.idProduct == supported_devices[j].product_id) {
+                       if (desc.idVendor == supported_devices[j].vendor_id &&
+                           desc.idProduct == supported_devices[j].product_id) {
 
                                if (op == SCAN_OP_BUILD_DEVLIST) {
                                        am7xxx_device *new_device;
@@ -542,7 +598,7 @@ static int scan_devices(am7xxx_context *ctx, scan_op op,
                                                goto out;
                                        }
 
-                                       libusb_set_configuration((*dev)->usb_device, 1);
+                                       libusb_set_configuration((*dev)->usb_device, 2);
                                        libusb_claim_interface((*dev)->usb_device, 0);
                                        goto out;
                                }
@@ -619,6 +675,7 @@ AM7XXX_PUBLIC void am7xxx_shutdown(am7xxx_context *ctx)
        while (current) {
                am7xxx_device *next = current->next;
                am7xxx_close_device(current);
+               free(current->device_info);
                free(current);
                current = next;
        }
@@ -646,12 +703,28 @@ AM7XXX_PUBLIC int am7xxx_open_device(am7xxx_context *ctx, am7xxx_device **dev,
        ret = scan_devices(ctx, SCAN_OP_OPEN_DEVICE, device_index, dev);
        if (ret < 0) {
                errno = ENODEV;
+               goto out;
        } else if (ret > 0) {
                warning(ctx, "device %d already open\n", device_index);
                errno = EBUSY;
                ret = -EBUSY;
+               goto out;
        }
 
+       /* Philips/Sagemcom PicoPix projectors require that the DEVINFO packet
+        * is the first one to be sent to the device in order for it to
+        * successfully return the correct device information.
+        *
+        * So, if there is not a cached version of it (from a previous open),
+        * we ask for device info at open time,
+        */
+       if ((*dev)->device_info == NULL) {
+               ret = am7xxx_get_device_info(*dev, NULL);
+               if (ret < 0)
+                       error(ctx, "cannot get device info\n");
+       }
+
+out:
        return ret;
 }
 
@@ -675,7 +748,7 @@ AM7XXX_PUBLIC int am7xxx_get_device_info(am7xxx_device *dev,
        int ret;
        struct am7xxx_header h = {
                .packet_type     = AM7XXX_PACKET_TYPE_DEVINFO,
-               .unknown0        = 0x00,
+               .direction       = AM7XXX_DIRECTION_OUT,
                .header_data_len = 0x00,
                .unknown2        = 0x3e,
                .unknown3        = 0x10,
@@ -689,6 +762,11 @@ AM7XXX_PUBLIC int am7xxx_get_device_info(am7xxx_device *dev,
                },
        };
 
+       if (dev->device_info) {
+               memcpy(device_info, dev->device_info, sizeof(*device_info));
+               return 0;
+       }
+
        ret = send_header(dev, &h);
        if (ret < 0)
                return ret;
@@ -697,12 +775,27 @@ AM7XXX_PUBLIC int am7xxx_get_device_info(am7xxx_device *dev,
        if (ret < 0)
                return ret;
 
-       device_info->native_width = h.header_data.devinfo.native_width;
-       device_info->native_height = h.header_data.devinfo.native_height;
+       if (h.packet_type != AM7XXX_PACKET_TYPE_DEVINFO) {
+               error(dev->ctx, "expected packet type: %d, got %d instead!\n",
+                     AM7XXX_PACKET_TYPE_DEVINFO, h.packet_type);
+               errno = ENOTSUP;
+               return -ENOTSUP;
+       }
+
+       dev->device_info = malloc(sizeof(*dev->device_info));
+       if (dev->device_info == NULL) {
+               error(dev->ctx, "cannot allocate a device info (%s)\n",
+                      strerror(errno));
+               return -ENOMEM;
+       }
+       memset(dev->device_info, 0, sizeof(*dev->device_info));
+
+       dev->device_info->native_width = h.header_data.devinfo.native_width;
+       dev->device_info->native_height = h.header_data.devinfo.native_height;
 #if 0
        /* No reason to expose these in the public API until we know what they mean */
-       device_info->unknown0 = h.header_data.devinfo.unknown0;
-       device_info->unknown1 = h.header_data.devinfo.unknown1;
+       dev->device_info->unknown0 = h.header_data.devinfo.unknown0;
+       dev->device_info->unknown1 = h.header_data.devinfo.unknown1;
 #endif
 
        return 0;
@@ -780,7 +873,7 @@ AM7XXX_PUBLIC int am7xxx_send_image(am7xxx_device *dev,
        int ret;
        struct am7xxx_header h = {
                .packet_type     = AM7XXX_PACKET_TYPE_IMAGE,
-               .unknown0        = 0x00,
+               .direction       = AM7XXX_DIRECTION_OUT,
                .header_data_len = sizeof(struct am7xxx_image_header),
                .unknown2        = 0x3e,
                .unknown3        = 0x10,
@@ -806,18 +899,18 @@ AM7XXX_PUBLIC int am7xxx_send_image(am7xxx_device *dev,
        return send_data(dev, image, image_size);
 }
 
-AM7XXX_PUBLIC int am7xxx_set_power_mode(am7xxx_device *dev, am7xxx_power_mode mode)
+AM7XXX_PUBLIC int am7xxx_set_power_mode(am7xxx_device *dev, am7xxx_power_mode power)
 {
        int ret;
        struct am7xxx_header h = {
                .packet_type     = AM7XXX_PACKET_TYPE_POWER,
-               .unknown0        = 0x00,
+               .direction       = AM7XXX_DIRECTION_OUT,
                .header_data_len = sizeof(struct am7xxx_power_header),
                .unknown2        = 0x3e,
                .unknown3        = 0x10,
        };
 
-       switch(mode) {
+       switch(power) {
        case AM7XXX_POWER_OFF:
                h.header_data.power.bit2 = 0;
                h.header_data.power.bit1 = 0;
@@ -828,6 +921,7 @@ AM7XXX_PUBLIC int am7xxx_set_power_mode(am7xxx_device *dev, am7xxx_power_mode mo
                h.header_data.power.bit2 = 0;
                h.header_data.power.bit1 = 0;
                h.header_data.power.bit0 = 1;
+               break;
 
        case AM7XXX_POWER_MIDDLE:
                h.header_data.power.bit2 = 0;
@@ -848,7 +942,51 @@ AM7XXX_PUBLIC int am7xxx_set_power_mode(am7xxx_device *dev, am7xxx_power_mode mo
                break;
 
        default:
-               error(dev->ctx, "Power mode not supported!\n");
+               error(dev->ctx, "Unsupported power mode.\n");
+               return -EINVAL;
+       };
+
+       ret = send_header(dev, &h);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+AM7XXX_PUBLIC int am7xxx_set_zoom_mode(am7xxx_device *dev, am7xxx_zoom_mode zoom)
+{
+       int ret;
+       struct am7xxx_header h = {
+               .packet_type     = AM7XXX_PACKET_TYPE_ZOOM,
+               .direction       = AM7XXX_DIRECTION_OUT,
+               .header_data_len = sizeof(struct am7xxx_zoom_header),
+               .unknown2        = 0x3e,
+               .unknown3        = 0x10,
+       };
+
+       switch(zoom) {
+       case AM7XXX_ZOOM_ORIGINAL:
+               h.header_data.zoom.bit1 = 0;
+               h.header_data.zoom.bit0 = 0;
+               break;
+
+       case AM7XXX_ZOOM_H:
+               h.header_data.zoom.bit1 = 0;
+               h.header_data.zoom.bit0 = 1;
+               break;
+
+       case AM7XXX_ZOOM_H_V:
+               h.header_data.zoom.bit1 = 1;
+               h.header_data.zoom.bit0 = 0;
+               break;
+
+       case AM7XXX_ZOOM_TEST:
+               h.header_data.zoom.bit1 = 1;
+               h.header_data.zoom.bit0 = 1;
+               break;
+
+       default:
+               error(dev->ctx, "Unsupported zoom mode.\n");
                return -EINVAL;
        };
 
index 51f9324..24636c3 100644 (file)
@@ -105,6 +105,24 @@ typedef enum {
 } am7xxx_power_mode;
 
 /**
+ * The display zoom modes.
+ *
+ * An am7xxx device can display images using several zoom modes.
+ *
+ * @note Changing the zoom mode can change the aspect ratio of the displayed
+ * image.
+ *
+ * @note On the zoom test screen the version of the firmware running on the
+ * device is shown as well (e.g SPI_V21.0.0_2011.03.18).
+ */
+typedef enum {
+       AM7XXX_ZOOM_ORIGINAL = 0, /**< Original Size, as retrieved via #am7xxx_device_info. */
+       AM7XXX_ZOOM_H        = 1, /**< Zoom 1: H Scale (changes aspect ratio). */
+       AM7XXX_ZOOM_H_V      = 2, /**< Zoom 2: H/V Scale (changes aspect ratio). */
+       AM7XXX_ZOOM_TEST     = 3, /**< Zoom test screen, the firmware version is shown as well. */
+} am7xxx_zoom_mode;
+
+/**
  * Initialize the library context and data structures, and scan for devices.
  *
  * @param[out] ctx A pointer to the context the library will be used in.
@@ -226,17 +244,38 @@ int am7xxx_send_image(am7xxx_device *dev,
 /**
  * Set the power mode of an am7xxx device.
  *
- * \note If we set the mode to AM7XXX_POWER_OFF we can't turn the
- * display on again by using only am7xxx_set_power_mode(). This needs to be
- * investigated, maybe some other command can reset the device.
+ * @note When setting the mode to AM7XXX_POWER_OFF the display can't be turned
+ * on again by using only am7xxx_set_power_mode(), am7xxx_set_zoom_mode() has
+ * to be called first, the current guess is that the latter performs some
+ * other resets beside setting the zoom mode.
  *
- * @param[in] dev A pointer to the structure representing the device to get info of
- * @param[in] mode The power mode to put the device in (see @link am7xxx_power_mode @endlink enum)
+ * @param[in] dev A pointer to the structure representing the device to set power mode to
+ * @param[in] power The power mode to put the device in (see #am7xxx_power_mode enum)
+ *
+ * @return 0 on success, a negative value on error
+ *
+ */
+int am7xxx_set_power_mode(am7xxx_device *dev, am7xxx_power_mode power);
+
+/**
+ * Set the zoom mode of an am7xxx device.
+ *
+ * @note When setting the mode to AM7XXX_ZOOM_TEST, the calling program might
+ * want to skip displaying actual images.
+ *
+ * @note It looks like that power mode and zoom mode are related somehow wrt.
+ * resetting the operational mode after AM7XXX_POWER_OFF, applications can
+ * restore the display properly using this combination:
+ *  - Off: power mode 0, zoom mode 3
+ *  - On: power mode != 0, zoom mode != 3
+ *
+ * @param[in] dev A pointer to the structure representing the device to set zoom mode to
+ * @param[in] zoom The zoom mode to put the device in (see #am7xxx_zoom_mode enum)
  *
  * @return 0 on success, a negative value on error
  *
  */
-int am7xxx_set_power_mode(am7xxx_device *dev, am7xxx_power_mode mode);
+int am7xxx_set_zoom_mode(am7xxx_device *dev, am7xxx_zoom_mode zoom);
 
 #ifdef __cplusplus
 }
index be300ea..72e3fd4 100644 (file)
  */
 
 #include <string.h>
+
+#ifdef __MINGW32__
+#define le32toh(x) (x)
+#define htole32(x) (x)
+#else
 #include <endian.h>
+#endif
 
 #include "serialize.h"
 
index e2dd7d4..f1869c9 100644 (file)
@@ -16,7 +16,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/* You can transform a serializazion block of code which uses put-* into the
+/* You can transform a serialization block of code which uses put-* into the
  * correspondent unserialization block with this vim substitution pattern:
  *
  *   s/put_\([^(]*\)(\([^,]*\),\s*\([^)]*\))/\2 = get_\1(\3)/g