aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt32
-rw-r--r--ChangeLog46
-rw-r--r--LICENSE165
-rw-r--r--cmake/FindLibUSB.cmake30
-rw-r--r--cmake/LibFindMacros.cmake266
-rw-r--r--image_header.h157
-rw-r--r--o3000.c1431
-rw-r--r--o3000.h101
-rw-r--r--o3000_portable.h144
-rw-r--r--o3000_private.h102
-rw-r--r--o3000_xfer_handler.c175
-rw-r--r--o3000_xfer_handler.h39
12 files changed, 2688 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..074bf52
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,32 @@
+#
+# O-3000 driver
+#
+
+cmake_minimum_required(VERSION 2.4)
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
+
+# find libusb library (version 1.0.19 or higher is needed!)
+find_package(LibUSB REQUIRED)
+
+include_directories(.)
+include_directories(${LIBUSB_INCLUDE_DIRS})
+
+add_library( o3000 SHARED
+ o3000.c
+ o3000_private.h
+ o3000_xfer_handler.c
+ o3000_xfer_handler.h
+ )
+
+set_target_properties ( o3000 PROPERTIES
+ OUTPUT_NAME "o3000"
+ VERSION 2.0.2
+ SOVERSION 2
+ LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
+ )
+
+target_compile_options(o3000 PRIVATE -Wall -g -ggdb -O3 -fPIC)
+target_link_libraries(o3000 pthread ${LIBUSB_LIBRARIES})
+
+install(TARGETS o3000 DESTINATION lib)
+install(DIRECTORY ../../include/o3000 DESTINATION include FILES_MATCHING PATTERN "*.h*")
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..f1ec48c
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,46 @@
+-------------------------------------------------------------------------------
+- ChangeLog O-3000 Host Driver
+-------------------------------------------------------------------------------
+
+Version 2.0.2
+ 2018-04-11-PR
+ * Function handle_transfer() rewritten:
+ The image frame synchronization was a bit hacky. Image frames were passed only to the
+ overlaying application after the next image frame has been received. But when receiving a single
+ snapshot, there won't follow an image. This causes a delay of one image until the application
+ receives it. Now the function passes images to the application after a image is
+ received completely without checking the preamble of the following image.
+ * Bug race condition at o3000_init() function:
+ Accessing the session table for getting next free session and allocation session structure
+ must be atomic.
+ * New function o3000_get_num_cam() added:
+ This function returns the number of connected O-3000 cameras to the system.
+ * New loglevel O3000_LOG_VERBOSE added:
+ This loglevel will set the libusb to debugging mode and is very useful for debugging purposes.
+
+Version 2.0.1
+ 2015-08-11 -PR
+ * Bugfix in function handle_transfer(): A negative byte offset was calculated when the resolution
+ has changed during wrap-around. A SEGFAULT occured.
+ * Fields added to image header and image header version update to 4:
+ - data rate in bytes/seconds
+ - frames per seconds
+ - field-of-view added used by lense distortion algorithm
+ * Short USB bulk transfers are supported now by the driver. Therefore the resolution
+ doesn't have to be a multiple of 512 bytes anymore and binning is done correctly.
+ There are following restrictions about image resolution settings:
+ - Window size must start with an even pixel coordinate (see examples below)
+ - Minimum image resolution is 640x480.
+ - Binning is allowed at full-screen only (1280x960)
+ E. g. valid window: X = 110 to 1111, Y = 32 to 917
+ E. g. invalid window: X = 111 to 1111, Y = 33 to 917 --> coordinate 111 and 33 are odd numbers
+
+ 2016-03-01
+ * Calling convention added for Win32 and Win64.
+
+Version 2.0.0 - 2015-02-16
+ * PR: Multi camera sessions capability. The host can scan the number of cameras and establish a connection to
+ each of them. Sending a XML message to the camera uses the synchronous API with OS dependet timeout handling.
+
+Version 1.0.0 - 2015-02-16
+ * PB: Inititial version
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..65c5ca8
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/cmake/FindLibUSB.cmake b/cmake/FindLibUSB.cmake
new file mode 100644
index 0000000..6468937
--- /dev/null
+++ b/cmake/FindLibUSB.cmake
@@ -0,0 +1,30 @@
+# - Try to find libusb-1.0
+# Once done, this will define
+#
+# LIBUSB_FOUND - system has libusb-1.0
+# LIBUSB_INCLUDE_DIRS - the libusb-1.0 include directories
+# LIBUSB_LIBRARIES - link these to use libusb-1.0
+
+include(LibFindMacros)
+
+# Use pkg-config to get hints about paths
+# libfind_pkg_check_modules(LIBUSB_PKGCONF libusb-1.0>=1.0.19)
+libfind_pkg_check_modules(LIBUSB_PKGCONF libusb-1.0)
+
+# Include dir
+find_path(LIBUSB_INCLUDE_DIR
+ NAMES libusb.h
+ PATHS ${LIBUSB_PKGCONF_INCLUDE_DIRS}
+)
+
+# Finally the library itself
+find_library(LIBUSB_LIBRARY
+ NAMES usb-1.0
+ PATHS ${LIBUSB_PKGCONF_LIBRARY_DIRS}
+)
+
+# Set the include dir variables and the libraries and let libfind_process do the rest.
+# NOTE: Singular variables for this library, plural for libraries this this lib depends on.
+set(LIBUSB_PROCESS_INCLUDES LIBUSB_INCLUDE_DIR)
+set(LIBUSB_PROCESS_LIBS LIBUSB_LIBRARY)
+libfind_process(LIBUSB)
diff --git a/cmake/LibFindMacros.cmake b/cmake/LibFindMacros.cmake
new file mode 100644
index 0000000..3ef5844
--- /dev/null
+++ b/cmake/LibFindMacros.cmake
@@ -0,0 +1,266 @@
+# Version 2.2
+# Public Domain, originally written by Lasse Kärkkäinen <tronic>
+# Maintained at https://github.com/Tronic/cmake-modules
+# Please send your improvements as pull requests on Github.
+
+# Find another package and make it a dependency of the current package.
+# This also automatically forwards the "REQUIRED" argument.
+# Usage: libfind_package(<prefix> <another package> [extra args to find_package])
+macro (libfind_package PREFIX PKG)
+ set(${PREFIX}_args ${PKG} ${ARGN})
+ if (${PREFIX}_FIND_REQUIRED)
+ set(${PREFIX}_args ${${PREFIX}_args} REQUIRED)
+ endif()
+ find_package(${${PREFIX}_args})
+ set(${PREFIX}_DEPENDENCIES ${${PREFIX}_DEPENDENCIES};${PKG})
+ unset(${PREFIX}_args)
+endmacro()
+
+# A simple wrapper to make pkg-config searches a bit easier.
+# Works the same as CMake's internal pkg_check_modules but is always quiet.
+macro (libfind_pkg_check_modules)
+ find_package(PkgConfig QUIET)
+ if (PKG_CONFIG_FOUND)
+ pkg_check_modules(${ARGN} QUIET)
+ endif()
+endmacro()
+
+# Avoid useless copy&pasta by doing what most simple libraries do anyway:
+# pkg-config, find headers, find library.
+# Usage: libfind_pkg_detect(<prefix> <pkg-config args> FIND_PATH <name> [other args] FIND_LIBRARY <name> [other args])
+# E.g. libfind_pkg_detect(SDL2 sdl2 FIND_PATH SDL.h PATH_SUFFIXES SDL2 FIND_LIBRARY SDL2)
+function (libfind_pkg_detect PREFIX)
+ # Parse arguments
+ set(argname pkgargs)
+ foreach (i ${ARGN})
+ if ("${i}" STREQUAL "FIND_PATH")
+ set(argname pathargs)
+ elseif ("${i}" STREQUAL "FIND_LIBRARY")
+ set(argname libraryargs)
+ else()
+ set(${argname} ${${argname}} ${i})
+ endif()
+ endforeach()
+ if (NOT pkgargs)
+ message(FATAL_ERROR "libfind_pkg_detect requires at least a pkg_config package name to be passed.")
+ endif()
+ # Find library
+ libfind_pkg_check_modules(${PREFIX}_PKGCONF ${pkgargs})
+ if (pathargs)
+ find_path(${PREFIX}_INCLUDE_DIR NAMES ${pathargs} HINTS ${${PREFIX}_PKGCONF_INCLUDE_DIRS})
+ endif()
+ if (libraryargs)
+ find_library(${PREFIX}_LIBRARY NAMES ${libraryargs} HINTS ${${PREFIX}_PKGCONF_LIBRARY_DIRS})
+ endif()
+endfunction()
+
+# Extracts a version #define from a version.h file, output stored to <PREFIX>_VERSION.
+# Usage: libfind_version_header(Foobar foobar/version.h FOOBAR_VERSION_STR)
+# Fourth argument "QUIET" may be used for silently testing different define names.
+# This function does nothing if the version variable is already defined.
+function (libfind_version_header PREFIX VERSION_H DEFINE_NAME)
+ # Skip processing if we already have a version or if the include dir was not found
+ if (${PREFIX}_VERSION OR NOT ${PREFIX}_INCLUDE_DIR)
+ return()
+ endif()
+ set(quiet ${${PREFIX}_FIND_QUIETLY})
+ # Process optional arguments
+ foreach(arg ${ARGN})
+ if (arg STREQUAL "QUIET")
+ set(quiet TRUE)
+ else()
+ message(AUTHOR_WARNING "Unknown argument ${arg} to libfind_version_header ignored.")
+ endif()
+ endforeach()
+ # Read the header and parse for version number
+ set(filename "${${PREFIX}_INCLUDE_DIR}/${VERSION_H}")
+ if (NOT EXISTS ${filename})
+ if (NOT quiet)
+ message(AUTHOR_WARNING "Unable to find ${${PREFIX}_INCLUDE_DIR}/${VERSION_H}")
+ endif()
+ return()
+ endif()
+ file(READ "${filename}" header)
+ string(REGEX REPLACE ".*#[ \t]*define[ \t]*${DEFINE_NAME}[ \t]*\"([^\n]*)\".*" "\\1" match "${header}")
+ # No regex match?
+ if (match STREQUAL header)
+ if (NOT quiet)
+ message(AUTHOR_WARNING "Unable to find \#define ${DEFINE_NAME} \"<version>\" from ${${PREFIX}_INCLUDE_DIR}/${VERSION_H}")
+ endif()
+ return()
+ endif()
+ # Export the version string
+ set(${PREFIX}_VERSION "${match}" PARENT_SCOPE)
+endfunction()
+
+# Do the final processing once the paths have been detected.
+# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain
+# all the variables, each of which contain one include directory.
+# Ditto for ${PREFIX}_PROCESS_LIBS and library files.
+# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES.
+# Also handles errors in case library detection was required, etc.
+function (libfind_process PREFIX)
+ # Skip processing if already processed during this configuration run
+ if (${PREFIX}_FOUND)
+ return()
+ endif()
+
+ set(found TRUE) # Start with the assumption that the package was found
+
+ # Did we find any files? Did we miss includes? These are for formatting better error messages.
+ set(some_files FALSE)
+ set(missing_headers FALSE)
+
+ # Shorthands for some variables that we need often
+ set(quiet ${${PREFIX}_FIND_QUIETLY})
+ set(required ${${PREFIX}_FIND_REQUIRED})
+ set(exactver ${${PREFIX}_FIND_VERSION_EXACT})
+ set(findver "${${PREFIX}_FIND_VERSION}")
+ set(version "${${PREFIX}_VERSION}")
+
+ # Lists of config option names (all, includes, libs)
+ unset(configopts)
+ set(includeopts ${${PREFIX}_PROCESS_INCLUDES})
+ set(libraryopts ${${PREFIX}_PROCESS_LIBS})
+
+ # Process deps to add to
+ foreach (i ${PREFIX} ${${PREFIX}_DEPENDENCIES})
+ if (DEFINED ${i}_INCLUDE_OPTS OR DEFINED ${i}_LIBRARY_OPTS)
+ # The package seems to export option lists that we can use, woohoo!
+ list(APPEND includeopts ${${i}_INCLUDE_OPTS})
+ list(APPEND libraryopts ${${i}_LIBRARY_OPTS})
+ else()
+ # If plural forms don't exist or they equal singular forms
+ if ((NOT DEFINED ${i}_INCLUDE_DIRS AND NOT DEFINED ${i}_LIBRARIES) OR
+ ({i}_INCLUDE_DIR STREQUAL ${i}_INCLUDE_DIRS AND ${i}_LIBRARY STREQUAL ${i}_LIBRARIES))
+ # Singular forms can be used
+ if (DEFINED ${i}_INCLUDE_DIR)
+ list(APPEND includeopts ${i}_INCLUDE_DIR)
+ endif()
+ if (DEFINED ${i}_LIBRARY)
+ list(APPEND libraryopts ${i}_LIBRARY)
+ endif()
+ else()
+ # Oh no, we don't know the option names
+ message(FATAL_ERROR "We couldn't determine config variable names for ${i} includes and libs. Aieeh!")
+ endif()
+ endif()
+ endforeach()
+
+ if (includeopts)
+ list(REMOVE_DUPLICATES includeopts)
+ endif()
+
+ if (libraryopts)
+ list(REMOVE_DUPLICATES libraryopts)
+ endif()
+
+ string(REGEX REPLACE ".*[ ;]([^ ;]*(_INCLUDE_DIRS|_LIBRARIES))" "\\1" tmp "${includeopts} ${libraryopts}")
+ if (NOT tmp STREQUAL "${includeopts} ${libraryopts}")
+ message(AUTHOR_WARNING "Plural form ${tmp} found in config options of ${PREFIX}. This works as before but is now deprecated. Please only use singular forms INCLUDE_DIR and LIBRARY, and update your find scripts for LibFindMacros > 2.0 automatic dependency system (most often you can simply remove the PROCESS variables entirely).")
+ endif()
+
+ # Include/library names separated by spaces (notice: not CMake lists)
+ unset(includes)
+ unset(libs)
+
+ # Process all includes and set found false if any are missing
+ foreach (i ${includeopts})
+ list(APPEND configopts ${i})
+ if (NOT "${${i}}" STREQUAL "${i}-NOTFOUND")
+ list(APPEND includes "${${i}}")
+ else()
+ set(found FALSE)
+ set(missing_headers TRUE)
+ endif()
+ endforeach()
+
+ # Process all libraries and set found false if any are missing
+ foreach (i ${libraryopts})
+ list(APPEND configopts ${i})
+ if (NOT "${${i}}" STREQUAL "${i}-NOTFOUND")
+ list(APPEND libs "${${i}}")
+ else()
+ set (found FALSE)
+ endif()
+ endforeach()
+
+ # Version checks
+ if (found AND findver)
+ if (NOT version)
+ message(WARNING "The find module for ${PREFIX} does not provide version information, so we'll just assume that it is OK. Please fix the module or remove package version requirements to get rid of this warning.")
+ elseif (version VERSION_LESS findver OR (exactver AND NOT version VERSION_EQUAL findver))
+ set(found FALSE)
+ set(version_unsuitable TRUE)
+ endif()
+ endif()
+
+ # If all-OK, hide all config options, export variables, print status and exit
+ if (found)
+ foreach (i ${configopts})
+ mark_as_advanced(${i})
+ endforeach()
+ if (NOT quiet)
+ message(STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}")
+ if (LIBFIND_DEBUG)
+ message(STATUS " ${PREFIX}_DEPENDENCIES=${${PREFIX}_DEPENDENCIES}")
+ message(STATUS " ${PREFIX}_INCLUDE_OPTS=${includeopts}")
+ message(STATUS " ${PREFIX}_INCLUDE_DIRS=${includes}")
+ message(STATUS " ${PREFIX}_LIBRARY_OPTS=${libraryopts}")
+ message(STATUS " ${PREFIX}_LIBRARIES=${libs}")
+ endif()
+ set (${PREFIX}_INCLUDE_OPTS ${includeopts} PARENT_SCOPE)
+ set (${PREFIX}_LIBRARY_OPTS ${libraryopts} PARENT_SCOPE)
+ set (${PREFIX}_INCLUDE_DIRS ${includes} PARENT_SCOPE)
+ set (${PREFIX}_LIBRARIES ${libs} PARENT_SCOPE)
+ set (${PREFIX}_FOUND TRUE PARENT_SCOPE)
+ endif()
+ return()
+ endif()
+
+ # Format messages for debug info and the type of error
+ set(vars "Relevant CMake configuration variables:\n")
+ foreach (i ${configopts})
+ mark_as_advanced(CLEAR ${i})
+ set(val ${${i}})
+ if ("${val}" STREQUAL "${i}-NOTFOUND")
+ set (val "<not found>")
+ elseif (val AND NOT EXISTS ${val})
+ set (val "${val} (does not exist)")
+ else()
+ set(some_files TRUE)
+ endif()
+ set(vars "${vars} ${i}=${val}\n")
+ endforeach()
+ set(vars "${vars}You may use CMake GUI, cmake -D or ccmake to modify the values. Delete CMakeCache.txt to discard all values and force full re-detection if necessary.\n")
+ if (version_unsuitable)
+ set(msg "${PREFIX} ${${PREFIX}_VERSION} was found but")
+ if (exactver)
+ set(msg "${msg} only version ${findver} is acceptable.")
+ else()
+ set(msg "${msg} version ${findver} is the minimum requirement.")
+ endif()
+ else()
+ if (missing_headers)
+ set(msg "We could not find development headers for ${PREFIX}. Do you have the necessary dev package installed?")
+ elseif (some_files)
+ set(msg "We only found some files of ${PREFIX}, not all of them. Perhaps your installation is incomplete or maybe we just didn't look in the right place?")
+ if(findver)
+ set(msg "${msg} This could also be caused by incompatible version (if it helps, at least ${PREFIX} ${findver} should work).")
+ endif()
+ else()
+ set(msg "We were unable to find package ${PREFIX}.")
+ endif()
+ endif()
+
+ # Fatal error out if REQUIRED
+ if (required)
+ set(msg "REQUIRED PACKAGE NOT FOUND\n${msg} This package is REQUIRED and you need to install it or adjust CMake configuration in order to continue building ${CMAKE_PROJECT_NAME}.")
+ message(FATAL_ERROR "${msg}\n${vars}")
+ endif()
+ # Otherwise just print a nasty warning
+ if (NOT quiet)
+ message(WARNING "WARNING: MISSING PACKAGE\n${msg} This package is NOT REQUIRED and you may ignore this warning but by doing so you may miss some functionality of ${CMAKE_PROJECT_NAME}. \n${vars}")
+ endif()
+endfunction()
+
diff --git a/image_header.h b/image_header.h
new file mode 100644
index 0000000..dd752e7
--- /dev/null
+++ b/image_header.h
@@ -0,0 +1,157 @@
+/**
+* @file image_header.h
+* @brief Image header definition
+* @author Patrick Brunner - brunner@stettbacher.ch
+* @author Patrick Roth - roth@stettbacher.ch
+* @version 1.1
+* @date 2016-03-01
+* @copyright 2012-2016 Stettbacher Signal Processing AG
+*
+* @remarks
+*
+* <PRE>
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+* </PRE>
+*
+*/
+
+
+#ifndef _IMAGE_HEADER_H
+#define _IMAGE_HEADER_H
+
+#include "o3000/o3000_portable.h"
+
+#define O3000_IMG_HEADER_VERSION 4 ///< O-3000 image header version
+
+/**
+ * Image header size in bytes
+ *
+ * @note The struct img_header_t defined below must be a multiple of 512 bytes.
+ * The macro @ref IMAGE_HEADER_SIZE just helps to verify the header size, as done with
+ * the assert command in image_header_init. Unfortunately, assertions in C can only
+ * be performed at runtime, not at compile-time...
+ */
+#define IMAGE_HEADER_SIZE 512
+
+/**
+ * Image data format.
+ * These are all possible values for the field format in struct img_header_t.
+ */
+enum enumDataFormat_t {
+ DF_RAW_MONO_8 = 0, ///< monochrome image with 8 bit per pixel
+ DF_RAW_MONO_12, ///< monochrome image with 12 bit per pixel
+ DF_RAW_BAYER_8, ///< bayer image with 8 bit per pixel
+ DF_RAW_BAYER_12, ///< bayer image with 12 bit per pixel
+ DF_HDR_MONO_20_COMP, ///< 20 bit HDR monochrome image compressed to 12 bits
+ DF_HDR_BAYER_20_COMP, ///< 20 bit HDR bayer image compressed to 12 bits
+};
+
+/**
+ * Bayer pattern color order.
+ * These are all possible values for the field bayer pattern in struct img_header_t.
+ * The individual values describe the order of the pixels in the image data array.
+ * Depending on the start coordinate, the pattern starts with a red,
+ * green (2 possibilities), or blue pixel.
+ * The pattern then repeats every 2 pixels in the X- and Y-direction.
+ *
+ * @code
+ * X | Y | 1st row | 2nd row | enum value
+ * -----+-------+--------------+--------------+-----------
+ * even | even | Gr, R, Gr, R | B, Gb, B, Gb | BP_GR
+ * odd | even | R, Gr, R, Gr | Gb, B, Gb, B | BP_RG
+ * even | odd | B, Gb, B, Gb | Gr, R, Gr, R | BP_BG
+ * odd | odd | Gb, B, Gb, B | R, Gr, R, Gr | BP_GB
+ * 1st row: first row data within the region of interest
+ *
+ * R: red pixel
+ * Gr: green pixel, adjacent to red pixel
+ * Gb: green pixel, adjacent to blue pixel
+ * B: blue pixel
+ * @endcode
+ */
+enum enumBayerPattern_t {
+ BP_GR = 0, ///< first row: green-red-green-red-...
+ BP_RG, ///< first row: red-green-red-green-...
+ BP_BG, ///< first row: blue-green-blue-green-...
+ BP_GB, ///< first row: green-blue-green-blue-...
+};
+
+
+/**
+ * Image header (must be a multiple of 512 bytes)
+ *
+ * @code
+ *
+ * X/Y coordinate system
+ *
+ * +---------------------------------> x-axis
+ * |0/0 1/0 2/0
+ * |0/1 1/1 2/1
+ * |
+ * | +-----------------+
+ * | | |
+ * | | field of view |
+ * | | |
+ * | +-----------------+
+ * v
+ * y-axis
+ *
+ * @endcode
+ *
+ * - upper left corner starts with coordinate 0/0
+ * - the image width defines the maximum horizontal view in x-direction
+ * - the image height defines the maximum vertical view y-direction
+ *
+ * As the sensor data may contain embedded statistics which should be ignored
+ * by the application, image_start defines the offset in bytes of the actual image
+ * data. The payload size contains the regular image size plus the size
+ * of the embedded data. If there is no embedded data, image start offset
+ * is 0 and the payload and image size are equal.
+ *
+ * @note Embedded data not only contains statistics lines at the beginning,
+ * but also at the end of the image. The field @ref bayer_pattern is defined for bayer-based
+ * data formats only (e.g. DF_RAW_BAYER_8).
+ */
+struct img_header_t {
+
+ // since image header version 1
+ uint32_t preamble[2]; ///< constant preamble (8 bytes)
+ uint32_t version; ///< header version number (4 bytes)
+ uint32_t payload_size; ///< payload size (in bytes) (4 bytes)
+ uint32_t image_start; ///< image start offset (in bytes) (4 bytes)
+ uint32_t image_size; ///< image size (in bytes) (4 bytes)
+ uint32_t width; ///< current image width (x-direction) (in pixels) (4 bytes)
+ uint32_t height; ///< current image height (y-direction) (in pixels) (4 bytes)
+ uint32_t format; ///< image data format (see @ref enumDataFormat_t) (4 bytes)
+ uint32_t frame_count; ///< frame sequence number (4 bytes)
+
+ // since image header version 2
+ uint32_t bayer_pattern; ///< bayer pattern color order (see @ref enumBayerPattern_t) (4 bytes)
+
+ // since image header version 3
+ uint32_t datarate; ///< USB data rate in bytes per seconds (4 bytes)
+ float fps; ///< frames per seconds (4 bytes)
+
+ // since image header version 4
+ uint16_t fov_x_start; ///< start x-cooredinate of current field of view (2 bytes)
+ uint16_t fov_x_end; ///< end x-cooredinate of current field of view (2 bytes)
+ uint16_t fov_y_start; ///< start y-cooredinate of current field of view (2 bytes)
+ uint16_t fov_y_end; ///< end y-cooredinate of current field of view (2 bytes)
+
+ uint8_t padding[452]; ///< padding bytes to fill header (452 bytes)
+} __packed__;
+
+
+#endif // _IMAGE_HEADER_H
diff --git a/o3000.c b/o3000.c
new file mode 100644
index 0000000..1ad5676
--- /dev/null
+++ b/o3000.c
@@ -0,0 +1,1431 @@
+/**
+* @file o3000.c
+* @brief O-3000 Camera driver
+* @author Patrick Brunner - brunner@stettbacher.ch
+* @author Patrick Roth - roth@stettbacher.ch
+* @version 1.1
+* @date 2016-03-01
+* @copyright 2012-2016 Stettbacher Signal Processing AG
+*
+* @remarks
+*
+* <PRE>
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+* </PRE>
+*
+*/
+
+
+/**
+
+@mainpage O-3000 camera driver
+
+@section intro Introduction
+
+This document describes the Application Programming Interface (API) for the
+<b>Stettbacher Signal Processing USB O-3000 Camera</b>, further referenced as "the camera".</p>
+The driver delivers a generic user interface for accessing the camera and runs on different operating system like:
+<ul>
+<li> Linux </li>
+<li> Windows </li>
+<li> MAC </li>
+</ul>
+The driver supports session handling. Therfore it's able to handle several camera session on the same host system accessing from one
+or several user applications.
+
+@section api API
+
+Two groups of functions represent the interface:
+<ol>
+<li> A set of functions provided by the camera API which can be called from
+ the user application (see @ref api_fun). </li>
+<li> A set of callback functions the user application must implement and
+ register with the camera API (see @ref cb_fun). </li>
+</ol>
+
+@subsection api_fun API functions
+
+The basic approach using the camera driver is:
+<ol>
+<li> Implementing all required callback functions, see @ref cb_fun. </li>
+<li> Initialising the camera driver, see o3000_init(). </li>
+<li> Connecting to the camera driver, see o3000_connect(). </li>
+</ol>
+
+Once the user application is connected to the driver by calling o3000_connect(), it will block until:
+<ul>
+<li> The driver exits by calling o3000_exit(). </li>
+<li> The camera is disconnected from the system. </li>
+</ul>
+
+XML messages are transfered to the camera by calling o3000_send_xml(). If the camera sends a XML message to the host, the
+driver triggers the @ref xml_cb. The same concept is used, when the camera sends video data to the host. After receving a complete frame, the driver calls the
+@ref video_cb.
+
+@note The video driver triggers any callback from the thread context, where o3000_connect() is called from. This means that the o3000_send_xml() function should
+never be called from the Video, XML or Logging callback. Otherwise the driver will lock. It's important, that any XML message sent with o3000_send_xml() to the camera
+is called from a different thread context.
+
+
+
+@subsection cb_fun Callback functions
+
+@subsubsection video_cb Video handler
+
+The video handler is called by the camera driver whenever a complete frame
+is received. The video handler holds a pointer to the image
+data (the frame) and a pointer to a structure describing the image format.
+This handler is mandatory.
+
+@subsubsection xml_cb XML handler
+
+The XML handler is called by the camera driver whenever a XML message is
+received. The XML handler holds a pointer to a string buffer
+containing the XML message, and the length of that message. This handler is
+mandatory.
+
+@subsubsection log_cb Logging handler
+
+The Logging handler is called by the driver to print debug messages.
+The handler contains a pointer to a (zero-terminated) string buffer
+containing the log message. This handler is optional.
+@note The verbosity of the messages depends on the log level and is defined from @ref O3000_LOG_NONE to @ref O3000_LOG_VERBOSE.
+
+@section example Example
+
+A simplified example with C is shown below, focussing on the order in which the various
+camera driver functions should be called. It's up to the user to implement
+multithreading in order to avoid a deadlock due to the blocking nature of o3000_connect()!
+
+@code
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <o3000/o3000.h>
+
+
+static char xml_msg[] = {"<camera><stream></stream></camera>"};
+
+static void xml_handling(int id, char* msg, int len) {
+ // insert your code here
+}
+
+static void video_handling(int id, unsigned char* buf, struct img_header_t* img_header) {
+ // insert your code here
+}
+
+static void log_handling(int id, char* msg) {
+ // insert your code here
+}
+
+int main(int argc, char **argv) {
+
+ int ret = 0;
+ int num_camera, i;
+ int cam_session;
+
+
+ // setup camera session with 4 MByte video cache
+ cam_session = o3000_init(O3000_VID, O3000_PID, 1024*1024*4, xml_handling, video_handling, log_handling, O3000_LOG_ERROR);
+ if(cam_session < 0) {
+ exit(1);
+ }
+
+ num_camera = o3000_device_discovery(cam_session);
+ if(num_camera < 0) {
+ o3000_exit(cam_session);
+ exit(1);
+ }
+
+ if(num_camera == 0) {
+ printf("%s: no camera connected to the system\n", __func__);
+ o3000_exit(cam_session);
+ exit(1);
+ }
+
+ // establish connection to first available camera
+ for(i = 0; i < num_camera; i++) {
+ printf("%s: establish connection to camera %d\n", __func__, i);
+ ret = o3000_connect(cam_session, i, xml_msg, strlen(xml_msg));
+ if(ret == O3000_ERROR_BUSY) {
+ printf("%s: device %d is already in use\n", __func__, i);
+ }
+ else {
+ break;
+ }
+ }
+
+ o3000_exit(cam_session);
+ exit(0);
+}
+
+@endcode
+
+*/
+
+/* Video buffering concept
+
+At program initialisation, a number of transfer buffers is sent to libusb.
+Libusb handles the low-level interactions with the kernel. A transfer is split
+into a number of URBs. These URBs are sent to the kernel and returned to libusb
+with the data received. As soon as enough data was received, libusb calls the
+transfer callback, which processes the data. In this case, the transfer handler
+is called. As soon as the transfer is handled, the transfer is resubmitted to
+libusb, ready to be filled again.
+
+Each transfer points to a buffer, where received data is copied to. All transfer
+buffers T(x) and the frame buffer FB (see below) are contiguous, i.e. starting
+from T(0) (transfer 0) to the end of the frame buffer a linear memory region
+without holes is allocated.
+
+Memory Layout:
+
++---- start of video cache
+| +---- start of frame buffer
+| |
+| |
+V V
++------+------+------+------+------+------+--------------------+
+| T(0) | T(1) | T(2) | T(3) | .... | T(n) | Frame buffer |
++------+------+------+------+------+------+--------------------+
+
+The transfers are submitted starting from T(0). As they are queued, the order
+T(0), T(1), etc. is always maintained. After filling the last buffer T(n),
+the sequence starts over with T(0). No double-buffering takes place, as by the
+strict sequence order and the fact that a received buffer is processed first
+before resubmission there is no risk of overwriting unprocessed data.
+
+The transfer handler scans through the data received. Whenever a complete frame
+is found in the buffers, the video callback is called, which is supposed to
+process the frame.
+
+A special case applies, if an incomplete frame remains at the end of T(n), which
+will be continued with T(0) (and maybe subsequent transfers). In that situation,
+if the frame is completely received, the second part, starting from T(0) will
+be copied to the frame buffer. As the frame buffer is linked directly after T(n),
+the frame is now located in a linear part of the memory, which is a condition for
+the video callback function which expects a pointer to a linear buffer, holding
+the frame.
+
+An other special case occurs, when a snapshot is sent. The last portion of a snapshot
+is usually sent as a short bulk transfer packet, which means, that a subsequent snapshot
+or streaming frame will be aligned to the start of a transfer, leaving a gap of invalid
+data between two frames in the video cache.
+
+Depending on the transfer size chosen, a frame may extend over several transfers,
+or a transfer may contain several frames.
+
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "o3000/o3000_portable.h"
+#include "o3000_private.h"
+#include "o3000/o3000.h"
+#include "o3000_xfer_handler.h"
+
+
+#define CAM_IN_EP (1 | LIBUSB_ENDPOINT_IN) ///< endpoint for video data
+#define XML_IN_EP (2 | LIBUSB_ENDPOINT_IN) ///< endpoint for inbound XML messages
+#define XML_OUT_EP (1) ///< endpoint for outbound XML messages
+
+
+
+/**
+ * Inbound XML messages size
+ */
+#define XML_IN_BUF_SIZE 16384
+
+
+/**
+ * Maximum image size ever expected.
+ *
+ * @note The size must be a multiple of 512 bytes!
+ */
+#define MAX_IMAGE_SIZE (1280*964*2+IMAGE_HEADER_SIZE)
+
+/**
+ * Minimum video cache size.
+ * It's the double of the maximum image size ever expected.
+ */
+#define MIN_VIDEO_CACHE_SIZE (MAX_IMAGE_SIZE)
+
+
+/**
+ * maximum number of camera sessions
+ */
+#define MAX_NUM_SESSION 16
+
+
+/**
+ * The video cache is divided into several USB transfer queued at at kernel.
+ */
+#define NUM_USB_VIDEO_TRANSFER 10
+
+
+/**
+ * Session table
+ * Each table entry points to an active session. If the entry is NULL it's not used and can be used for a new session.
+ */
+static struct o3000_session_t *session_table[MAX_NUM_SESSION];
+
+/*
+ * Thread synchronization mutex
+ */
+static o3000_mutex_static_t session_lock = O3000_MUTEX_INITIALIZER;
+
+/**
+ * Maps loglevel numbers to descriptive strings
+ */
+static const char *dbg_strlevel[] = {
+ "NONE",
+ "ERROR",
+ "WARNING",
+ "INFO",
+ "DEBUG"
+};
+
+
+
+/**
+ * Return session by ID
+ *
+ * @param id session ID
+ * @return adress to session or NULL if not available
+ */
+static struct o3000_session_t *get_session(int id) {
+
+ struct o3000_session_t *session;
+
+ if(id < 0 || id >= MAX_NUM_SESSION) {
+ return NULL;
+ }
+ session = session_table[id];
+ if(session == NULL) {
+ return NULL;
+ }
+ return session;
+}
+
+
+/**
+ * Cleanup given session.
+ * Everything is cleaned up like:
+ * - closing current session
+ * - release USB transfers
+ * - release video cache and XML message buffer
+ *
+ * In the end, the current session is freed.
+ *
+ */
+static void cleanup(int id) {
+
+ struct o3000_session_t *session;
+ int i;
+
+
+ session = get_session(id);
+ if(session == NULL) {
+ return;
+ }
+
+ // release device list
+ if(session->device_list != NULL) {
+ o3000_log(session, O3000_LOG_DEBUG, "%s: cleanup USB device discovery list\n", __func__);
+ libusb_free_device_list(session->device_list, 1);
+ session->device_list = NULL;
+ session->num_device_list = 0;
+ }
+
+ // close USB device
+ if(session->dev != NULL) {
+ o3000_log(session, O3000_LOG_DEBUG, "%s: close USB device handler\n", __func__);
+ libusb_close(session->dev);
+ session->dev = NULL;
+ }
+
+ // release USB video transfer
+ for(i = 0; i < NUM_USB_VIDEO_TRANSFER; i++) {
+ if(session->transfer_data[i] != NULL) {
+ o3000_log(session, O3000_LOG_DEBUG, "%s: release USB video transfers %d\n", __func__, i);
+ libusb_free_transfer(session->transfer_data[i]);
+ session->transfer_data[i] = NULL;
+ }
+ }
+
+ // release USB transfer transfer array
+ if(session->transfer_data != NULL) {
+ o3000_log(session, O3000_LOG_DEBUG, "%s: release USB transfer array\n", __func__);
+ free(session->transfer_data);
+ session->transfer_data = NULL;
+ }
+
+ // release USB XML transfer for incoming messages
+ if(session->transfer_xml_in != NULL) {
+ o3000_log(session, O3000_LOG_DEBUG, "%s: release USB XML transfer for incoming messages\n", __func__);
+ libusb_free_transfer(session->transfer_xml_in);
+ session->transfer_xml_in = NULL;
+ }
+
+ // release XML buffer
+ if(session->xml_in_buf != NULL) {
+ o3000_log(session, O3000_LOG_DEBUG, "%s: release XML message buffer\n", __func__);
+ free(session->xml_in_buf);
+ session->xml_in_buf = NULL;
+ }
+
+ // release video cache
+ if(session->video_cache != NULL) {
+ o3000_log(session, O3000_LOG_DEBUG, "%s: release video cache\n", __func__);
+ free(session->video_cache);
+ session->video_cache = NULL;
+ }
+
+
+
+ // add more cleanup code here...
+ // ..
+
+
+
+ // close libusb context
+ if(session->libusb_ctx != NULL) {
+ o3000_log(session, O3000_LOG_DEBUG, "%s: release USB context\n", __func__);
+ libusb_exit(session->libusb_ctx);
+ session->libusb_ctx = NULL;
+ }
+
+ // finally release this session
+ if(session_table[id] != NULL) {
+ o3000_log(session, O3000_LOG_DEBUG, "%s: release USB session\n", __func__);
+ free(session_table[id]);
+ session_table[id] = NULL;
+ }
+}
+
+
+
+/**
+ * Check wether given USB device is a camera
+ *
+ * @param session session pointer
+ * @param device USB device to check
+ * @return TRUE if it's a camera otherwise FALSE
+ */
+static int is_camera(struct o3000_session_t *session, libusb_device *device) {
+
+ int ret, dev_of_interest;
+ struct libusb_device_descriptor desc;
+
+ ret = libusb_get_device_descriptor(device, &desc);
+ if(ret != 0) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: getting USB descriptor failed (code %d)\n", __func__, ret);
+ return FALSE;
+ }
+
+ dev_of_interest = FALSE;
+ if((desc.idVendor == session->vid) && (desc.idProduct == session->pid)) {
+ dev_of_interest = TRUE;
+ }
+
+ o3000_log(session, O3000_LOG_DEBUG, "%s: USB device detected (Device class %d, VID:PID 0x%x:0x%x, S/N %d) - %s\n",
+ __func__, desc.bDeviceClass, desc.idVendor, desc.idProduct, desc.iSerialNumber, dev_of_interest == TRUE ? "camera":"no camera");
+
+ return dev_of_interest;
+}
+
+
+/**
+ * Print a meaningful message for given transfer result.
+ *
+ * @param result libusb submit transfer return code.
+ * @return o3000 error code translation (see @ref o3000.h)
+ */
+static int print_transfer_result(struct o3000_session_t *session, int result) {
+
+ int error_code = O3000_ERROR_OTHER;
+
+ switch(result) {
+ case 0:
+ o3000_log(session, O3000_LOG_DEBUG, "%s: transfer successfully completed\n", __func__);
+ error_code = O3000_SUCCESS;
+ break;
+
+ case LIBUSB_ERROR_NO_DEVICE:
+ o3000_log(session, O3000_LOG_ERROR, "%s: device has been disconnected\n", __func__);
+ error_code = O3000_ERROR_NODEV;
+ break;
+
+ case LIBUSB_ERROR_BUSY:
+ o3000_log(session, O3000_LOG_ERROR, "%s: transfer has already been submitted\n", __func__);
+ error_code = O3000_ERROR_BUSY;
+ break;
+
+ case LIBUSB_ERROR_NOT_SUPPORTED:
+ o3000_log(session, O3000_LOG_ERROR, "%s: transfer flags not supported by the OS\n", __func__);
+ break;
+
+ default:
+ o3000_log(session, O3000_LOG_ERROR, "%s: other error (code %d)\n",__func__, result);
+ }
+ return error_code;
+}
+
+
+/**
+ * Print a meaningful message for given transfer status.
+ *
+ * @param status libusb status code
+ */
+static void print_transfer_status(struct o3000_session_t *session, enum libusb_transfer_status status) {
+ switch(status) {
+ case LIBUSB_TRANSFER_COMPLETED:
+ o3000_log(session, O3000_LOG_DEBUG, "%s: transfer successfully completed\n", __func__);
+ break;
+ case LIBUSB_TRANSFER_ERROR:
+ o3000_log(session, O3000_LOG_DEBUG, "%s: Error: transfer failed\n", __func__);
+ break;
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ o3000_log(session, O3000_LOG_DEBUG, "transfer timed out\n", __func__);
+ break;
+ case LIBUSB_TRANSFER_CANCELLED:
+ o3000_log(session, O3000_LOG_DEBUG, "transfer was cancelled\n", __func__);
+ break;
+ case LIBUSB_TRANSFER_STALL:
+ o3000_log(session, O3000_LOG_DEBUG, "%s: endpoint stalled\n", __func__);
+ break;
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ o3000_log(session, O3000_LOG_DEBUG, "%s: device has been disconnected\n", __func__);
+ break;
+ case LIBUSB_TRANSFER_OVERFLOW:
+ o3000_log(session, O3000_LOG_DEBUG, "%s: device sent more data than requested\n", __func__);
+ break;
+ default:
+ o3000_log(session, O3000_LOG_DEBUG, "%s: Error: unknown\n", __func__);
+ }
+}
+
+
+/**
+ * Check whether all transfers are cleanuped (freed).
+ * This includes all video transfers and the XML transfer.
+ *
+ * @param session current session
+ * @return FALSE if any transfer is pending for cleaning up; TRUE if all transfers are cleanuped
+ */
+static int allTransfersCleanup(struct o3000_session_t *session) {
+ int i;
+
+ if(session->transfer_xml_in != NULL) {
+ return FALSE;
+ }
+
+ for(i = 0; i < NUM_USB_VIDEO_TRANSFER; i++) {
+ if(session->transfer_data[i] != NULL) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+/**
+ * Cleanup current USB transfer (XML or video transfer).
+ *
+ * @param transfer current transfer to clean up
+ */
+static void cleanup_transfer(struct libusb_transfer *transfer) {
+
+ int i;
+ struct o3000_session_t *session = transfer->user_data;
+
+
+ session->cleanup_transfers = TRUE;
+
+ // print transfer status for debugging purposes
+ print_transfer_status(session, transfer->status);
+
+
+ if(transfer == session->transfer_xml_in) {
+ // release USB XML transfer
+ if(session->transfer_xml_in != NULL) {
+ libusb_free_transfer(session->transfer_xml_in);
+ o3000_log(session, O3000_LOG_INFO, "%s: cleanup XML transfer (%p)\n", __func__, session->transfer_xml_in);
+ session->transfer_xml_in = NULL;
+ transfer = NULL;
+ }
+ }
+ else {
+ // release USB video transfer
+ for(i = 0; i < NUM_USB_VIDEO_TRANSFER; i++) {
+ if(session->transfer_data[i] == transfer) {
+ libusb_free_transfer(session->transfer_data[i]);
+ o3000_log(session, O3000_LOG_INFO, "%s: cleanup video transfer %d (%p)\n", __func__, i, session->transfer_data[i]);
+ session->transfer_data[i] = NULL;
+ transfer = NULL;
+ break;
+ }
+ }
+ }
+
+ if(transfer != NULL) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: FIXME: This transfer %p should be cleaned!\n", __func__, transfer);
+ }
+
+ // reset running flag only when all transfers are cleaned up
+ if(allTransfersCleanup(session)) {
+ o3000_log(session, O3000_LOG_INFO, "%s: all USB transfers are cleaned up\n", __func__);
+ session->running = FALSE;
+ }
+}
+
+
+/**
+ * Transfer done callback for inbound XML.
+ *
+ * @note Transfer done only means submitted, not actually completed!
+ *
+ * @param transfer Pointer to XML transfer which has been submitted
+ */
+static void LIBUSB_CALL cb_xml_in_xfer_done(struct libusb_transfer *transfer) {
+
+ int retval;
+ struct o3000_session_t *session = transfer->user_data;
+
+
+ if(transfer->status == LIBUSB_TRANSFER_COMPLETED) {
+
+ // check whether string length is out of range
+ if(transfer->actual_length < 0 || transfer->actual_length >= XML_IN_BUF_SIZE) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: invalid XML message length (len = %d)\n", __func__, transfer->actual_length);
+ }
+ else {
+ session->xml_in_buf[transfer->actual_length] = '\0';
+ session->xml_cb(session->id, session->xml_in_buf, transfer->actual_length);
+ }
+
+ // resubmit transfer only if driver isn't cleaning up
+ if(session->cleanup_transfers == FALSE) {
+ retval = libusb_submit_transfer(session->transfer_xml_in);
+ if(retval != 0) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: resubmit XML transfer failed (code %d) --> cleanup\n", __func__, retval);
+ print_transfer_result(session, retval);
+ cleanup_transfer(transfer);
+ }
+ }
+ else {
+ o3000_log(session, O3000_LOG_INFO, "%s: cleaning up in progress\n", __func__);
+ cleanup_transfer(transfer);
+ }
+ }
+ else {
+ /*
+ * USB transfer is not completed!
+ * Do only clean up the transfer if the device has been disconnected or cleaning-up flag is set.
+ * Otherwise skip error and resubmit current transfer.
+ */
+ if(transfer->status == LIBUSB_TRANSFER_NO_DEVICE || session->cleanup_transfers == TRUE) {
+ o3000_log(session, O3000_LOG_INFO, "%s: cleaning up in progress (%s), no device (%s)\n",
+ __func__, session->cleanup_transfers == TRUE ? "yes":"no", transfer->status == LIBUSB_TRANSFER_NO_DEVICE ? "yes":"no");
+ cleanup_transfer(transfer);
+ }
+ else {
+ retval = libusb_submit_transfer(session->transfer_xml_in);
+ if(retval != 0) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: Could resubmit XML transfer (code %d)\n", __func__, retval);
+ print_transfer_result(session, retval);
+ cleanup_transfer(transfer);
+ }
+ }
+ }
+}
+
+
+/**
+ * Transfer done callback for video data.
+ *
+ * @note Transfer done only means submitted, not actually completed!
+ *
+ * @param transfer Pointer to transfer which just has been submitted
+ */
+static void LIBUSB_CALL cb_video_xfer_done(struct libusb_transfer *transfer) {
+
+ int retval;
+ struct o3000_session_t *session = transfer->user_data;
+
+
+ if(transfer->status == LIBUSB_TRANSFER_COMPLETED) {
+
+ if(transfer->actual_length > 0) {
+ handle_transfer(session, (uint8_t*)(transfer->buffer), transfer->actual_length);
+ }
+
+ // resubmit transfer only if driver is not exiting
+ if(session->cleanup_transfers == FALSE) {
+ retval = libusb_submit_transfer(transfer);
+ if(retval != 0) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: Could resubmit video transfer (code %d)\n", __func__, retval);
+ print_transfer_result(session, retval);
+ cleanup_transfer(transfer);
+ }
+ }
+ else {
+ o3000_log(session, O3000_LOG_INFO, "%s: cleaning up in progress\n", __func__);
+ cleanup_transfer(transfer);
+ }
+ }
+ else {
+ /*
+ * USB transfer is not completed!
+ * Do only clean up the transfer if the device has been disconnected or cleaning-up flag is set.
+ * Otherwise skip error and resubmit current transfer.
+ */
+ if(transfer->status == LIBUSB_TRANSFER_NO_DEVICE || session->cleanup_transfers == TRUE) {
+ o3000_log(session, O3000_LOG_INFO, "%s: cleaning up in progress (%s), no device (%s)\n",
+ __func__, session->cleanup_transfers == TRUE ? "yes":"no", transfer->status == LIBUSB_TRANSFER_NO_DEVICE ? "yes":"no");
+ cleanup_transfer(transfer);
+ }
+ else {
+ retval = libusb_submit_transfer(transfer);
+ if(retval != 0) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: Could resubmit video transfer (code %d)\n", __func__, retval);
+ print_transfer_result(session, retval);
+ cleanup_transfer(transfer);
+ }
+ }
+ }
+}
+
+
+/**
+ * Prepare USB transfers to receive video data and XML messages from device.
+ *
+ * @param session session pointer
+ * @return 0 on success, or error code (see @ref o3000.h)
+ */
+static int prepare_usb_transfers(struct o3000_session_t *session) {
+
+ int i, retval;
+ struct libusb_transfer *new_transfer;
+ int error_code = O3000_ERROR_OTHER;
+ uint8_t *xfer_buf;
+
+
+ // paranoia check
+ if(session->transfer_data == NULL) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: FIXME: pointer array used for video transfer is not allocated!\n", __func__);
+ return O3000_ERROR_OTHER;
+ }
+
+ /*
+ * setup video transfer array
+ */
+ for(i = 0; i < NUM_USB_VIDEO_TRANSFER; i++) {
+
+ // Allocate transfer
+ new_transfer = libusb_alloc_transfer(0);
+ if(new_transfer == NULL) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: Could not allocate video transfer %d\n", __func__, i);
+ error_code = O3000_ERROR_NOMEM;
+ goto _prepare_usb_transfer_abort;
+ }
+ session->transfer_data[i] = new_transfer;
+
+ // Video transfers are using the same callback but with own user data pointer pointing to current session definition.
+ xfer_buf = session->video_cache + i*(session->video_chunk_size);
+ libusb_fill_bulk_transfer(new_transfer, session->dev, CAM_IN_EP, xfer_buf, session->video_chunk_size, cb_video_xfer_done, session, 0);
+
+ // Submit transfer
+ retval = libusb_submit_transfer(new_transfer);
+ if(retval != 0) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: Could not submit video transfer %d (code %d)\n", __func__, i, retval);
+ error_code = print_transfer_result(session, retval);
+ goto _prepare_usb_transfer_abort;
+ }
+ o3000_log(session, O3000_LOG_DEBUG, "%s: video transfer %d allocated and submitted (%p)\n", __func__, i, new_transfer);
+ }
+
+ /*
+ * setup single XML transfer
+ */
+ new_transfer = libusb_alloc_transfer(0);
+ if(new_transfer == NULL) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: Could not allocate XML transfer\n", __func__);
+ error_code = O3000_ERROR_NOMEM;
+ goto _prepare_usb_transfer_abort;
+ }
+ session->transfer_xml_in = new_transfer;
+
+ // prepare and submit transfer
+ libusb_fill_bulk_transfer(new_transfer, session->dev, XML_IN_EP, (unsigned char*)(session->xml_in_buf), XML_IN_BUF_SIZE, cb_xml_in_xfer_done, session, 0);
+ retval = libusb_submit_transfer(new_transfer);
+ if(retval != 0) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: Could not submit XML transfer (code %d)\n", __func__, retval);
+ error_code = print_transfer_result(session, retval);
+ goto _prepare_usb_transfer_abort;
+ }
+
+ o3000_log(session, O3000_LOG_DEBUG, "%s: XML transfer allocated and submitted (%p)\n", __func__, new_transfer);
+ return 0;
+
+
+_prepare_usb_transfer_abort:
+ return error_code;
+}
+
+
+/**
+ * Send XML message to device.
+ *
+ * @param session current session
+ * @param msg message content
+ * @param msg_len message content length
+ * @return 0 on success, or error code (see @ref o3000.h)
+ */
+static int send_xml(struct o3000_session_t *session, const char *msg, int msg_len) {
+
+ int xml_msg_transferred, retval, ret_code;
+
+ // use timeout of 1 second
+ ret_code = 0;
+ retval = libusb_bulk_transfer(session->dev, XML_OUT_EP, (unsigned char*)msg, msg_len, &xml_msg_transferred, 1000);
+ if(retval != 0) {
+ if(retval == LIBUSB_ERROR_TIMEOUT) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: sending XML message failed (timeout)\n", __func__, retval);
+ ret_code = O3000_ERROR_USB_TRANSFER_TIMEOUT;
+ }
+ else if(retval == LIBUSB_ERROR_PIPE) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: sending XML message failed (pipe error)\n", __func__, retval);
+ ret_code = O3000_ERROR_USB_EP_HALTED;
+ }
+ else if(retval == LIBUSB_ERROR_NO_DEVICE) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: sending XML message failed (no device)\n", __func__, retval);
+ ret_code = O3000_ERROR_NODEV;
+ }
+ else {
+ o3000_log(session, O3000_LOG_ERROR, "%s: sending XML message failed (code %d)\n", __func__, retval);
+ ret_code = O3000_ERROR_OTHER;
+ }
+ }
+ else if(xml_msg_transferred != msg_len) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: less data are transfered to the device than expected (%d of %d bytes)\n", __func__, xml_msg_transferred, msg_len);
+ ret_code = O3000_ERROR_LESS_DATA;
+ }
+ return ret_code;
+}
+
+
+/**
+ * Send XML packet from host to device.
+ *
+ * @note Never call this function from the thread context o3000_connect() is called from!
+ *
+ * @param id session ID
+ * @param msg message content
+ * @param msg_len message content length
+ * @return 0 on success, or error code (see @ref o3000.h)
+ */
+int __stdcall o3000_send_xml(int id, const char *msg, int msg_len) {
+
+ struct o3000_session_t *session;
+
+ session = get_session(id);
+ if(session == NULL) {
+ return O3000_ERROR_INVALID_SESSION_ID;
+ }
+
+ if(msg == NULL) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: no message defined\n", __func__);
+ return O3000_ERROR_OTHER;
+ }
+
+ if(msg_len <= 0) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: invalid message length %d\n", __func__, msg_len);
+ return O3000_ERROR_OTHER;
+ }
+
+ if(session->running == FALSE) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: driver is not running\n", __func__);
+ return O3000_ERROR_DRV_NOT_CONNECTED;
+ }
+
+ if(session->cleanup_transfers == TRUE) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: driver is cleaning up\n", __func__);
+ return O3000_ERROR_DRV_NOT_CONNECTED;
+ }
+
+ return (send_xml(session, msg, msg_len));
+}
+
+
+/**
+ * Disconnect a currently claimed USB connection.
+ * Use this function if the driver is connected to an USB device. The o3000_connect() function-call is blocking now.
+ * After disconnecting, the function o3000_connect() returns. The session is not cleaned-up due to the driver is ready the reconnect
+ * to the same USB device by calling o3000_connect() again.
+ *
+ * @param id session ID
+ * @return 0 on success, or error code defined at @ref o3000.h.
+ */
+int __stdcall o3000_disconnect(int id) {
+
+ struct o3000_session_t *session;
+ int retval;
+
+
+ session = get_session(id);
+ if(session == NULL) {
+ return O3000_ERROR_INVALID_SESSION_ID;
+ }
+
+ if(session->running == FALSE) {
+ o3000_log(session, O3000_LOG_WARNING, "%s: driver is not connected!\n", __func__);
+ return O3000_ERROR_DRV_NOT_CONNECTED;
+ }
+
+ o3000_log(session, O3000_LOG_DEBUG, "%s: disconnect USB device (release interface)\n", __func__);
+ session->cleanup_transfers = TRUE;
+ session->disconnect_dev = TRUE;
+ retval = libusb_release_interface(session->dev, 0);
+ if(retval == LIBUSB_ERROR_NOT_FOUND) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: releasing interface failed: interface not claimed\n", __func__);
+ }
+ else if(retval == LIBUSB_ERROR_NO_DEVICE) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: releasing interface failed: device has been disconnected\n", __func__);
+ }
+ else if(retval != 0) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: releasing interface failed (code %d)\n", __func__, retval);
+ }
+ return retval;
+}
+
+
+/**
+ * Estabslish a connection to an existing device.
+ * If several devices are available on the same system, the device number is used. The number of devices are requested
+ * by o3000_get_num_device().
+ *
+ * NOTE
+ * This function will block until the USB device is disconnected manually or the driver is stopped by calling o3000_exit().
+ *
+ * @param id session ID
+ * @param device_nr device number to connect (use 0 to connect to first available device)
+ * @param config_msg configuration message to be sent to device after successful connection, or NULL if not used
+ * @param config_msg_len configuration message length
+ * @return 0 is returned only after calling dmm_exit() or dmm_disconnect(). Otherwise the error code defined at @ref o3000.h.
+ */
+int __stdcall o3000_connect(int id, int device_nr, char *config_msg, int config_msg_len) {
+
+ struct o3000_session_t *session;
+ int ret, i, dev_cnt, completed;
+ libusb_device *device;
+ libusb_device_handle *handle;
+ int error_code = 0;
+
+
+ session = get_session(id);
+ if(session == NULL) {
+ return O3000_ERROR_INVALID_SESSION_ID;
+ }
+
+ // reset all flags here
+ session->cleanup_transfers = FALSE;
+ session->disconnect_dev = FALSE;
+ session->release_session = FALSE;
+
+ // check whether device list is defined
+ if(session->device_list == NULL) {
+ ret = o3000_device_discovery(id);
+ if(ret < 0) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: device discovery failed\n", __func__);
+ return ret;
+ }
+ }
+
+ // look for device in question
+ dev_cnt = 0;
+ device = NULL;
+ for(i = 0; i < session->num_device_list; i++) {
+ if(is_camera(session, session->device_list[i]) == TRUE) {
+ if(device_nr == dev_cnt) {
+ device = session->device_list[i];
+ break;
+ }
+ dev_cnt++;
+ }
+ }
+
+ if(device == NULL) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: USB device not available\n", __func__);
+ error_code = O3000_ERROR_NODEV;
+ goto _connect_abort;
+ }
+
+ // now open the device
+ ret = libusb_open(device, &handle);
+ if(ret != 0) {
+ if(ret == LIBUSB_ERROR_NO_MEM) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: opening device failed: no memory\n", __func__);
+ error_code = O3000_ERROR_NOMEM;
+ }
+ else if(ret == LIBUSB_ERROR_ACCESS) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: opening device failed: no permission\n", __func__);
+ error_code = O3000_ERROR_ACCESS;
+ }
+ else if(ret == LIBUSB_ERROR_NO_DEVICE) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: opening device failed: no such device\n", __func__);
+ error_code = O3000_ERROR_NODEV;
+ }
+ else {
+ o3000_log(session, O3000_LOG_ERROR, "%s: opening device failed: other\n", __func__);
+ error_code = O3000_ERROR_OTHER;
+ }
+ goto _connect_abort;
+ }
+ session->dev = handle;
+
+ // now claim the interface
+ ret = libusb_claim_interface(handle, 0);
+ if(ret != 0) {
+ if(ret == LIBUSB_ERROR_NOT_FOUND) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: claiming device failed: requested device does not exist\n", __func__);
+ error_code = O3000_ERROR_NODEV;
+ }
+ else if(ret == LIBUSB_ERROR_BUSY) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: claiming device failed: device already in use\n", __func__);
+ error_code = O3000_ERROR_BUSY;
+ }
+ else if(ret == LIBUSB_ERROR_NO_DEVICE) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: claiming device failed: device has been disconnected\n", __func__);
+ error_code = O3000_ERROR_NODEV;
+ }
+ else {
+ o3000_log(session, O3000_LOG_ERROR, "%s: claiming device failed:other\n", __func__);
+ error_code = O3000_ERROR_OTHER;
+ }
+ goto _connect_abort;
+ }
+
+ // setup all USB transfers (video and XML)
+ if(prepare_usb_transfers(session)) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: preparing USB transfers failed\n", __func__);
+ error_code = O3000_ERROR_OTHER;
+ goto _connect_abort;
+ }
+
+ /*
+ * Send configuration message to device if available
+ */
+ if(config_msg != NULL && config_msg_len > 0) {
+ o3000_log(session, O3000_LOG_INFO, "%s: send configuration string\n", __func__);
+ ret = send_xml(session, config_msg, config_msg_len);
+ if(ret != 0) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: sending configuration string failed (code %d)\n", __func__, ret);
+ }
+ }
+
+ /*
+ * Finally enter main-loop
+ */
+ o3000_log(session, O3000_LOG_INFO, "%s: enter blocking loop for event handling\n", __func__);
+ session->running = TRUE;
+ while(session->running == TRUE) {
+ completed = 0;
+ ret = libusb_handle_events_completed(session->libusb_ctx, &completed);
+ if(ret != 0) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: USB event handling error (code %d)\n", __func__, ret);
+ break;
+ }
+ }
+
+
+ /*
+ * There are following reasons to get here:
+ *
+ * 1. o3000_disconnect() was called
+ * 2. o3000_exit() was called
+ * 3. the device isn't available anymore because it was unplugged, it does an USB hardware reset
+ *
+ * The current session is cleaned-up only if o3000_exit() has been called. Otherwise the session will further exist and a reconnection with o3000_connect() will be allowed.
+ * Actually, it doesn't make any sense to connect to an unavailable device. But to keep it simple, a session is cleaned-up only with o3000_exit() without any exception.
+ */
+ if(session->release_session == FALSE && session->disconnect_dev == FALSE) {
+ // device was unplugged (see reason 3 few lines above)
+ o3000_log(session, O3000_LOG_DEBUG, "%s: release interface\n", __func__);
+ ret = libusb_release_interface(session->dev, 0);
+ if(ret != 0) {
+ o3000_log(session, O3000_LOG_ERROR, "%s: releasing interface failed (error code %d)\n", __func__, ret);
+ }
+ ret = O3000_ERROR_NODEV;
+ }
+
+ // close USB device
+ if(session->dev != NULL) {
+ o3000_log(session, O3000_LOG_DEBUG, "%s: close USB device handler\n", __func__);
+ libusb_close(session->dev);
+ session->dev = NULL;
+ }
+
+ // release session due to o3000_exit() call
+ if(session->release_session == TRUE) {
+ o3000_log(session, O3000_LOG_DEBUG, "%s: cleanup session\n", __func__);
+ session->release_session = FALSE;
+ cleanup(id);
+ }
+ return ret;
+
+_connect_abort:
+ // close USB device
+ if(session->dev != NULL) {
+ o3000_log(session, O3000_LOG_DEBUG, "%s: close USB device handler\n", __func__);
+ libusb_close(session->dev);
+ session->dev = NULL;
+ }
+ return error_code;
+}
+
+
+/**
+ * The device discovery returns the number of USB devices in question.
+ * In fact, the number of devices with the session PID/VID are counted. After knowing the number of devices, any connection can be
+ * established with o3000_connect().
+ *
+ * @param id valid session ID
+ * @return number of USB device or a negative return code
+ */
+int __stdcall o3000_device_discovery(int id) {
+
+ int num_device, i, num_camera;
+ struct o3000_session_t *session;
+
+
+ session = get_session(id);
+ if(session == NULL) {
+ return O3000_ERROR_INVALID_SESSION_ID;
+ }
+
+ // check whether device list is allocated already
+ if(session->device_list != NULL) {
+ libusb_free_device_list(session->device_list, 1);
+ session->device_list = NULL;
+ }
+
+ // get list with all USB device currently attached to the system
+ session->num_device_list = 0;
+ num_device = libusb_get_device_list(session->libusb_ctx, &(session->device_list));
+ o3000_log(session, O3000_LOG_DEBUG, "%s: %d USB connected to the system\n", __func__, num_device);
+
+ if(num_device < 0) {
+ o3000_log(session, O3000_LOG_WARNING, "%s: currently no USB device attached to the system (code %d)\n", __func__, num_device);
+ return O3000_ERROR_NODEV;
+ }
+ session->num_device_list = num_device;
+
+ // count number of devices in question
+ num_camera = 0;
+ for(i = 0; i < num_device; i++) {
+ if(is_camera(session, session->device_list[i]) == TRUE) {
+ num_camera++;
+ }
+ }
+
+ o3000_log(session, O3000_LOG_INFO, "%s: number of cameras found: %d\n", __func__, num_camera);
+ return num_camera;
+}
+
+
+/**
+ * Get number of connected O-3000 cameras to the system.
+ *
+ * This function is thread save and can be called without an already opened
+ * session.
+ *
+ * @return number of connected cameras or negative error code (see @ref o3000.h)
+ */
+int __stdcall o3000_get_num_cam(void) {
+
+ libusb_context *libusb_ctx;
+ libusb_device **device_list;
+ int num_device, num_camera, i;
+ struct libusb_device_descriptor desc;
+
+ if(libusb_init(&libusb_ctx)) {
+ return O3000_ERROR_OTHER;
+ }
+
+ num_device = libusb_get_device_list(libusb_ctx, &device_list);
+
+ if(num_device < 0) {
+ libusb_exit(libusb_ctx);
+ return O3000_ERROR_NODEV;
+ }
+
+ // count number of devices in question
+ num_camera = 0;
+ for(i = 0; i < num_device; i++) {
+ if(libusb_get_device_descriptor(device_list[i], &desc)) {
+ libusb_free_device_list(device_list, 1);
+ libusb_exit(libusb_ctx);
+ return O3000_ERROR_OTHER;
+ }
+ if((desc.idVendor != O3000_VID) || (desc.idProduct != O3000_PID)) {
+ continue;
+ }
+ num_camera++;
+ }
+
+ libusb_free_device_list(device_list, 1);
+ libusb_exit(libusb_ctx);
+ return num_camera;
+}
+
+
+/**
+ * Logging function.
+ *
+ * Concatenates level and message and forwards the resulting string to the logging function.
+ *
+ * @param session session pointer
+ * @param level The logging level (from @ref O3000_LOG_NONE to @ref O3000_LOG_DEBUG)
+ * @param fmt The format string
+ */
+void __stdcall o3000_log(struct o3000_session_t *session, const int level, const char *fmt, ...) {
+
+ va_list args;
+ char str[1024];
+ char *str_ptr = str;
+ int level_int;
+ int max_level;
+
+ if(level > session->loglevel) {
+ return; // ignore messages beyond session loglevel
+ }
+
+ if(session->log_cb == NULL) {
+ // session has no callback registered; ignore request
+ return;
+ }
+
+ if(fmt == NULL) {
+ return;
+ }
+
+ // check range of level and limit (clamp) it
+ max_level = sizeof(dbg_strlevel)/sizeof(char*)-1;
+ level_int = (level > max_level) ? max_level : level;
+
+ str_ptr += sprintf(str_ptr, "%s: ", dbg_strlevel[level_int]);
+
+ va_start(args, fmt);
+ vsprintf(str, fmt, args);
+ va_end(args);
+
+ session->log_cb(session->id, str);
+}
+
+
+/**
+ * Initialize O-3000 session.
+ * A new O-3000 session is initialized by calling this function. Several buffers are allocated and the USB context is setup.
+ * The driver automatically divides the video cache to @ref NUM_USB_VIDEO_TRANSFER USB transfers. The video cache size must be at least
+ * an image frame size. It's recommended that the size is more the 2 times the maximum expected image size.
+ *
+ * A suitable video cache size is calculated by following example:
+ * Maximum expected image resolution: 1280x960 with 2 bytes/pixel
+ * Recommended video cache size: 1280*960*2*3 = 3686400 bytes = 3.5 MByte (3 times the image size)
+ *
+ * NOTE
+ * This function must be called before connecting to any O-3000 camera.
+ *
+ * @param vid USB vendor ID; use default VID if 0
+ * @param pid USB product ID; use default PID if 0
+ * @param video_cache_size Requested video cache size (in bytes); note: might be adjusted by the driver
+ * @param xml_cb XML receive callback
+ * @param video_cb Video receive callback
+ * @param log_cb Logging callback, or NULL if not used
+ * @param loglevel logging level (from @ref O3000_LOG_NONE to @ref O3000_LOG_VERBOSE)
+ * @return positive session ID starting from 0, or error code (see @ref o3000.h)
+ */
+int __stdcall o3000_init(int vid, int pid, unsigned int video_cache_size,
+ void __stdcall (*xml_cb)(int id, char* buf, int len),
+ void __stdcall (*video_cb)(int id, unsigned char* buf, struct img_header_t* img_header),
+ void __stdcall (*log_cb)(int id, char* msg),
+ int loglevel) {
+
+ int i, new_session_id, ret;
+ struct o3000_session_t *new_session;
+ int error_code = O3000_ERROR_OTHER;
+ int transfer_size, total_buffer_size;
+
+
+ // Validate callbacks
+ if(xml_cb == NULL || video_cb == NULL) {
+ return O3000_ERROR_NOCALLBACK;
+ }
+
+ /*
+ * Look for next free session
+ * Session table access must be atmic
+ */
+ new_session_id = -1;
+ o3000_mutex_static_lock(&session_lock);
+ for(i = 0; i < MAX_NUM_SESSION; i++) {
+ if(session_table[i] == NULL) {
+ new_session_id = i;
+ break;
+ }
+ }
+
+ if(new_session_id == -1) {
+ return O3000_ERROR_NO_FREE_SESSION;
+ }
+
+ // allocate session and save session data
+ new_session = calloc(1, sizeof(struct o3000_session_t));
+ if(new_session == NULL) {
+ return O3000_ERROR_NOMEM;
+ }
+ session_table[new_session_id] = new_session;
+ o3000_mutex_static_unlock(&session_lock);
+
+ new_session->id = new_session_id;
+ new_session->vid = ((vid == 0) ? O3000_VID : vid); // set default if vid not set
+ new_session->pid = ((pid == 0) ? O3000_PID : pid); // set default if vid not set
+
+ if(loglevel < O3000_LOG_NONE || loglevel > O3000_LOG_VERBOSE) {
+ new_session->loglevel = O3000_LOG_NONE;
+ }
+ else{
+ new_session->loglevel = loglevel;
+ }
+
+ new_session->xml_cb = xml_cb;
+ new_session->video_cb = video_cb;
+ new_session->log_cb = log_cb;
+
+
+ // open libusb context
+ ret = libusb_init(&(new_session->libusb_ctx));
+ if(ret != 0) {
+ o3000_log(new_session, O3000_LOG_ERROR, "%s: initializing libusb context failed (code %d)\n", __func__, ret);
+ error_code = O3000_ERROR_OTHER;
+ goto _o3000_init_abort;
+ }
+
+ /*
+ * If loglevel is set to O3000_LOG_VERBOSE, do set libusb loglevel to debug.
+ * This will flood the logging handler and is useful for debugging purposes only!
+ */
+ if(loglevel == O3000_LOG_VERBOSE) {
+ libusb_set_debug(new_session->libusb_ctx, LIBUSB_LOG_LEVEL_DEBUG);
+ }
+ else {
+ libusb_set_debug(new_session->libusb_ctx, LIBUSB_LOG_LEVEL_NONE);
+ }
+
+ // allocate XML buffer
+ if((new_session->xml_in_buf = malloc(XML_IN_BUF_SIZE)) == NULL) {
+ o3000_log(new_session, O3000_LOG_ERROR, "%s: failed to allocate XML buffer\n", __func__);
+ error_code = O3000_ERROR_NOMEM;
+ goto _o3000_init_abort;
+ }
+
+
+ /*
+ * The image size must be larger or equal to the maximum image size ever to be expected. The
+ * video cache is divided in several adjacent bulk transfers.
+ */
+ if(video_cache_size < MIN_VIDEO_CACHE_SIZE) {
+ video_cache_size = MIN_VIDEO_CACHE_SIZE;
+ }
+
+
+ // calculate video cache
+ transfer_size = video_cache_size/NUM_USB_VIDEO_TRANSFER;
+ if(transfer_size % 512) {
+ /*
+ * Do adjust USB bulk transfer size to a multiple of 512 bytes which is
+ * equal to the bulk payload size (at high-speed).
+ */
+ transfer_size = ((transfer_size/512)+1)*512;
+ }
+ video_cache_size = NUM_USB_VIDEO_TRANSFER * transfer_size;
+
+ // calculate and allocate total buffer size
+ total_buffer_size = video_cache_size + MAX_IMAGE_SIZE;
+ if((new_session->video_cache = malloc(total_buffer_size)) == NULL) {
+ o3000_log(new_session, O3000_LOG_ERROR, "%s: failed to allocate video cache\n", __func__);
+ error_code = O3000_ERROR_NOMEM;
+ goto _o3000_init_abort;
+ }
+ new_session->frame_buf = new_session->video_cache + video_cache_size;
+ new_session->video_cache_size = video_cache_size;
+ new_session->video_chunk_size = transfer_size;
+ o3000_log(new_session, O3000_LOG_DEBUG, "%s: video cache %d bytes (divided into %d chunks of %d bytes)\n",
+ __func__, video_cache_size, NUM_USB_VIDEO_TRANSFER, transfer_size);
+
+ // allocate video transfer array
+ new_session->transfer_data = calloc(1, sizeof(struct libusb_transfer*)*NUM_USB_VIDEO_TRANSFER);
+ if(new_session->transfer_data == NULL) {
+ o3000_log(new_session, O3000_LOG_ERROR, "%s: allocating USB video transfers failed\n", __func__);
+ error_code = O3000_ERROR_NOMEM;
+ goto _o3000_init_abort;
+ }
+
+ o3000_log(new_session, O3000_LOG_INFO, "%s: new session initialized (ID %d, video cache %d bytes, video chunk %d x %d bytes)\n",
+ __func__, new_session_id, video_cache_size, NUM_USB_VIDEO_TRANSFER, new_session->video_chunk_size);
+
+ // finally return the valid session ID
+ return new_session_id;
+
+_o3000_init_abort:
+ cleanup(new_session_id);
+ return error_code;
+}
+
+
+/**
+ * Exit O-3000 session.
+ * This function closes an active session and releases all system resources.
+ *
+ * @param id session ID
+ */
+void __stdcall o3000_exit(int id) {
+
+ struct o3000_session_t *session;
+
+
+ session = get_session(id);
+ if(session == NULL) {
+ return;
+ }
+
+ if(session->running == FALSE) {
+ o3000_log(session, O3000_LOG_INFO, "%s: driver not at main-loop --> cleanup session\n", __func__, id);
+ cleanup(id);
+ }
+ else {
+ o3000_log(session, O3000_LOG_DEBUG, "%s: driver at main-loop --> release USB interface\n", __func__, id);
+ session->release_session = TRUE;
+ o3000_disconnect(id);
+ }
+}
+
+/** @} */
diff --git a/o3000.h b/o3000.h
new file mode 100644
index 0000000..b13b767
--- /dev/null
+++ b/o3000.h
@@ -0,0 +1,101 @@
+/**
+* @file o3000.h
+* @brief O-3000 Camera driver API
+* @author Patrick Brunner - brunner@stettbacher.ch
+* @author Patrick Roth - roth@stettbacher.ch
+* @version 1.1
+* @date 2016-03-01
+* @copyright 2012-2016 Stettbacher Signal Processing AG
+*
+* @remarks
+*
+* <PRE>
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+* </PRE>
+*
+*/
+
+#ifndef _O3000_H
+#define _O3000_H
+
+#include "o3000/o3000_portable.h"
+#include "o3000/image_header.h"
+
+/**
+ * O-3000 library version
+ */
+#define O3000_VERSION "2.0.2"
+
+#define O3000_VID 0x0483 ///< O3000 vendor ID
+#define O3000_PID 0xA098 ///< O3000 product ID
+
+
+/*
+ * All possible error codes
+ */
+#define O3000_SUCCESS 0 ///< success (no error)
+#define O3000_ERROR_NOCALLBACK -1 ///< one or more callback functions are missing
+#define O3000_ERROR_NOMEM -2 ///< failed to allocate memory
+#define O3000_ERROR_NODEV -3 ///< device not found
+#define O3000_ERROR_NO_FREE_SESSION -4 ///< all session are in used
+#define O3000_ERROR_INVALID_SESSION_ID -5 ///< session ID is invalid
+#define O3000_ERROR_ACCESS -6 ///< user has insufficient permissions
+#define O3000_ERROR_BUSY -7 ///< another program or driver has claimed the device
+#define O3000_ERROR_DRV_NOT_CONNECTED -8 ///< O-3000 driver is not running (call o3000_connect() first)
+#define O3000_ERROR_XML_XFER_RUNNING -9 ///< XML transfer to device in progress
+#define O3000_ERROR_USB_TRANSFER_TIMEOUT -10 ///< USB transfer timeout
+#define O3000_ERROR_USB_EP_HALTED -11 ///< USB endpoint halted
+#define O3000_ERROR_LESS_DATA -12 ///< USB received less data than expected
+
+#define O3000_ERROR_OTHER -1000 ///< other (unspecified) error
+
+
+/*
+ * All possible logging levels (don't touch it!!)
+ */
+#define O3000_LOG_NONE 0 ///< nothing is logged
+#define O3000_LOG_ERROR 1 ///< error message (top priority log message)
+#define O3000_LOG_WARNING 2 ///< warning message
+#define O3000_LOG_INFO 3 ///< info message
+#define O3000_LOG_DEBUG 4 ///< debug message
+#define O3000_LOG_VERBOSE 5 ///< verbose debug message, libusb is debugging too (lowest priority log message)
+
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+
+int __stdcall o3000_init(int vid, int pid, unsigned int video_cache_size,
+ void __stdcall (*xml_cb)(int id, char* buf, int len),
+ void __stdcall (*video_cb)(int id, unsigned char* buf, struct img_header_t* img_header),
+ void __stdcall (*log_cb)(int id, char* msg),
+ int loglevel);
+
+void __stdcall o3000_exit(int id);
+
+int __stdcall o3000_get_num_cam(void);
+int __stdcall o3000_device_discovery(int id);
+int __stdcall o3000_connect(int id, int device_nr, char *config_msg, int config_msg_len);
+int __stdcall o3000_disconnect(int id);
+
+int __stdcall o3000_send_xml(int id, const char *msg, int msg_len);
+
+
+#if defined(__cplusplus) || defined(c_plusplus)
+} // extern "C"
+#endif
+
+#endif // _O3000_H
diff --git a/o3000_portable.h b/o3000_portable.h
new file mode 100644
index 0000000..4e7da6a
--- /dev/null
+++ b/o3000_portable.h
@@ -0,0 +1,144 @@
+/**
+* @file o3000_portable.h
+* @brief O-3000 definitions to make portable platform-independent code
+* @author Patrick Roth - roth@stettbacher.ch
+* @author Christian Jaeggi - jaeggi@stettbacher.ch
+* @version 1.0
+* @date 2016-03-01
+* @copyright 2012-2016 Stettbacher Signal Processing AG
+*
+* @remarks
+*
+* <PRE>
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+* </PRE>
+*
+*/
+
+
+#ifndef _O3000_PORTABLE_H
+#define _O3000_PORTABLE_H
+
+
+/* stdint.h is not available on older MSVC */
+#if defined(_MSC_VER) && (_MSC_VER < 1600) && (!defined(_STDINT)) && (!defined(_STDINT_H))
+
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+
+#else
+
+#include <stdint.h>
+
+#endif // defined(_MSC_VER) && (_MSC_VER < 1600) && (!defined(_STDINT)) && (!defined(_STDINT_H))
+
+
+
+/*
+ * Calling conventions
+ * The System V ABI is one of the major ABIs in use today and is virtually universal among Unix
+ * systems. It is the calling convention used by toolchains such as i686-elf-gcc and x86_64-elf-gcc.
+ *
+ * Windows builds often use "project files" and are less transparent and more ad hoc than Makefiles.
+ * As an example, the driver crashes by calling a callback previously register via .NET. Therfore
+ * on MSVC, the calling ABI is defined. On other system (UNIX), the macros below are left
+ * empty.
+ *
+ * _WIN32 is always defined for Win32 or Win64 applications.
+ */
+#ifndef _WIN32
+
+#define __cdecl /* nothing */
+#define __stdcall /* nothing */
+#define __fastcall /* nothing */
+
+#else
+
+#include <windows.h>
+
+#endif // _WIN32
+
+
+/*
+ * aligned malloc functiions
+ */
+#ifndef _WIN32
+
+#define ALIGNED_ALLOC(align, size) aligned_alloc(align, size)
+#define ALIGNED_FREE(buf) free(buf)
+
+#else
+
+#include <malloc.h>
+#define ALIGNED_ALLOC(align, size) _aligned_malloc(size, align)
+#define ALIGNED_FREE(buf) _aligned_free(buf)
+
+#endif // _WIN32
+
+
+/*
+ * structure packing macros
+ */
+#if defined(_MSC_VER)
+
+#define __func__ __FUNCTION__
+#define __packed__ __packed
+
+#else
+
+/* define nothing for __func__ because it's used as default macro at source code */
+#define __packed__ __attribute__ ((packed))
+
+#endif // defined(_MSC_VER)
+
+
+/*
+ * mutexes
+ *
+ * copied from libusb project see files os/threads_windows.* and os/threads_poxix.*
+ */
+#if defined(_MSC_VER)
+
+#define O3000_MUTEX_INITIALIZER 0L
+typedef LONG o3000_mutex_static_t;
+static inline void o3000_mutex_static_lock(o3000_mutex_static_t *mutex) {
+ while (InterlockedExchange(mutex, 1L) == 1L)
+ SleepEx(0, TRUE);
+}
+static inline void o3000_mutex_static_unlock(o3000_mutex_static_t *mutex) {
+ InterlockedExchange(mutex, 0L);
+}
+
+#else
+
+#include <pthread.h>
+
+#define O3000_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+typedef pthread_mutex_t o3000_mutex_static_t;
+static inline void o3000_mutex_static_lock(o3000_mutex_static_t *mutex) {
+ (void)pthread_mutex_lock(mutex);
+}
+static inline void o3000_mutex_static_unlock(o3000_mutex_static_t *mutex) {
+ (void)pthread_mutex_unlock(mutex);
+}
+
+#endif // defined(_MSC_VER)
+
+
+#endif // _O3000_PORTABLE_H
+
+
diff --git a/o3000_private.h b/o3000_private.h
new file mode 100644
index 0000000..7087d46
--- /dev/null
+++ b/o3000_private.h
@@ -0,0 +1,102 @@
+/**
+* @file o3000_private.h
+* @brief O-3000 Camera driver API (internal)
+* @author Patrick Brunner - brunner@stettbacher.ch
+* @author Patrick Roth - roth@stettbacher.ch
+* @version 1.1
+* @date 2016-03-01
+* @copyright 2012-2016 Stettbacher Signal Processing AG
+*
+* @remarks
+*
+* <PRE>
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+* </PRE>
+*
+*/
+
+
+#ifndef __O3000_PRIVATE_H
+#define __O3000_PRIVATE_H
+
+#include <libusb-1.0/libusb.h>
+#include "o3000/o3000_portable.h"
+#include "o3000/image_header.h"
+
+
+#define TRUE 1 ///< TRUE value (not 0)
+#define FALSE 0 ///< FALSE value must be 0
+
+
+
+/**
+ * Image frame synchronization state
+ */
+enum image_frame_state_t {
+ IMG_FRAME_STATE_NOSYNC = 0, ///< not synchronized (preamble not found)
+ IMG_FRAME_STATE_SYNC, ///< synchronized (preamble found)
+};
+
+
+/**
+ * O-3000 session description
+ *
+ * Each session descripes a connection between the camera and the user application.
+ *
+ * @note These data are used internally and are not accessible from the user application.
+ */
+struct o3000_session_t {
+ int id; ///< session ID
+ libusb_context *libusb_ctx; ///< libusb context pointer
+ struct libusb_device_handle *dev; ///< the USB device handler (a connection to this device is established)
+ int running; ///< running flag: TRUE if driver is connected and at main-loop (function o3000_connect() is blocking)
+ int cleanup_transfers; ///< TRUE if driver is cleaning up all USB transfers
+ int disconnect_dev; ///< TRUE if the user wants to disconnect the USB device by software. This flag is set by o3000_disconnect().
+ int release_session; ///< TRUE indicates to release session when leaving main-loop (see function o3000_connect()). This flag is set by o3000_exit().
+
+ int vid; ///< USB vendor ID used for this session
+ int pid; ///< USB product ID used for this session
+ libusb_device **device_list; ///< USB device list with pid and vid defined above
+ int num_device_list; ///< number of entries at device list
+
+ int loglevel; ///< logging level (from @ref O3000_LOG_NONE to @ref O3000_LOG_DEBUG)
+
+ void __stdcall (*xml_cb)(int id, char* buf, int len); ///< XML callback
+ void __stdcall (*video_cb)(int id, unsigned char* buf, struct img_header_t* img_header); ///< video callback
+ void __stdcall (*log_cb)(int id, char* msg); //< logging callback
+
+
+ char *xml_in_buf; ///< buffer for inbound XML messages
+ uint8_t *video_cache; ///< video data buffer
+ int video_chunk_size; ///< chunk size of one video transfer in bytes
+ uint8_t *frame_buf; ///< start of frame buffer, memory-adjacent to video cache
+ unsigned int video_cache_size; ///< actual video cache size in bytes
+
+ struct libusb_transfer **transfer_data; ///< generic USB transfer structures for incoming video data (dyn. allocated array of transfers)
+ struct libusb_transfer *transfer_xml_in; ///< generic USB transfer structures for incoming XML messages
+
+ enum image_frame_state_t frame_state; ///< current image frame synchronization state
+ uint8_t *frame_start; ///< start address of image frame (in case if @ref IMG_FRAME_STATE_SYNC)
+ int frame_rx_cnt; ///< image frame payload counter, image received if @ref frame_payload_cnt is more or equal @ref frame_size
+ struct img_header_t *frame_img_header; ///< start address of current image header
+ uint8_t *image_start; ///< start address of pixel data
+ int frame_size; ///< image frame size (including image header)
+};
+
+
+
+void __stdcall o3000_log(struct o3000_session_t *session, const int level, const char *fmt, ...);
+
+#endif // __O3000_PRIVATE_H
diff --git a/o3000_xfer_handler.c b/o3000_xfer_handler.c
new file mode 100644
index 0000000..4ae8b2f
--- /dev/null
+++ b/o3000_xfer_handler.c
@@ -0,0 +1,175 @@
+/**
+* @file o3000_xfer_handler.c
+* @brief O-3000 USB transfer handler
+* @author Patrick Brunner - brunner@stettbacher.ch
+* @author Patrick Roth - roth@stettbacher.ch
+* @version 1.1
+* @date 2016-03-01
+* @copyright 2012-2016 Stettbacher Signal Processing AG
+*
+* @remarks
+*
+* <PRE>
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+* </PRE>
+*
+*/
+
+
+#include <string.h>
+
+#include "o3000/o3000.h"
+#include "o3000_private.h"
+
+
+/**
+ * The preamble, used to detect the beginning of a new frame
+ */
+static const uint8_t preamble[] = {0xaa, 0x55, 0xde, 0xad, 0xbe, 0xef, 0x55, 0xaa};
+
+/**
+ * Check whether the argument points to a valid preamble.
+ *
+ * @param p Memory pointer
+ * @return TRUE: points to valid preamble, FALSE: no valid preamble
+ */
+static int is_preamble(uint8_t *p) {
+ if( (*p == preamble[7]) &&
+ (*(p+1) == preamble[6]) &&
+ (*(p+2) == preamble[5]) &&
+ (*(p+3) == preamble[4]) &&
+ (*(p+4) == preamble[3]) &&
+ (*(p+5) == preamble[2]) &&
+ (*(p+6) == preamble[1]) &&
+ (*(p+7) == preamble[0])) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+
+/**
+ * Scan for the preamble in the buffer specified.
+ *
+ * @param buf Buffer start address
+ * @param size Buffer size
+ * @return preamble offset in bytes (w.r.t. to start address) or -1 if not found
+ */
+static int scan_for_preamble(uint8_t *buf, int size) {
+
+ int i, size_preamble, end;
+
+ size_preamble = sizeof(((struct img_header_t*)0)->preamble);
+ end = size - size_preamble;
+
+ for(i = 0; i < end; i++) {
+ if(is_preamble(buf+i) == TRUE) {
+ return i; // preamble found at offset i
+ }
+ }
+ return -1; // preamble not found
+}
+
+/**
+ * Video transfer handler
+ *
+ * This function recursively scans through a video transfer (video data chunk).
+ * Incomplete image frames are tracked continuously. Whenever an image frame is completed, the video callback handler is called.
+ *
+ * NOTE
+ * The video-cache consists of severalf memory-adjacent USB transfers plus an adjacent framebuffer. In case of a wrap-around
+ * the end of the image frame (located at the start of the video-cache) is copied to the framebuffer. So this frame won't be fragmented.
+ * In case of receiving short USB transfers, two image frames won't be adjacent at the video-cache. Therfore, after handling a image
+ * the state machine is set to not-synced state.
+ *
+ * @param session current session (is never NULL otherwise SEGFAULT)
+ * @param addr Start address of block to be scanned.
+ * @param len Lenght of block.
+ */
+void handle_transfer(struct o3000_session_t *session, uint8_t *addr, int len) {
+
+ int offset;
+ struct img_header_t *img_header; // points to current image header structure
+ int wraparound_chunk_size; // chunk size (bytes) at wrap-around to copy to frame buffer
+ int frame_data_remain; // remaining image frame data to process in bytes
+
+ if(session->frame_state == IMG_FRAME_STATE_NOSYNC) {
+ offset = scan_for_preamble(addr, len);
+ if(offset < 0) {
+ o3000_log(session, O3000_LOG_WARNING, "%s: preamble not found (start address=%p, len=%d)\n",
+ __func__, addr - session->video_cache, len);
+ return;
+ }
+
+ // preamble found, we are synchronized now!
+ session->frame_state = IMG_FRAME_STATE_SYNC;
+ session->frame_start = addr + offset; // set start address of image frame
+ session->frame_rx_cnt = 0; // reset data counter
+ len -= offset; // skip image data located before preamble
+
+ o3000_log(session, O3000_LOG_DEBUG, "%s: preamble found at address offset %d, remaining data of current image frame %d bytes\n",
+ __func__, session->frame_start - session->video_cache, len);
+
+ // parse image header
+ img_header = (struct img_header_t*)(session->frame_start);
+ session->frame_img_header = img_header;
+ session->image_start = session->frame_start + IMAGE_HEADER_SIZE + img_header->image_start;
+ session->frame_size = img_header->payload_size + IMAGE_HEADER_SIZE;
+ }
+
+
+ if(session->frame_state == IMG_FRAME_STATE_SYNC) {
+ session->frame_rx_cnt += len;
+
+ o3000_log(session, O3000_LOG_DEBUG, "%s: sync: %d bytes of %d bytes (image size) received\n",
+ __func__, session->frame_rx_cnt, session->frame_size);
+
+ // checking whether whole image frame is received
+ if(session->frame_rx_cnt >= session->frame_size) {
+
+ o3000_log(session, O3000_LOG_DEBUG, "%s: sync: image frame received\n", __func__);
+
+ if(session->frame_start > addr) {
+ /*
+ * We have a wrap-around to resolve. Do copy part of the image frame which resides at the
+ * start of the video cache to the frame buffer. Note that the frame buffer is memory-adjacent
+ * to the video cache.
+ */
+ wraparound_chunk_size = session->frame_size - (session->frame_buf - session->frame_start);
+ o3000_log(session, O3000_LOG_DEBUG, "%s: wrap-around, copy %d bytes to frame buffer\n", __func__, wraparound_chunk_size);
+ memcpy(session->frame_buf, session->video_cache, wraparound_chunk_size);
+ }
+
+ // delegate image to overlaying application
+ session->video_cb(session->id, session->image_start, session->frame_img_header);
+
+ /*
+ * Image frame is processed.
+ * The following image may won't be memory-adjacent to this one. Because a snapshot
+ * is sent with zero packet. The easiest way is to change state to NOT SYNC to trigger
+ * syncronization part again.
+ */
+ session->frame_state = IMG_FRAME_STATE_NOSYNC;
+ frame_data_remain = session->frame_rx_cnt - session->frame_size;
+ o3000_log(session, O3000_LOG_DEBUG, "%s: sync: %d bytes remaining from next image frame\n", __func__, frame_data_remain);
+ if(frame_data_remain > 0) {
+ handle_transfer(session, addr+(len-frame_data_remain), frame_data_remain);
+ }
+ }
+ }
+}
+
diff --git a/o3000_xfer_handler.h b/o3000_xfer_handler.h
new file mode 100644
index 0000000..09fdb3b
--- /dev/null
+++ b/o3000_xfer_handler.h
@@ -0,0 +1,39 @@
+/**
+* @file o3000_xfer_handler.h
+* @brief O-3000 USB transfer handler
+* @author Patrick Brunner - brunner@stettbacher.ch
+* @author Patrick Roth - roth@stettbacher.ch
+* @version 1.1
+* @date 2016-03-01
+* @copyright 2012-2016 Stettbacher Signal Processing AG
+*
+* @remarks
+*
+* <PRE>
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+* </PRE>
+*
+*/
+
+
+#ifndef __O3000_XFER_HANDLER_H
+#define __O3000_XFER_HANDLER_H
+
+#include "o3000/o3000_portable.h"
+#include "o3000_private.h"
+
+void handle_transfer(struct o3000_session_t *session, uint8_t *addr, int len);
+
+#endif // __O3000_XFER_HANDLER_H