From 1d578d5b0e883fd7e033c6426a8a516a4121ebbd Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 25 Jun 2013 14:27:13 -0700 Subject: [PATCH] Lots of Kinect bits. --- cmake/modules/FindFreenect.cmake | 44 + cmake/modules/FindLibUSB.cmake | 44 + cmake/modules/FindSkeltrack.cmake | 44 + interface/CMakeLists.txt | 11 +- interface/external/LibUSB/include/libusb.h | 1443 ++++++++++++ interface/external/LibUSB/include/libusbi.h | 935 ++++++++ .../external/LibUSB/include/os/darwin_usb.h | 169 ++ .../external/LibUSB/include/os/linux_usbfs.h | 139 ++ .../external/LibUSB/include/os/poll_posix.h | 10 + .../external/LibUSB/include/os/poll_windows.h | 115 + .../LibUSB/include/os/threads_posix.h | 48 + .../LibUSB/include/os/threads_windows.h | 86 + .../external/LibUSB/include/os/windows_usb.h | 608 +++++ interface/external/LibUSB/include/version.h | 18 + .../external/LibUSB/lib/UNIX/libusb-1.0.a | Bin 0 -> 372942 bytes .../external/LibUSB/lib/UNIX/libusb-1.0.la | 41 + .../freenect/include/libfreenect-audio.h | 115 + .../include/libfreenect-registration.h | 126 + .../external/freenect/include/libfreenect.h | 619 +++++ .../external/freenect/lib/UNIX/libfreenect.a | Bin 0 -> 65072 bytes .../freenect/lib/UNIX/libfreenect_sync.a | Bin 0 -> 12952 bytes interface/external/skeltrack/AUTHORS | 1 + interface/external/skeltrack/CMakeLists.txt | 14 + interface/external/skeltrack/COPYING | 165 ++ .../skeltrack/include/skeltrack-joint.h | 90 + .../skeltrack/include/skeltrack-skeleton.h | 95 + .../skeltrack/include/skeltrack-smooth.h | 36 + .../skeltrack/include/skeltrack-util.h | 127 ++ .../external/skeltrack/include/skeltrack.h | 28 + .../skeltrack/lib/UNIX/libskeltrack.a | Bin 0 -> 53760 bytes .../external/skeltrack/src/skeltrack-joint.c | 159 ++ .../skeltrack/src/skeltrack-skeleton.c | 2027 +++++++++++++++++ .../external/skeltrack/src/skeltrack-smooth.c | 226 ++ .../external/skeltrack/src/skeltrack-util.c | 626 +++++ interface/src/Webcam.cpp | 81 +- interface/src/Webcam.h | 6 + 36 files changed, 8268 insertions(+), 28 deletions(-) create mode 100644 cmake/modules/FindFreenect.cmake create mode 100644 cmake/modules/FindLibUSB.cmake create mode 100644 cmake/modules/FindSkeltrack.cmake create mode 100644 interface/external/LibUSB/include/libusb.h create mode 100644 interface/external/LibUSB/include/libusbi.h create mode 100644 interface/external/LibUSB/include/os/darwin_usb.h create mode 100644 interface/external/LibUSB/include/os/linux_usbfs.h create mode 100644 interface/external/LibUSB/include/os/poll_posix.h create mode 100644 interface/external/LibUSB/include/os/poll_windows.h create mode 100644 interface/external/LibUSB/include/os/threads_posix.h create mode 100644 interface/external/LibUSB/include/os/threads_windows.h create mode 100644 interface/external/LibUSB/include/os/windows_usb.h create mode 100644 interface/external/LibUSB/include/version.h create mode 100644 interface/external/LibUSB/lib/UNIX/libusb-1.0.a create mode 100644 interface/external/LibUSB/lib/UNIX/libusb-1.0.la create mode 100644 interface/external/freenect/include/libfreenect-audio.h create mode 100644 interface/external/freenect/include/libfreenect-registration.h create mode 100644 interface/external/freenect/include/libfreenect.h create mode 100644 interface/external/freenect/lib/UNIX/libfreenect.a create mode 100644 interface/external/freenect/lib/UNIX/libfreenect_sync.a create mode 100644 interface/external/skeltrack/AUTHORS create mode 100644 interface/external/skeltrack/CMakeLists.txt create mode 100644 interface/external/skeltrack/COPYING create mode 100644 interface/external/skeltrack/include/skeltrack-joint.h create mode 100644 interface/external/skeltrack/include/skeltrack-skeleton.h create mode 100644 interface/external/skeltrack/include/skeltrack-smooth.h create mode 100644 interface/external/skeltrack/include/skeltrack-util.h create mode 100644 interface/external/skeltrack/include/skeltrack.h create mode 100644 interface/external/skeltrack/lib/UNIX/libskeltrack.a create mode 100644 interface/external/skeltrack/src/skeltrack-joint.c create mode 100644 interface/external/skeltrack/src/skeltrack-skeleton.c create mode 100644 interface/external/skeltrack/src/skeltrack-smooth.c create mode 100644 interface/external/skeltrack/src/skeltrack-util.c diff --git a/cmake/modules/FindFreenect.cmake b/cmake/modules/FindFreenect.cmake new file mode 100644 index 0000000000..97adc4302a --- /dev/null +++ b/cmake/modules/FindFreenect.cmake @@ -0,0 +1,44 @@ +# Try to find the Freenect library to read from Kinect +# +# You must provide a FREENECT_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# FREENECT_FOUND - system found Freenect +# FREENECT_INCLUDE_DIRS - the Freenect include directory +# FREENECT_LIBRARIES - Link this to use Freenect +# +# Created on 6/25/2013 by Andrzej Kapolka +# Copyright (c) 2013 High Fidelity +# + +if (FREENECT_LIBRARIES AND FREENECT_INCLUDE_DIRS) + # in cache already + set(FREENECT_FOUND TRUE) +else (FREENECT_LIBRARIES AND FREENECT_INCLUDE_DIRS) + find_path(FREENECT_INCLUDE_DIRS libfreenect.h ${FREENECT_ROOT_DIR}/include) + + if (APPLE) + find_library(FREENECT_LIBRARIES libfreenect.a ${FREENECT_ROOT_DIR}/lib/MacOS/) + elseif (UNIX) + find_library(FREENECT_LIBRARIES libfreenect.a ${FREENECT_ROOT_DIR}/lib/UNIX/) + endif () + + if (FREENECT_INCLUDE_DIRS AND FREENECT_LIBRARIES) + set(FREENECT_FOUND TRUE) + endif (FREENECT_INCLUDE_DIRS AND FREENECT_LIBRARIES) + + if (FREENECT_FOUND) + if (NOT FREENECT_FIND_QUIETLY) + message(STATUS "Found Freenect: ${FREENECT_LIBRARIES}") + endif (NOT FREENECT_FIND_QUIETLY) + else (FREENECT_FOUND) + if (FREENECT_FIND_REQUIRED) + message(FATAL_ERROR "Could not find Freenect") + endif (FREENECT_FIND_REQUIRED) + endif (FREENECT_FOUND) + + # show the FREENECT_INCLUDE_DIRS and FREENECT_LIBRARIES variables only in the advanced view + mark_as_advanced(FREENECT_INCLUDE_DIRS FREENECT_LIBRARIES) + +endif (FREENECT_LIBRARIES AND FREENECT_INCLUDE_DIRS) diff --git a/cmake/modules/FindLibUSB.cmake b/cmake/modules/FindLibUSB.cmake new file mode 100644 index 0000000000..f9599752a1 --- /dev/null +++ b/cmake/modules/FindLibUSB.cmake @@ -0,0 +1,44 @@ +# Try to find the LibUSB library to interact with USB devices +# +# You must provide a LIBUSB_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# LIBUSB_FOUND - system found LibUSB +# LIBUSB_INCLUDE_DIRS - the LibUSB include directory +# LIBUSB_LIBRARIES - Link this to use LibUSB +# +# Created on 6/25/2013 by Andrzej Kapolka +# Copyright (c) 2013 High Fidelity +# + +if (LIBUSB_LIBRARIES AND LIBUSB_INCLUDE_DIRS) + # in cache already + set(LIBUSB_FOUND TRUE) +else (LIBUSB_LIBRARIES AND LIBUSB_INCLUDE_DIRS) + find_path(LIBUSB_INCLUDE_DIRS libusb.h ${LIBUSB_ROOT_DIR}/include) + + if (APPLE) + find_library(LIBUSB_LIBRARIES libusb-1.0.a ${LIBUSB_ROOT_DIR}/lib/MacOS/) + elseif (UNIX) + find_library(LIBUSB_LIBRARIES libusb-1.0.a ${LIBUSB_ROOT_DIR}/lib/UNIX/) + endif () + + if (LIBUSB_INCLUDE_DIRS AND LIBUSB_LIBRARIES) + set(LIBUSB_FOUND TRUE) + endif (LIBUSB_INCLUDE_DIRS AND LIBUSB_LIBRARIES) + + if (LIBUSB_FOUND) + if (NOT LIBUSB_FIND_QUIETLY) + message(STATUS "Found LibUSB: ${LIBUSB_LIBRARIES}") + endif (NOT LIBUSB_FIND_QUIETLY) + else (LIBUSB_FOUND) + if (LIBUSB_FIND_REQUIRED) + message(FATAL_ERROR "Could not find LibUSB") + endif (LIBUSB_FIND_REQUIRED) + endif (LIBUSB_FOUND) + + # show the LIBUSB_INCLUDE_DIRS and LIBUSB_LIBRARIES variables only in the advanced view + mark_as_advanced(LIBUSB_INCLUDE_DIRS LIBUSB_LIBRARIES) + +endif (LIBUSB_LIBRARIES AND LIBUSB_INCLUDE_DIRS) diff --git a/cmake/modules/FindSkeltrack.cmake b/cmake/modules/FindSkeltrack.cmake new file mode 100644 index 0000000000..a05e0c2990 --- /dev/null +++ b/cmake/modules/FindSkeltrack.cmake @@ -0,0 +1,44 @@ +# Try to find the Skeltrack library to perform skeleton tracking via depth camera +# +# You must provide a SKELTRACK_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# SKELTRACK_FOUND - system found Skeltrack +# SKELTRACK_INCLUDE_DIRS - the Skeltrack include directory +# SKELTRACK_LIBRARIES - Link this to use Skeltrack +# +# Created on 6/25/2013 by Andrzej Kapolka +# Copyright (c) 2013 High Fidelity +# + +if (SKELTRACK_LIBRARIES AND SKELTRACK_INCLUDE_DIRS) + # in cache already + set(SKELTRACK_FOUND TRUE) +else (SKELTRACK_LIBRARIES AND SKELTRACK_INCLUDE_DIRS) + find_path(SKELTRACK_INCLUDE_DIRS skeltrack.h ${SKELTRACK_ROOT_DIR}/include) + + if (APPLE) + find_library(SKELTRACK_LIBRARIES libskeltrack.a ${SKELTRACK_ROOT_DIR}/lib/MacOS/) + elseif (UNIX) + find_library(SKELTRACK_LIBRARIES libskeltrack.a ${SKELTRACK_ROOT_DIR}/lib/UNIX/) + endif () + + if (SKELTRACK_INCLUDE_DIRS AND SKELTRACK_LIBRARIES) + set(SKELTRACK_FOUND TRUE) + endif (SKELTRACK_INCLUDE_DIRS AND SKELTRACK_LIBRARIES) + + if (SKELTRACK_FOUND) + if (NOT SKELTRACK_FIND_QUIETLY) + message(STATUS "Found Skeltrack: ${SKELTRACK_LIBRARIES}") + endif (NOT SKELTRACK_FIND_QUIETLY) + else (SKELTRACK_FOUND) + if (SKELTRACK_FIND_REQUIRED) + message(FATAL_ERROR "Could not find Skeltrack") + endif (SKELTRACK_FIND_REQUIRED) + endif (SKELTRACK_FOUND) + + # show the SKELTRACK_INCLUDE_DIRS and SKELTRACK_LIBRARIES variables only in the advanced view + mark_as_advanced(SKELTRACK_INCLUDE_DIRS SKELTRACK_LIBRARIES) + +endif (SKELTRACK_LIBRARIES AND SKELTRACK_INCLUDE_DIRS) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 148e6819be..234f365687 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -13,6 +13,9 @@ set(PORTAUDIO_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/PortAudio) set(SPEEX_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Speex) set(OPENCV_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/OpenCV) set(UVCCAMERACONTROL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/UVCCameraControl) +set(LIBUSB_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/LibUSB) +set(FREENECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/freenect) +set(SKELTRACK_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/skeltrack) if (APPLE) set(GL_HEADERS "#include \n#include ") @@ -91,6 +94,9 @@ find_package(SpeexDSP REQUIRED) find_package(OpenCV) find_package(ZLIB) find_package(UVCCameraControl) +find_package(LibUSB) +find_package(Freenect) +find_package(Skeltrack) # include headers for interface and InterfaceConfig. include_directories( @@ -106,9 +112,12 @@ include_directories( ${LIBOVR_INCLUDE_DIRS} ${OPENCV_INCLUDE_DIRS} ${SPEEXDSP_INCLUDE_DIRS} + ${FREENECT_INCLUDE_DIRS} + ${SKELTRACK_INCLUDE_DIRS} ) -target_link_libraries(${TARGET_NAME} ${QT_LIBRARIES} ${OPENCV_LIBRARIES} ${ZLIB_LIBRARIES} ${SPEEXDSP_LIBRARIES}) +target_link_libraries(${TARGET_NAME} ${QT_LIBRARIES} ${OPENCV_LIBRARIES} ${ZLIB_LIBRARIES} ${SPEEXDSP_LIBRARIES} + ${FREENECT_LIBRARIES} ${SKELTRACK_LIBRARIES} ${LIBUSB_LIBRARIES}) if (APPLE) # link in required OS X frameworks and include the right GL headers diff --git a/interface/external/LibUSB/include/libusb.h b/interface/external/LibUSB/include/libusb.h new file mode 100644 index 0000000000..58b406f247 --- /dev/null +++ b/interface/external/LibUSB/include/libusb.h @@ -0,0 +1,1443 @@ +/* + * Public libusb header file + * Copyright (C) 2007-2008 Daniel Drake + * Copyright (c) 2001 Johannes Erdfelt + * + * 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 + */ + +#ifndef LIBUSB_H +#define LIBUSB_H + +#ifdef _MSC_VER +/* on MS environments, the inline keyword is available in C++ only */ +#define inline __inline +/* ssize_t is also not available (copy/paste from MinGW) */ +#ifndef _SSIZE_T_DEFINED +#define _SSIZE_T_DEFINED +#undef ssize_t +#ifdef _WIN64 + typedef __int64 ssize_t; +#else + typedef int ssize_t; +#endif /* _WIN64 */ +#endif /* _SSIZE_T_DEFINED */ +#endif /* _MSC_VER */ + +/* stdint.h is also not usually available on MS */ +#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; +#else +#include +#endif + +#include +#include +#include + +#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__) +#include +#endif + +/* 'interface' might be defined as a macro on Windows, so we need to + * undefine it so as not to break the current libusb API, because + * libusb_config_descriptor has an 'interface' member + * As this can be problematic if you include windows.h after libusb.h + * in your sources, we force windows.h to be included first. */ +#if defined(_WIN32) || defined(__CYGWIN__) +#include +#if defined(interface) +#undef interface +#endif +#endif + +/** \def LIBUSB_CALL + * \ingroup misc + * libusb's Windows calling convention. + * + * Under Windows, the selection of available compilers and configurations + * means that, unlike other platforms, there is not one true calling + * convention (calling convention: the manner in which parameters are + * passed to funcions in the generated assembly code). + * + * Matching the Windows API itself, libusb uses the WINAPI convention (which + * translates to the stdcall convention) and guarantees that the + * library is compiled in this way. The public header file also includes + * appropriate annotations so that your own software will use the right + * convention, even if another convention is being used by default within + * your codebase. + * + * The one consideration that you must apply in your software is to mark + * all functions which you use as libusb callbacks with this LIBUSB_CALL + * annotation, so that they too get compiled for the correct calling + * convention. + * + * On non-Windows operating systems, this macro is defined as nothing. This + * means that you can apply it to your code without worrying about + * cross-platform compatibility. + */ +/* LIBUSB_CALL must be defined on both definition and declaration of libusb + * functions. You'd think that declaration would be enough, but cygwin will + * complain about conflicting types unless both are marked this way. + * The placement of this macro is important too; it must appear after the + * return type, before the function name. See internal documentation for + * API_EXPORTED. + */ +#if defined(_WIN32) || defined(__CYGWIN__) +#define LIBUSB_CALL WINAPI +#else +#define LIBUSB_CALL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** \def libusb_cpu_to_le16 + * \ingroup misc + * Convert a 16-bit value from host-endian to little-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the host-endian value to convert + * \returns the value in little-endian byte order + */ +static inline uint16_t libusb_cpu_to_le16(const uint16_t x) +{ + union { + uint8_t b8[2]; + uint16_t b16; + } _tmp; + _tmp.b8[1] = x >> 8; + _tmp.b8[0] = x & 0xff; + return _tmp.b16; +} + +/** \def libusb_le16_to_cpu + * \ingroup misc + * Convert a 16-bit value from little-endian to host-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the little-endian value to convert + * \returns the value in host-endian byte order + */ +#define libusb_le16_to_cpu libusb_cpu_to_le16 + +/* standard USB stuff */ + +/** \ingroup desc + * Device and/or Interface Class codes */ +enum libusb_class_code { + /** In the context of a \ref libusb_device_descriptor "device descriptor", + * this bDeviceClass value indicates that each interface specifies its + * own class information and all interfaces operate independently. + */ + LIBUSB_CLASS_PER_INTERFACE = 0, + + /** Audio class */ + LIBUSB_CLASS_AUDIO = 1, + + /** Communications class */ + LIBUSB_CLASS_COMM = 2, + + /** Human Interface Device class */ + LIBUSB_CLASS_HID = 3, + + /** Physical */ + LIBUSB_CLASS_PHYSICAL = 5, + + /** Printer class */ + LIBUSB_CLASS_PRINTER = 7, + + /** Image class */ + LIBUSB_CLASS_PTP = 6, /* legacy name from libusb-0.1 usb.h */ + LIBUSB_CLASS_IMAGE = 6, + + /** Mass storage class */ + LIBUSB_CLASS_MASS_STORAGE = 8, + + /** Hub class */ + LIBUSB_CLASS_HUB = 9, + + /** Data class */ + LIBUSB_CLASS_DATA = 10, + + /** Smart Card */ + LIBUSB_CLASS_SMART_CARD = 0x0b, + + /** Content Security */ + LIBUSB_CLASS_CONTENT_SECURITY = 0x0d, + + /** Video */ + LIBUSB_CLASS_VIDEO = 0x0e, + + /** Personal Healthcare */ + LIBUSB_CLASS_PERSONAL_HEALTHCARE = 0x0f, + + /** Diagnostic Device */ + LIBUSB_CLASS_DIAGNOSTIC_DEVICE = 0xdc, + + /** Wireless class */ + LIBUSB_CLASS_WIRELESS = 0xe0, + + /** Application class */ + LIBUSB_CLASS_APPLICATION = 0xfe, + + /** Class is vendor-specific */ + LIBUSB_CLASS_VENDOR_SPEC = 0xff +}; + +/** \ingroup desc + * Descriptor types as defined by the USB specification. */ +enum libusb_descriptor_type { + /** Device descriptor. See libusb_device_descriptor. */ + LIBUSB_DT_DEVICE = 0x01, + + /** Configuration descriptor. See libusb_config_descriptor. */ + LIBUSB_DT_CONFIG = 0x02, + + /** String descriptor */ + LIBUSB_DT_STRING = 0x03, + + /** Interface descriptor. See libusb_interface_descriptor. */ + LIBUSB_DT_INTERFACE = 0x04, + + /** Endpoint descriptor. See libusb_endpoint_descriptor. */ + LIBUSB_DT_ENDPOINT = 0x05, + + /** HID descriptor */ + LIBUSB_DT_HID = 0x21, + + /** HID report descriptor */ + LIBUSB_DT_REPORT = 0x22, + + /** Physical descriptor */ + LIBUSB_DT_PHYSICAL = 0x23, + + /** Hub descriptor */ + LIBUSB_DT_HUB = 0x29, +}; + +/* Descriptor sizes per descriptor type */ +#define LIBUSB_DT_DEVICE_SIZE 18 +#define LIBUSB_DT_CONFIG_SIZE 9 +#define LIBUSB_DT_INTERFACE_SIZE 9 +#define LIBUSB_DT_ENDPOINT_SIZE 7 +#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define LIBUSB_DT_HUB_NONVAR_SIZE 7 + +#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define LIBUSB_ENDPOINT_DIR_MASK 0x80 + +/** \ingroup desc + * Endpoint direction. Values for bit 7 of the + * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme. + */ +enum libusb_endpoint_direction { + /** In: device-to-host */ + LIBUSB_ENDPOINT_IN = 0x80, + + /** Out: host-to-device */ + LIBUSB_ENDPOINT_OUT = 0x00 +}; + +#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ + +/** \ingroup desc + * Endpoint transfer type. Values for bits 0:1 of the + * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field. + */ +enum libusb_transfer_type { + /** Control endpoint */ + LIBUSB_TRANSFER_TYPE_CONTROL = 0, + + /** Isochronous endpoint */ + LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1, + + /** Bulk endpoint */ + LIBUSB_TRANSFER_TYPE_BULK = 2, + + /** Interrupt endpoint */ + LIBUSB_TRANSFER_TYPE_INTERRUPT = 3 +}; + +/** \ingroup misc + * Standard requests, as defined in table 9-3 of the USB2 specifications */ +enum libusb_standard_request { + /** Request status of the specific recipient */ + LIBUSB_REQUEST_GET_STATUS = 0x00, + + /** Clear or disable a specific feature */ + LIBUSB_REQUEST_CLEAR_FEATURE = 0x01, + + /* 0x02 is reserved */ + + /** Set or enable a specific feature */ + LIBUSB_REQUEST_SET_FEATURE = 0x03, + + /* 0x04 is reserved */ + + /** Set device address for all future accesses */ + LIBUSB_REQUEST_SET_ADDRESS = 0x05, + + /** Get the specified descriptor */ + LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06, + + /** Used to update existing descriptors or add new descriptors */ + LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07, + + /** Get the current device configuration value */ + LIBUSB_REQUEST_GET_CONFIGURATION = 0x08, + + /** Set device configuration */ + LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, + + /** Return the selected alternate setting for the specified interface */ + LIBUSB_REQUEST_GET_INTERFACE = 0x0A, + + /** Select an alternate interface for the specified interface */ + LIBUSB_REQUEST_SET_INTERFACE = 0x0B, + + /** Set then report an endpoint's synchronization frame */ + LIBUSB_REQUEST_SYNCH_FRAME = 0x0C, +}; + +/** \ingroup misc + * Request type bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. */ +enum libusb_request_type { + /** Standard */ + LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5), + + /** Class */ + LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5), + + /** Vendor */ + LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5), + + /** Reserved */ + LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5) +}; + +/** \ingroup misc + * Recipient bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. Values 4 through 31 are reserved. */ +enum libusb_request_recipient { + /** Device */ + LIBUSB_RECIPIENT_DEVICE = 0x00, + + /** Interface */ + LIBUSB_RECIPIENT_INTERFACE = 0x01, + + /** Endpoint */ + LIBUSB_RECIPIENT_ENDPOINT = 0x02, + + /** Other */ + LIBUSB_RECIPIENT_OTHER = 0x03, +}; + +#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C + +/** \ingroup desc + * Synchronization type for isochronous endpoints. Values for bits 2:3 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_sync_type { + /** No synchronization */ + LIBUSB_ISO_SYNC_TYPE_NONE = 0, + + /** Asynchronous */ + LIBUSB_ISO_SYNC_TYPE_ASYNC = 1, + + /** Adaptive */ + LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2, + + /** Synchronous */ + LIBUSB_ISO_SYNC_TYPE_SYNC = 3 +}; + +#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 + +/** \ingroup desc + * Usage type for isochronous endpoints. Values for bits 4:5 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_usage_type { + /** Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_DATA = 0, + + /** Feedback endpoint */ + LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1, + + /** Implicit feedback Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2, +}; + +/** \ingroup desc + * A structure representing the standard USB device descriptor. This + * descriptor is documented in section 9.6.1 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_device_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this + * context. */ + uint8_t bDescriptorType; + + /** USB specification release number in binary-coded decimal. A value of + * 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */ + uint16_t bcdUSB; + + /** USB-IF class code for the device. See \ref libusb_class_code. */ + uint8_t bDeviceClass; + + /** USB-IF subclass code for the device, qualified by the bDeviceClass + * value */ + uint8_t bDeviceSubClass; + + /** USB-IF protocol code for the device, qualified by the bDeviceClass and + * bDeviceSubClass values */ + uint8_t bDeviceProtocol; + + /** Maximum packet size for endpoint 0 */ + uint8_t bMaxPacketSize0; + + /** USB-IF vendor ID */ + uint16_t idVendor; + + /** USB-IF product ID */ + uint16_t idProduct; + + /** Device release number in binary-coded decimal */ + uint16_t bcdDevice; + + /** Index of string descriptor describing manufacturer */ + uint8_t iManufacturer; + + /** Index of string descriptor describing product */ + uint8_t iProduct; + + /** Index of string descriptor containing device serial number */ + uint8_t iSerialNumber; + + /** Number of possible configurations */ + uint8_t bNumConfigurations; +}; + +/** \ingroup desc + * A structure representing the standard USB endpoint descriptor. This + * descriptor is documented in section 9.6.3 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_endpoint_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in + * this context. */ + uint8_t bDescriptorType; + + /** The address of the endpoint described by this descriptor. Bits 0:3 are + * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, + * see \ref libusb_endpoint_direction. + */ + uint8_t bEndpointAddress; + + /** Attributes which apply to the endpoint when it is configured using + * the bConfigurationValue. Bits 0:1 determine the transfer type and + * correspond to \ref libusb_transfer_type. Bits 2:3 are only used for + * isochronous endpoints and correspond to \ref libusb_iso_sync_type. + * Bits 4:5 are also only used for isochronous endpoints and correspond to + * \ref libusb_iso_usage_type. Bits 6:7 are reserved. + */ + uint8_t bmAttributes; + + /** Maximum packet size this endpoint is capable of sending/receiving. */ + uint16_t wMaxPacketSize; + + /** Interval for polling endpoint for data transfers. */ + uint8_t bInterval; + + /** For audio devices only: the rate at which synchronization feedback + * is provided. */ + uint8_t bRefresh; + + /** For audio devices only: the address if the synch endpoint */ + uint8_t bSynchAddress; + + /** Extra descriptors. If libusb encounters unknown endpoint descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup desc + * A structure representing the standard USB interface descriptor. This + * descriptor is documented in section 9.6.5 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_interface_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE + * in this context. */ + uint8_t bDescriptorType; + + /** Number of this interface */ + uint8_t bInterfaceNumber; + + /** Value used to select this alternate setting for this interface */ + uint8_t bAlternateSetting; + + /** Number of endpoints used by this interface (excluding the control + * endpoint). */ + uint8_t bNumEndpoints; + + /** USB-IF class code for this interface. See \ref libusb_class_code. */ + uint8_t bInterfaceClass; + + /** USB-IF subclass code for this interface, qualified by the + * bInterfaceClass value */ + uint8_t bInterfaceSubClass; + + /** USB-IF protocol code for this interface, qualified by the + * bInterfaceClass and bInterfaceSubClass values */ + uint8_t bInterfaceProtocol; + + /** Index of string descriptor describing this interface */ + uint8_t iInterface; + + /** Array of endpoint descriptors. This length of this array is determined + * by the bNumEndpoints field. */ + const struct libusb_endpoint_descriptor *endpoint; + + /** Extra descriptors. If libusb encounters unknown interface descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup desc + * A collection of alternate settings for a particular USB interface. + */ +struct libusb_interface { + /** Array of interface descriptors. The length of this array is determined + * by the num_altsetting field. */ + const struct libusb_interface_descriptor *altsetting; + + /** The number of alternate settings that belong to this interface */ + int num_altsetting; +}; + +/** \ingroup desc + * A structure representing the standard USB configuration descriptor. This + * descriptor is documented in section 9.6.3 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_config_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG + * in this context. */ + uint8_t bDescriptorType; + + /** Total length of data returned for this configuration */ + uint16_t wTotalLength; + + /** Number of interfaces supported by this configuration */ + uint8_t bNumInterfaces; + + /** Identifier value for this configuration */ + uint8_t bConfigurationValue; + + /** Index of string descriptor describing this configuration */ + uint8_t iConfiguration; + + /** Configuration characteristics */ + uint8_t bmAttributes; + + /** Maximum power consumption of the USB device from this bus in this + * configuration when the device is fully opreation. Expressed in units + * of 2 mA. */ + uint8_t MaxPower; + + /** Array of interfaces supported by this configuration. The length of + * this array is determined by the bNumInterfaces field. */ + const struct libusb_interface *interface; + + /** Extra descriptors. If libusb encounters unknown configuration + * descriptors, it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup asyncio + * Setup packet for control transfers. */ +struct libusb_control_setup { + /** Request type. Bits 0:4 determine recipient, see + * \ref libusb_request_recipient. Bits 5:6 determine type, see + * \ref libusb_request_type. Bit 7 determines data transfer direction, see + * \ref libusb_endpoint_direction. + */ + uint8_t bmRequestType; + + /** Request. If the type bits of bmRequestType are equal to + * \ref libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD + * "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to + * \ref libusb_standard_request. For other cases, use of this field is + * application-specific. */ + uint8_t bRequest; + + /** Value. Varies according to request */ + uint16_t wValue; + + /** Index. Varies according to request, typically used to pass an index + * or offset */ + uint16_t wIndex; + + /** Number of bytes to transfer */ + uint16_t wLength; +}; + +#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) + +/* libusb */ + +struct libusb_context; +struct libusb_device; +struct libusb_device_handle; + +/** \ingroup lib + * Structure representing the libusb version. + */ +struct libusb_version { + /** Library major version. */ + const uint16_t major; + + /** Library minor version. */ + const uint16_t minor; + + /** Library micro version. */ + const uint16_t micro; + + /** Library nano version. This field is only nonzero on Windows. */ + const uint16_t nano; + + /** Library release candidate suffix string, e.g. "-rc4". */ + const char *rc; + + /** Output of `git describe --tags` at library build time. */ + const char *describe; +}; + +/** \ingroup lib + * Structure representing a libusb session. The concept of individual libusb + * sessions allows for your program to use two libraries (or dynamically + * load two modules) which both independently use libusb. This will prevent + * interference between the individual libusb users - for example + * libusb_set_debug() will not affect the other user of the library, and + * libusb_exit() will not destroy resources that the other user is still + * using. + * + * Sessions are created by libusb_init() and destroyed through libusb_exit(). + * If your application is guaranteed to only ever include a single libusb + * user (i.e. you), you do not have to worry about contexts: pass NULL in + * every function call where a context is required. The default context + * will be used. + * + * For more information, see \ref contexts. + */ +typedef struct libusb_context libusb_context; + +/** \ingroup dev + * Structure representing a USB device detected on the system. This is an + * opaque type for which you are only ever provided with a pointer, usually + * originating from libusb_get_device_list(). + * + * Certain operations can be performed on a device, but in order to do any + * I/O you will have to first obtain a device handle using libusb_open(). + * + * Devices are reference counted with libusb_device_ref() and + * libusb_device_unref(), and are freed when the reference count reaches 0. + * New devices presented by libusb_get_device_list() have a reference count of + * 1, and libusb_free_device_list() can optionally decrease the reference count + * on all devices in the list. libusb_open() adds another reference which is + * later destroyed by libusb_close(). + */ +typedef struct libusb_device libusb_device; + + +/** \ingroup dev + * Structure representing a handle on a USB device. This is an opaque type for + * which you are only ever provided with a pointer, usually originating from + * libusb_open(). + * + * A device handle is used to perform I/O and other operations. When finished + * with a device handle, you should call libusb_close(). + */ +typedef struct libusb_device_handle libusb_device_handle; + +/** \ingroup dev + * Speed codes. Indicates the speed at which the device is operating. + */ +enum libusb_speed { + /** The OS doesn't report or know the device speed. */ + LIBUSB_SPEED_UNKNOWN = 0, + + /** The device is operating at low speed (1.5MBit/s). */ + LIBUSB_SPEED_LOW = 1, + + /** The device is operating at full speed (12MBit/s). */ + LIBUSB_SPEED_FULL = 2, + + /** The device is operating at high speed (480MBit/s). */ + LIBUSB_SPEED_HIGH = 3, + + /** The device is operating at super speed (5000MBit/s). */ + LIBUSB_SPEED_SUPER = 4, +}; + +/** \ingroup misc + * Error codes. Most libusb functions return 0 on success or one of these + * codes on failure. + * You can call \ref libusb_error_name() to retrieve a string representation + * of an error code. + */ +enum libusb_error { + /** Success (no error) */ + LIBUSB_SUCCESS = 0, + + /** Input/output error */ + LIBUSB_ERROR_IO = -1, + + /** Invalid parameter */ + LIBUSB_ERROR_INVALID_PARAM = -2, + + /** Access denied (insufficient permissions) */ + LIBUSB_ERROR_ACCESS = -3, + + /** No such device (it may have been disconnected) */ + LIBUSB_ERROR_NO_DEVICE = -4, + + /** Entity not found */ + LIBUSB_ERROR_NOT_FOUND = -5, + + /** Resource busy */ + LIBUSB_ERROR_BUSY = -6, + + /** Operation timed out */ + LIBUSB_ERROR_TIMEOUT = -7, + + /** Overflow */ + LIBUSB_ERROR_OVERFLOW = -8, + + /** Pipe error */ + LIBUSB_ERROR_PIPE = -9, + + /** System call interrupted (perhaps due to signal) */ + LIBUSB_ERROR_INTERRUPTED = -10, + + /** Insufficient memory */ + LIBUSB_ERROR_NO_MEM = -11, + + /** Operation not supported or unimplemented on this platform */ + LIBUSB_ERROR_NOT_SUPPORTED = -12, + + /* NB! Remember to update libusb_error_name() + when adding new error codes here. */ + + /** Other error */ + LIBUSB_ERROR_OTHER = -99, +}; + +/** \ingroup asyncio + * Transfer status codes */ +enum libusb_transfer_status { + /** Transfer completed without error. Note that this does not indicate + * that the entire amount of requested data was transferred. */ + LIBUSB_TRANSFER_COMPLETED, + + /** Transfer failed */ + LIBUSB_TRANSFER_ERROR, + + /** Transfer timed out */ + LIBUSB_TRANSFER_TIMED_OUT, + + /** Transfer was cancelled */ + LIBUSB_TRANSFER_CANCELLED, + + /** For bulk/interrupt endpoints: halt condition detected (endpoint + * stalled). For control endpoints: control request not supported. */ + LIBUSB_TRANSFER_STALL, + + /** Device was disconnected */ + LIBUSB_TRANSFER_NO_DEVICE, + + /** Device sent more data than requested */ + LIBUSB_TRANSFER_OVERFLOW, +}; + +/** \ingroup asyncio + * libusb_transfer.flags values */ +enum libusb_transfer_flags { + /** Report short frames as errors */ + LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0, + + /** Automatically free() transfer buffer during libusb_free_transfer() */ + LIBUSB_TRANSFER_FREE_BUFFER = 1<<1, + + /** Automatically call libusb_free_transfer() after callback returns. + * If this flag is set, it is illegal to call libusb_free_transfer() + * from your transfer callback, as this will result in a double-free + * when this flag is acted upon. */ + LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2, + + /** Terminate transfers that are a multiple of the endpoint's + * wMaxPacketSize with an extra zero length packet. This is useful + * when a device protocol mandates that each logical request is + * terminated by an incomplete packet (i.e. the logical requests are + * not separated by other means). + * + * This flag only affects host-to-device transfers to bulk and interrupt + * endpoints. In other situations, it is ignored. + * + * This flag only affects transfers with a length that is a multiple of + * the endpoint's wMaxPacketSize. On transfers of other lengths, this + * flag has no effect. Therefore, if you are working with a device that + * needs a ZLP whenever the end of the logical request falls on a packet + * boundary, then it is sensible to set this flag on every + * transfer (you do not have to worry about only setting it on transfers + * that end on the boundary). + * + * This flag is currently only supported on Linux. + * On other systems, libusb_submit_transfer() will return + * LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this flag is set. + * + * Available since libusb-1.0.9. + */ + LIBUSB_TRANSFER_ADD_ZERO_PACKET = 1 << 3, +}; + +/** \ingroup asyncio + * Isochronous packet descriptor. */ +struct libusb_iso_packet_descriptor { + /** Length of data to request in this packet */ + unsigned int length; + + /** Amount of data that was actually transferred */ + unsigned int actual_length; + + /** Status code for this packet */ + enum libusb_transfer_status status; +}; + +struct libusb_transfer; + +/** \ingroup asyncio + * Asynchronous transfer callback function type. When submitting asynchronous + * transfers, you pass a pointer to a callback function of this type via the + * \ref libusb_transfer::callback "callback" member of the libusb_transfer + * structure. libusb will call this function later, when the transfer has + * completed or failed. See \ref asyncio for more information. + * \param transfer The libusb_transfer struct the callback function is being + * notified about. + */ +typedef void (LIBUSB_CALL *libusb_transfer_cb_fn)(struct libusb_transfer *transfer); + +/** \ingroup asyncio + * The generic USB transfer structure. The user populates this structure and + * then submits it in order to request a transfer. After the transfer has + * completed, the library populates the transfer with the results and passes + * it back to the user. + */ +struct libusb_transfer { + /** Handle of the device that this transfer will be submitted to */ + libusb_device_handle *dev_handle; + + /** A bitwise OR combination of \ref libusb_transfer_flags. */ + uint8_t flags; + + /** Address of the endpoint where this transfer will be sent. */ + unsigned char endpoint; + + /** Type of the endpoint from \ref libusb_transfer_type */ + unsigned char type; + + /** Timeout for this transfer in millseconds. A value of 0 indicates no + * timeout. */ + unsigned int timeout; + + /** The status of the transfer. Read-only, and only for use within + * transfer callback function. + * + * If this is an isochronous transfer, this field may read COMPLETED even + * if there were errors in the frames. Use the + * \ref libusb_iso_packet_descriptor::status "status" field in each packet + * to determine if errors occurred. */ + enum libusb_transfer_status status; + + /** Length of the data buffer */ + int length; + + /** Actual length of data that was transferred. Read-only, and only for + * use within transfer callback function. Not valid for isochronous + * endpoint transfers. */ + int actual_length; + + /** Callback function. This will be invoked when the transfer completes, + * fails, or is cancelled. */ + libusb_transfer_cb_fn callback; + + /** User context data to pass to the callback function. */ + void *user_data; + + /** Data buffer */ + unsigned char *buffer; + + /** Number of isochronous packets. Only used for I/O with isochronous + * endpoints. */ + int num_iso_packets; + + /** Isochronous packet descriptors, for isochronous transfers only. */ + struct libusb_iso_packet_descriptor iso_packet_desc +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; +}; + +/** \ingroup misc + * Capabilities supported by this instance of libusb. Test if the loaded + * library supports a given capability by calling + * \ref libusb_has_capability(). + */ +enum libusb_capability { + /** The libusb_has_capability() API is available. */ + LIBUSB_CAP_HAS_CAPABILITY = 0, +}; + +int LIBUSB_CALL libusb_init(libusb_context **ctx); +void LIBUSB_CALL libusb_exit(libusb_context *ctx); +void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); +const struct libusb_version * LIBUSB_CALL libusb_get_version(void); +int LIBUSB_CALL libusb_has_capability(uint32_t capability); +const char * LIBUSB_CALL libusb_error_name(int errcode); + +ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, + libusb_device ***list); +void LIBUSB_CALL libusb_free_device_list(libusb_device **list, + int unref_devices); +libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev); +void LIBUSB_CALL libusb_unref_device(libusb_device *dev); + +int LIBUSB_CALL libusb_get_configuration(libusb_device_handle *dev, + int *config); +int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev, + struct libusb_device_descriptor *desc); +int LIBUSB_CALL libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor(libusb_device *dev, + uint8_t config_index, struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config); +void LIBUSB_CALL libusb_free_config_descriptor( + struct libusb_config_descriptor *config); +uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev); +int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev); +int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, + unsigned char endpoint); +int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, + unsigned char endpoint); + +int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **handle); +void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle); +libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle); + +int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev, + int configuration); +int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev, + int interface_number); +int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev, + int interface_number); + +libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid( + libusb_context *ctx, uint16_t vendor_id, uint16_t product_id); + +int LIBUSB_CALL libusb_set_interface_alt_setting(libusb_device_handle *dev, + int interface_number, int alternate_setting); +int LIBUSB_CALL libusb_clear_halt(libusb_device_handle *dev, + unsigned char endpoint); +int LIBUSB_CALL libusb_reset_device(libusb_device_handle *dev); + +int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev, + int interface_number); +int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev, + int interface_number); +int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle *dev, + int interface_number); + +/* async I/O */ + +/** \ingroup asyncio + * Get the data section of a control transfer. This convenience function is here + * to remind you that the data does not start until 8 bytes into the actual + * buffer, as the setup packet comes first. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns pointer to the first byte of the data section + */ +static inline unsigned char *libusb_control_transfer_get_data( + struct libusb_transfer *transfer) +{ + return transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; +} + +/** \ingroup asyncio + * Get the control setup packet of a control transfer. This convenience + * function is here to remind you that the control setup occupies the first + * 8 bytes of the transfer data buffer. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns a casted pointer to the start of the transfer data buffer + */ +static inline struct libusb_control_setup *libusb_control_transfer_get_setup( + struct libusb_transfer *transfer) +{ + return (struct libusb_control_setup *) transfer->buffer; +} + +/** \ingroup asyncio + * Helper function to populate the setup packet (first 8 bytes of the data + * buffer) for a control transfer. The wIndex, wValue and wLength values should + * be given in host-endian byte order. + * + * \param buffer buffer to output the setup packet into + * \param bmRequestType see the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field of + * \ref libusb_control_setup + * \param bRequest see the + * \ref libusb_control_setup::bRequest "bRequest" field of + * \ref libusb_control_setup + * \param wValue see the + * \ref libusb_control_setup::wValue "wValue" field of + * \ref libusb_control_setup + * \param wIndex see the + * \ref libusb_control_setup::wIndex "wIndex" field of + * \ref libusb_control_setup + * \param wLength see the + * \ref libusb_control_setup::wLength "wLength" field of + * \ref libusb_control_setup + */ +static inline void libusb_fill_control_setup(unsigned char *buffer, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + uint16_t wLength) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; + setup->bmRequestType = bmRequestType; + setup->bRequest = bRequest; + setup->wValue = libusb_cpu_to_le16(wValue); + setup->wIndex = libusb_cpu_to_le16(wIndex); + setup->wLength = libusb_cpu_to_le16(wLength); +} + +struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets); +int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer); +int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer); +void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer); + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a control transfer. + * + * If you pass a transfer buffer to this function, the first 8 bytes will + * be interpreted as a control setup packet, and the wLength field will be + * used to automatically populate the \ref libusb_transfer::length "length" + * field of the transfer. Therefore the recommended approach is: + * -# Allocate a suitably sized data buffer (including space for control setup) + * -# Call libusb_fill_control_setup() + * -# If this is a host-to-device transfer with a data stage, put the data + * in place after the setup packet + * -# Call this function + * -# Call libusb_submit_transfer() + * + * It is also legal to pass a NULL buffer to this function, in which case this + * function will not attempt to populate the length field. Remember that you + * must then populate the buffer and length fields later. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param buffer data buffer. If provided, this function will interpret the + * first 8 bytes as a setup packet and infer the transfer length from that. + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_control_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data, + unsigned int timeout) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; + transfer->dev_handle = dev_handle; + transfer->endpoint = 0; + transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; + transfer->timeout = timeout; + transfer->buffer = buffer; + if (setup) + transfer->length = LIBUSB_CONTROL_SETUP_SIZE + + libusb_le16_to_cpu(setup->wLength); + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an interrupt transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_interrupt_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an isochronous transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param num_iso_packets the number of isochronous packets + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, int num_iso_packets, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->num_iso_packets = num_iso_packets; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Convenience function to set the length of all packets in an isochronous + * transfer, based on the num_iso_packets field in the transfer structure. + * + * \param transfer a transfer + * \param length the length to set in each isochronous packet descriptor + * \see libusb_get_max_packet_size() + */ +static inline void libusb_set_iso_packet_lengths( + struct libusb_transfer *transfer, unsigned int length) +{ + int i; + for (i = 0; i < transfer->num_iso_packets; i++) + transfer->iso_packet_desc[i].length = length; +} + +/** \ingroup asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer. + * + * This is a thorough function which loops through all preceding packets, + * accumulating their lengths to find the position of the specified packet. + * Typically you will assign equal lengths to each packet in the transfer, + * and hence the above method is sub-optimal. You may wish to use + * libusb_get_iso_packet_buffer_simple() instead. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer_simple() + */ +static inline unsigned char *libusb_get_iso_packet_buffer( + struct libusb_transfer *transfer, unsigned int packet) +{ + int i; + size_t offset = 0; + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + for (i = 0; i < _packet; i++) + offset += transfer->iso_packet_desc[i].length; + + return transfer->buffer + offset; +} + +/** \ingroup asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer, for transfers where each + * packet is of identical size. + * + * This function relies on the assumption that every packet within the transfer + * is of identical size to the first packet. Calculating the location of + * the packet buffer is then just a simple calculation: + * buffer + (packet_size * packet) + * + * Do not use this function on transfers other than those that have identical + * packet lengths for each packet. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer() + */ +static inline unsigned char *libusb_get_iso_packet_buffer_simple( + struct libusb_transfer *transfer, unsigned int packet) +{ + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + return transfer->buffer + (transfer->iso_packet_desc[0].length * _packet); +} + +/* sync I/O */ + +int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle, + uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout); + +int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +/** \ingroup desc + * Retrieve a descriptor from the default control pipe. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. + * + * \param dev a device handle + * \param desc_type the descriptor type, see \ref libusb_descriptor_type + * \param desc_index the index of the descriptor to retrieve + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + */ +static inline int libusb_get_descriptor(libusb_device_handle *dev, + uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length) +{ + return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (desc_type << 8) | desc_index, 0, data, + (uint16_t) length, 1000); +} + +/** \ingroup desc + * Retrieve a descriptor from a device. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. The string returned is Unicode, as + * detailed in the USB specifications. + * + * \param dev a device handle + * \param desc_index the index of the descriptor to retrieve + * \param langid the language ID for the string descriptor + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + * \see libusb_get_string_descriptor_ascii() + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev, + uint8_t desc_index, uint16_t langid, unsigned char *data, int length) +{ + return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t)((LIBUSB_DT_STRING << 8) | desc_index), + langid, data, (uint16_t) length, 1000); +} + +int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *dev, + uint8_t desc_index, unsigned char *data, int length); + +/* polling and timeouts */ + +int LIBUSB_CALL libusb_try_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_events(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handling_ok(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handler_active(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_event_waiters(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_event_waiters(libusb_context *ctx); +int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv); + +int LIBUSB_CALL libusb_handle_events_timeout(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_handle_events_timeout_completed(libusb_context *ctx, + struct timeval *tv, int *completed); +int LIBUSB_CALL libusb_handle_events(libusb_context *ctx); +int LIBUSB_CALL libusb_handle_events_completed(libusb_context *ctx, int *completed); +int LIBUSB_CALL libusb_handle_events_locked(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_pollfds_handle_timeouts(libusb_context *ctx); +int LIBUSB_CALL libusb_get_next_timeout(libusb_context *ctx, + struct timeval *tv); + +/** \ingroup poll + * File descriptor for polling + */ +struct libusb_pollfd { + /** Numeric file descriptor */ + int fd; + + /** Event flags to poll for from . POLLIN indicates that you + * should monitor this file descriptor for becoming ready to read from, + * and POLLOUT indicates that you should monitor this file descriptor for + * nonblocking write readiness. */ + short events; +}; + +/** \ingroup poll + * Callback function, invoked when a new file descriptor should be added + * to the set of file descriptors monitored for events. + * \param fd the new file descriptor + * \param events events to monitor for, see \ref libusb_pollfd for a + * description + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_added_cb)(int fd, short events, + void *user_data); + +/** \ingroup poll + * Callback function, invoked when a file descriptor should be removed from + * the set of file descriptors being monitored for events. After returning + * from this callback, do not use that file descriptor again. + * \param fd the file descriptor to stop monitoring + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_removed_cb)(int fd, void *user_data); + +const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds( + libusb_context *ctx); +void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, + void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/interface/external/LibUSB/include/libusbi.h b/interface/external/LibUSB/include/libusbi.h new file mode 100644 index 0000000000..976be0d13b --- /dev/null +++ b/interface/external/LibUSB/include/libusbi.h @@ -0,0 +1,935 @@ +/* + * Internal header for libusb + * Copyright (C) 2007-2009 Daniel Drake + * Copyright (c) 2001 Johannes Erdfelt + * + * 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 + */ + +#ifndef LIBUSBI_H +#define LIBUSBI_H + +#include + +#include +#include +#include +#include +#ifdef HAVE_POLL_H +#include +#endif + +#include +#include + +/* Inside the libusb code, mark all public functions as follows: + * return_type API_EXPORTED function_name(params) { ... } + * But if the function returns a pointer, mark it as follows: + * DEFAULT_VISIBILITY return_type * LIBUSB_CALL function_name(params) { ... } + * In the libusb public header, mark all declarations as: + * return_type LIBUSB_CALL function_name(params); + */ +#define API_EXPORTED LIBUSB_CALL DEFAULT_VISIBILITY + +#define DEVICE_DESC_LENGTH 18 + +#define USB_MAXENDPOINTS 32 +#define USB_MAXINTERFACES 32 +#define USB_MAXCONFIG 8 + +struct list_head { + struct list_head *prev, *next; +}; + +/* Get an entry from the list + * ptr - the address of this list_head element in "type" + * type - the data type that contains "member" + * member - the list_head element in "type" + */ +#define list_entry(ptr, type, member) \ + ((type *)((uintptr_t)(ptr) - (uintptr_t)(&((type *)0L)->member))) + +/* Get each entry from a list + * pos - A structure pointer has a "member" element + * head - list head + * member - the list_head element in "pos" + * type - the type of the first parameter + */ +#define list_for_each_entry(pos, head, member, type) \ + for (pos = list_entry((head)->next, type, member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, type, member)) + +#define list_for_each_entry_safe(pos, n, head, member, type) \ + for (pos = list_entry((head)->next, type, member), \ + n = list_entry(pos->member.next, type, member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, type, member)) + +#define list_empty(entry) ((entry)->next == (entry)) + +static inline void list_init(struct list_head *entry) +{ + entry->prev = entry->next = entry; +} + +static inline void list_add(struct list_head *entry, struct list_head *head) +{ + entry->next = head->next; + entry->prev = head; + + head->next->prev = entry; + head->next = entry; +} + +static inline void list_add_tail(struct list_head *entry, + struct list_head *head) +{ + entry->next = head; + entry->prev = head->prev; + + head->prev->next = entry; + head->prev = entry; +} + +static inline void list_del(struct list_head *entry) +{ + entry->next->prev = entry->prev; + entry->prev->next = entry->next; +} + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *mptr = (ptr); \ + (type *)( (char *)mptr - offsetof(type,member) );}) + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define TIMESPEC_IS_SET(ts) ((ts)->tv_sec != 0 || (ts)->tv_nsec != 0) + +enum usbi_log_level { + LOG_LEVEL_DEBUG, + LOG_LEVEL_INFO, + LOG_LEVEL_WARNING, + LOG_LEVEL_ERROR, +}; + +void usbi_log(struct libusb_context *ctx, enum usbi_log_level level, + const char *function, const char *format, ...); + +void usbi_log_v(struct libusb_context *ctx, enum usbi_log_level level, + const char *function, const char *format, va_list args); + +#if !defined(_MSC_VER) || _MSC_VER >= 1400 + +#ifdef ENABLE_LOGGING +#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __FUNCTION__, __VA_ARGS__) +#else +#define _usbi_log(ctx, level, ...) do { (void)(ctx); } while(0) +#endif + +#ifdef ENABLE_DEBUG_LOGGING +#define usbi_dbg(...) _usbi_log(NULL, LOG_LEVEL_DEBUG, __VA_ARGS__) +#else +#define usbi_dbg(...) do {} while(0) +#endif + +#define usbi_info(ctx, ...) _usbi_log(ctx, LOG_LEVEL_INFO, __VA_ARGS__) +#define usbi_warn(ctx, ...) _usbi_log(ctx, LOG_LEVEL_WARNING, __VA_ARGS__) +#define usbi_err(ctx, ...) _usbi_log(ctx, LOG_LEVEL_ERROR, __VA_ARGS__) + +#else /* !defined(_MSC_VER) || _MSC_VER >= 1400 */ + +/* Old MS compilers don't support variadic macros. The code is simple, so we + * repeat it for each loglevel. Note that the debug case is special. + * + * Support for variadic macros was introduced in Visual C++ 2005. + * http://msdn.microsoft.com/en-us/library/ms177415%28v=VS.80%29.aspx + */ + +static inline void usbi_info(struct libusb_context *ctx, const char *fmt, ...) +{ +#ifdef ENABLE_LOGGING + va_list args; + va_start(args, fmt); + usbi_log_v(ctx, LOG_LEVEL_INFO, "", fmt, args); + va_end(args); +#else + (void)ctx; +#endif +} + +static inline void usbi_warn(struct libusb_context *ctx, const char *fmt, ...) +{ +#ifdef ENABLE_LOGGING + va_list args; + va_start(args, fmt); + usbi_log_v(ctx, LOG_LEVEL_WARNING, "", fmt, args); + va_end(args); +#else + (void)ctx; +#endif +} + +static inline void usbi_err(struct libusb_context *ctx, const char *fmt, ...) +{ +#ifdef ENABLE_LOGGING + va_list args; + va_start(args, fmt); + usbi_log_v(ctx, LOG_LEVEL_ERROR, "", fmt, args); + va_end(args); +#else + (void)ctx; +#endif +} + +static inline void usbi_dbg(const char *fmt, ...) +{ +#ifdef ENABLE_DEBUG_LOGGING + va_list args; + va_start(args, fmt); + usbi_log_v(NULL, LOG_LEVEL_DEBUG, "", fmt, args); + va_end(args); +#else + (void)fmt; +#endif +} + +#endif /* !defined(_MSC_VER) || _MSC_VER >= 1400 */ + +#define USBI_GET_CONTEXT(ctx) if (!(ctx)) (ctx) = usbi_default_context +#define DEVICE_CTX(dev) ((dev)->ctx) +#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) +#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) +#define ITRANSFER_CTX(transfer) \ + (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer))) + +#define IS_EPIN(ep) (0 != ((ep) & LIBUSB_ENDPOINT_IN)) +#define IS_EPOUT(ep) (!IS_EPIN(ep)) +#define IS_XFERIN(xfer) (0 != ((xfer)->endpoint & LIBUSB_ENDPOINT_IN)) +#define IS_XFEROUT(xfer) (!IS_XFERIN(xfer)) + +/* Internal abstractions for thread synchronization and poll */ +#if defined(THREADS_POSIX) +#include +#elif defined(OS_WINDOWS) +#include +#endif + +#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD) +#include +#include +#elif defined(OS_WINDOWS) +#include +#endif + +#if defined(OS_WINDOWS) && !defined(__GCC__) +#undef HAVE_GETTIMEOFDAY +int usbi_gettimeofday(struct timeval *tp, void *tzp); +#define LIBUSB_GETTIMEOFDAY_WIN32 +#define HAVE_USBI_GETTIMEOFDAY +#else +#ifdef HAVE_GETTIMEOFDAY +#define usbi_gettimeofday(tv, tz) gettimeofday((tv), (tz)) +#define HAVE_USBI_GETTIMEOFDAY +#endif +#endif + +extern struct libusb_context *usbi_default_context; + +struct libusb_context { + int debug; + int debug_fixed; + + /* internal control pipe, used for interrupting event handling when + * something needs to modify poll fds. */ + int ctrl_pipe[2]; + + struct list_head usb_devs; + usbi_mutex_t usb_devs_lock; + + /* A list of open handles. Backends are free to traverse this if required. + */ + struct list_head open_devs; + usbi_mutex_t open_devs_lock; + + /* this is a list of in-flight transfer handles, sorted by timeout + * expiration. URBs to timeout the soonest are placed at the beginning of + * the list, URBs that will time out later are placed after, and urbs with + * infinite timeout are always placed at the very end. */ + struct list_head flying_transfers; + usbi_mutex_t flying_transfers_lock; + + /* list of poll fds */ + struct list_head pollfds; + usbi_mutex_t pollfds_lock; + + /* a counter that is set when we want to interrupt event handling, in order + * to modify the poll fd set. and a lock to protect it. */ + unsigned int pollfd_modify; + usbi_mutex_t pollfd_modify_lock; + + /* user callbacks for pollfd changes */ + libusb_pollfd_added_cb fd_added_cb; + libusb_pollfd_removed_cb fd_removed_cb; + void *fd_cb_user_data; + + /* ensures that only one thread is handling events at any one time */ + usbi_mutex_t events_lock; + + /* used to see if there is an active thread doing event handling */ + int event_handler_active; + + /* used to wait for event completion in threads other than the one that is + * event handling */ + usbi_mutex_t event_waiters_lock; + usbi_cond_t event_waiters_cond; + +#ifdef USBI_TIMERFD_AVAILABLE + /* used for timeout handling, if supported by OS. + * this timerfd is maintained to trigger on the next pending timeout */ + int timerfd; +#endif +}; + +#ifdef USBI_TIMERFD_AVAILABLE +#define usbi_using_timerfd(ctx) ((ctx)->timerfd >= 0) +#else +#define usbi_using_timerfd(ctx) (0) +#endif + +struct libusb_device { + /* lock protects refcnt, everything else is finalized at initialization + * time */ + usbi_mutex_t lock; + int refcnt; + + struct libusb_context *ctx; + + uint8_t bus_number; + uint8_t device_address; + uint8_t num_configurations; + enum libusb_speed speed; + + struct list_head list; + unsigned long session_data; + unsigned char os_priv[0]; +}; + +struct libusb_device_handle { + /* lock protects claimed_interfaces */ + usbi_mutex_t lock; + unsigned long claimed_interfaces; + + struct list_head list; + struct libusb_device *dev; + unsigned char os_priv[0]; +}; + +enum { + USBI_CLOCK_MONOTONIC, + USBI_CLOCK_REALTIME +}; + +/* in-memory transfer layout: + * + * 1. struct usbi_transfer + * 2. struct libusb_transfer (which includes iso packets) [variable size] + * 3. os private data [variable size] + * + * from a libusb_transfer, you can get the usbi_transfer by rewinding the + * appropriate number of bytes. + * the usbi_transfer includes the number of allocated packets, so you can + * determine the size of the transfer and hence the start and length of the + * OS-private data. + */ + +struct usbi_transfer { + int num_iso_packets; + struct list_head list; + struct timeval timeout; + int transferred; + uint8_t flags; + + /* this lock is held during libusb_submit_transfer() and + * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate + * cancellation, submission-during-cancellation, etc). the OS backend + * should also take this lock in the handle_events path, to prevent the user + * cancelling the transfer from another thread while you are processing + * its completion (presumably there would be races within your OS backend + * if this were possible). */ + usbi_mutex_t lock; +}; + +enum usbi_transfer_flags { + /* The transfer has timed out */ + USBI_TRANSFER_TIMED_OUT = 1 << 0, + + /* Set by backend submit_transfer() if the OS handles timeout */ + USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1 << 1, + + /* Cancellation was requested via libusb_cancel_transfer() */ + USBI_TRANSFER_CANCELLING = 1 << 2, + + /* Operation on the transfer failed because the device disappeared */ + USBI_TRANSFER_DEVICE_DISAPPEARED = 1 << 3, +}; + +#define USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \ + ((struct libusb_transfer *)(((unsigned char *)(transfer)) \ + + sizeof(struct usbi_transfer))) +#define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \ + ((struct usbi_transfer *)(((unsigned char *)(transfer)) \ + - sizeof(struct usbi_transfer))) + +static inline void *usbi_transfer_get_os_priv(struct usbi_transfer *transfer) +{ + return ((unsigned char *)transfer) + sizeof(struct usbi_transfer) + + sizeof(struct libusb_transfer) + + (transfer->num_iso_packets + * sizeof(struct libusb_iso_packet_descriptor)); +} + +/* bus structures */ + +/* All standard descriptors have these 2 fields in common */ +struct usb_descriptor_header { + uint8_t bLength; + uint8_t bDescriptorType; +}; + +/* shared data and functions */ + +int usbi_io_init(struct libusb_context *ctx); +void usbi_io_exit(struct libusb_context *ctx); + +struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, + unsigned long session_id); +struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx, + unsigned long session_id); +int usbi_sanitize_device(struct libusb_device *dev); +void usbi_handle_disconnect(struct libusb_device_handle *handle); + +int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, + enum libusb_transfer_status status); +int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer); + +int usbi_parse_descriptor(unsigned char *source, const char *descriptor, + void *dest, int host_endian); +int usbi_get_config_index_by_value(struct libusb_device *dev, + uint8_t bConfigurationValue, int *idx); + +/* polling */ + +struct usbi_pollfd { + /* must come first */ + struct libusb_pollfd pollfd; + + struct list_head list; +}; + +int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events); +void usbi_remove_pollfd(struct libusb_context *ctx, int fd); +void usbi_fd_notification(struct libusb_context *ctx); + +/* device discovery */ + +/* we traverse usbfs without knowing how many devices we are going to find. + * so we create this discovered_devs model which is similar to a linked-list + * which grows when required. it can be freed once discovery has completed, + * eliminating the need for a list node in the libusb_device structure + * itself. */ +struct discovered_devs { + size_t len; + size_t capacity; + struct libusb_device *devices[0]; +}; + +struct discovered_devs *discovered_devs_append( + struct discovered_devs *discdevs, struct libusb_device *dev); + +/* OS abstraction */ + +/* This is the interface that OS backends need to implement. + * All fields are mandatory, except ones explicitly noted as optional. */ +struct usbi_os_backend { + /* A human-readable name for your backend, e.g. "Linux usbfs" */ + const char *name; + + /* Perform initialization of your backend. You might use this function + * to determine specific capabilities of the system, allocate required + * data structures for later, etc. + * + * This function is called when a libusb user initializes the library + * prior to use. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*init)(struct libusb_context *ctx); + + /* Deinitialization. Optional. This function should destroy anything + * that was set up by init. + * + * This function is called when the user deinitializes the library. + */ + void (*exit)(void); + + /* Enumerate all the USB devices on the system, returning them in a list + * of discovered devices. + * + * Your implementation should enumerate all devices on the system, + * regardless of whether they have been seen before or not. + * + * When you have found a device, compute a session ID for it. The session + * ID should uniquely represent that particular device for that particular + * connection session since boot (i.e. if you disconnect and reconnect a + * device immediately after, it should be assigned a different session ID). + * If your OS cannot provide a unique session ID as described above, + * presenting a session ID of (bus_number << 8 | device_address) should + * be sufficient. Bus numbers and device addresses wrap and get reused, + * but that is an unlikely case. + * + * After computing a session ID for a device, call + * usbi_get_device_by_session_id(). This function checks if libusb already + * knows about the device, and if so, it provides you with a libusb_device + * structure for it. + * + * If usbi_get_device_by_session_id() returns NULL, it is time to allocate + * a new device structure for the device. Call usbi_alloc_device() to + * obtain a new libusb_device structure with reference count 1. Populate + * the bus_number and device_address attributes of the new device, and + * perform any other internal backend initialization you need to do. At + * this point, you should be ready to provide device descriptors and so + * on through the get_*_descriptor functions. Finally, call + * usbi_sanitize_device() to perform some final sanity checks on the + * device. Assuming all of the above succeeded, we can now continue. + * If any of the above failed, remember to unreference the device that + * was returned by usbi_alloc_device(). + * + * At this stage we have a populated libusb_device structure (either one + * that was found earlier, or one that we have just allocated and + * populated). This can now be added to the discovered devices list + * using discovered_devs_append(). Note that discovered_devs_append() + * may reallocate the list, returning a new location for it, and also + * note that reallocation can fail. Your backend should handle these + * error conditions appropriately. + * + * This function should not generate any bus I/O and should not block. + * If I/O is required (e.g. reading the active configuration value), it is + * OK to ignore these suggestions :) + * + * This function is executed when the user wishes to retrieve a list + * of USB devices connected to the system. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*get_device_list)(struct libusb_context *ctx, + struct discovered_devs **discdevs); + + /* Open a device for I/O and other USB operations. The device handle + * is preallocated for you, you can retrieve the device in question + * through handle->dev. + * + * Your backend should allocate any internal resources required for I/O + * and other operations so that those operations can happen (hopefully) + * without hiccup. This is also a good place to inform libusb that it + * should monitor certain file descriptors related to this device - + * see the usbi_add_pollfd() function. + * + * This function should not generate any bus I/O and should not block. + * + * This function is called when the user attempts to obtain a device + * handle for a device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since + * discovery + * - another LIBUSB_ERROR code on other failure + * + * Do not worry about freeing the handle on failed open, the upper layers + * do this for you. + */ + int (*open)(struct libusb_device_handle *handle); + + /* Close a device such that the handle cannot be used again. Your backend + * should destroy any resources that were allocated in the open path. + * This may also be a good place to call usbi_remove_pollfd() to inform + * libusb of any file descriptors associated with this device that should + * no longer be monitored. + * + * This function is called when the user closes a device handle. + */ + void (*close)(struct libusb_device_handle *handle); + + /* Retrieve the device descriptor from a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. Alternatively, you may be able + * to retrieve it from a kernel interface (some Linux setups can do this) + * still without generating bus I/O. + * + * This function is expected to write DEVICE_DESC_LENGTH (18) bytes into + * buffer, which is guaranteed to be big enough. + * + * This function is called when sanity-checking a device before adding + * it to the list of discovered devices, and also when the user requests + * to read the device descriptor. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return 0 on success or a LIBUSB_ERROR code on failure. + */ + int (*get_device_descriptor)(struct libusb_device *device, + unsigned char *buffer, int *host_endian); + + /* Get the ACTIVE configuration descriptor for a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. You may also have to keep track + * of which configuration is active when the user changes it. + * + * This function is expected to write len bytes of data into buffer, which + * is guaranteed to be big enough. If you can only do a partial write, + * return an error code. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * - another LIBUSB_ERROR code on other failure + */ + int (*get_active_config_descriptor)(struct libusb_device *device, + unsigned char *buffer, size_t len, int *host_endian); + + /* Get a specific configuration descriptor for a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. + * + * The requested descriptor is expressed as a zero-based index (i.e. 0 + * indicates that we are requesting the first descriptor). The index does + * not (necessarily) equal the bConfigurationValue of the configuration + * being requested. + * + * This function is expected to write len bytes of data into buffer, which + * is guaranteed to be big enough. If you can only do a partial write, + * return an error code. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return 0 on success or a LIBUSB_ERROR code on failure. + */ + int (*get_config_descriptor)(struct libusb_device *device, + uint8_t config_index, unsigned char *buffer, size_t len, + int *host_endian); + + /* Get the bConfigurationValue for the active configuration for a device. + * Optional. This should only be implemented if you can retrieve it from + * cache (don't generate I/O). + * + * If you cannot retrieve this from cache, either do not implement this + * function, or return LIBUSB_ERROR_NOT_SUPPORTED. This will cause + * libusb to retrieve the information through a standard control transfer. + * + * This function must be non-blocking. + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - LIBUSB_ERROR_NOT_SUPPORTED if the value cannot be retrieved without + * blocking + * - another LIBUSB_ERROR code on other failure. + */ + int (*get_configuration)(struct libusb_device_handle *handle, int *config); + + /* Set the active configuration for a device. + * + * A configuration value of -1 should put the device in unconfigured state. + * + * This function can block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * - LIBUSB_ERROR_BUSY if interfaces are currently claimed (and hence + * configuration cannot be changed) + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure. + */ + int (*set_configuration)(struct libusb_device_handle *handle, int config); + + /* Claim an interface. When claimed, the application can then perform + * I/O to an interface's endpoints. + * + * This function should not generate any bus I/O and should not block. + * Interface claiming is a logical operation that simply ensures that + * no other drivers/applications are using the interface, and after + * claiming, no other drivers/applicatiosn can use the interface because + * we now "own" it. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the interface does not exist + * - LIBUSB_ERROR_BUSY if the interface is in use by another driver/app + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*claim_interface)(struct libusb_device_handle *handle, int interface_number); + + /* Release a previously claimed interface. + * + * This function should also generate a SET_INTERFACE control request, + * resetting the alternate setting of that interface to 0. It's OK for + * this function to block as a result. + * + * You will only ever be asked to release an interface which was + * successfully claimed earlier. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*release_interface)(struct libusb_device_handle *handle, int interface_number); + + /* Set the alternate setting for an interface. + * + * You will only ever be asked to set the alternate setting for an + * interface which was successfully claimed earlier. + * + * It's OK for this function to block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the alternate setting does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*set_interface_altsetting)(struct libusb_device_handle *handle, + int interface_number, int altsetting); + + /* Clear a halt/stall condition on an endpoint. + * + * It's OK for this function to block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*clear_halt)(struct libusb_device_handle *handle, + unsigned char endpoint); + + /* Perform a USB port reset to reinitialize a device. + * + * If possible, the handle should still be usable after the reset + * completes, assuming that the device descriptors did not change during + * reset and all previous interface state can be restored. + * + * If something changes, or you cannot easily locate/verify the resetted + * device, return LIBUSB_ERROR_NOT_FOUND. This prompts the application + * to close the old handle and re-enumerate the device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the device + * has been disconnected since it was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*reset_device)(struct libusb_device_handle *handle); + + /* Determine if a kernel driver is active on an interface. Optional. + * + * The presence of a kernel driver on an interface indicates that any + * calls to claim_interface would fail with the LIBUSB_ERROR_BUSY code. + * + * Return: + * - 0 if no driver is active + * - 1 if a driver is active + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*kernel_driver_active)(struct libusb_device_handle *handle, + int interface_number); + + /* Detach a kernel driver from an interface. Optional. + * + * After detaching a kernel driver, the interface should be available + * for claim. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*detach_kernel_driver)(struct libusb_device_handle *handle, + int interface_number); + + /* Attach a kernel driver to an interface. Optional. + * + * Reattach a kernel driver to the device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - LIBUSB_ERROR_BUSY if a program or driver has claimed the interface, + * preventing reattachment + * - another LIBUSB_ERROR code on other failure + */ + int (*attach_kernel_driver)(struct libusb_device_handle *handle, + int interface_number); + + /* Destroy a device. Optional. + * + * This function is called when the last reference to a device is + * destroyed. It should free any resources allocated in the get_device_list + * path. + */ + void (*destroy_device)(struct libusb_device *dev); + + /* Submit a transfer. Your implementation should take the transfer, + * morph it into whatever form your platform requires, and submit it + * asynchronously. + * + * This function must not block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * - another LIBUSB_ERROR code on other failure + */ + int (*submit_transfer)(struct usbi_transfer *itransfer); + + /* Cancel a previously submitted transfer. + * + * This function must not block. The transfer cancellation must complete + * later, resulting in a call to usbi_handle_transfer_cancellation() + * from the context of handle_events. + */ + int (*cancel_transfer)(struct usbi_transfer *itransfer); + + /* Clear a transfer as if it has completed or cancelled, but do not + * report any completion/cancellation to the library. You should free + * all private data from the transfer as if you were just about to report + * completion or cancellation. + * + * This function might seem a bit out of place. It is used when libusb + * detects a disconnected device - it calls this function for all pending + * transfers before reporting completion (with the disconnect code) to + * the user. Maybe we can improve upon this internal interface in future. + */ + void (*clear_transfer_priv)(struct usbi_transfer *itransfer); + + /* Handle any pending events. This involves monitoring any active + * transfers and processing their completion or cancellation. + * + * The function is passed an array of pollfd structures (size nfds) + * as a result of the poll() system call. The num_ready parameter + * indicates the number of file descriptors that have reported events + * (i.e. the poll() return value). This should be enough information + * for you to determine which actions need to be taken on the currently + * active transfers. + * + * For any cancelled transfers, call usbi_handle_transfer_cancellation(). + * For completed transfers, call usbi_handle_transfer_completion(). + * For control/bulk/interrupt transfers, populate the "transferred" + * element of the appropriate usbi_transfer structure before calling the + * above functions. For isochronous transfers, populate the status and + * transferred fields of the iso packet descriptors of the transfer. + * + * This function should also be able to detect disconnection of the + * device, reporting that situation with usbi_handle_disconnect(). + * + * When processing an event related to a transfer, you probably want to + * take usbi_transfer.lock to prevent races. See the documentation for + * the usbi_transfer structure. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*handle_events)(struct libusb_context *ctx, + struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready); + + /* Get time from specified clock. At least two clocks must be implemented + by the backend: USBI_CLOCK_REALTIME, and USBI_CLOCK_MONOTONIC. + + Description of clocks: + USBI_CLOCK_REALTIME : clock returns time since system epoch. + USBI_CLOCK_MONOTONIC: clock returns time since unspecified start + time (usually boot). + */ + int (*clock_gettime)(int clkid, struct timespec *tp); + +#ifdef USBI_TIMERFD_AVAILABLE + /* clock ID of the clock that should be used for timerfd */ + clockid_t (*get_timerfd_clockid)(void); +#endif + + /* Number of bytes to reserve for per-device private backend data. + * This private data area is accessible through the "os_priv" field of + * struct libusb_device. */ + size_t device_priv_size; + + /* Number of bytes to reserve for per-handle private backend data. + * This private data area is accessible through the "os_priv" field of + * struct libusb_device. */ + size_t device_handle_priv_size; + + /* Number of bytes to reserve for per-transfer private backend data. + * This private data area is accessible by calling + * usbi_transfer_get_os_priv() on the appropriate usbi_transfer instance. + */ + size_t transfer_priv_size; + + /* Mumber of additional bytes for os_priv for each iso packet. + * Can your backend use this? */ + /* FIXME: linux can't use this any more. if other OS's cannot either, + * then remove this */ + size_t add_iso_packet_size; +}; + +extern const struct usbi_os_backend * const usbi_backend; + +extern const struct usbi_os_backend linux_usbfs_backend; +extern const struct usbi_os_backend darwin_backend; +extern const struct usbi_os_backend openbsd_backend; +extern const struct usbi_os_backend windows_backend; + +#endif + diff --git a/interface/external/LibUSB/include/os/darwin_usb.h b/interface/external/LibUSB/include/os/darwin_usb.h new file mode 100644 index 0000000000..59d0a694ce --- /dev/null +++ b/interface/external/LibUSB/include/os/darwin_usb.h @@ -0,0 +1,169 @@ +/* + * darwin backend for libusb 1.0 + * Copyright (C) 2008-2009 Nathan Hjelm + * + * 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 + */ + +#if !defined(LIBUSB_DARWIN_H) +#define LIBUSB_DARWIN_H + +#include "libusbi.h" + +#include +#include +#include +#include + +/* IOUSBInterfaceInferface */ +#if defined (kIOUSBInterfaceInterfaceID300) + +#define usb_interface_t IOUSBInterfaceInterface300 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300 +#define InterfaceVersion 300 + +#elif defined (kIOUSBInterfaceInterfaceID245) + +#define usb_interface_t IOUSBInterfaceInterface245 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245 +#define InterfaceVersion 245 + +#elif defined (kIOUSBInterfaceInterfaceID220) + +#define usb_interface_t IOUSBInterfaceInterface220 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220 +#define InterfaceVersion 220 + +#elif defined (kIOUSBInterfaceInterfaceID197) + +#define usb_interface_t IOUSBInterfaceInterface197 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID197 +#define InterfaceVersion 197 + +#elif defined (kIOUSBInterfaceInterfaceID190) + +#define usb_interface_t IOUSBInterfaceInterface190 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID190 +#define InterfaceVersion 190 + +#elif defined (kIOUSBInterfaceInterfaceID182) + +#define usb_interface_t IOUSBInterfaceInterface182 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID182 +#define InterfaceVersion 182 + +#else + +#error "IOUSBFamily is too old. Please upgrade your OS" + +#endif + +/* IOUSBDeviceInterface */ +#if defined (kIOUSBDeviceInterfaceID320) + +#define usb_device_t IOUSBDeviceInterface320 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID320 +#define DeviceVersion 320 + +#elif defined (kIOUSBDeviceInterfaceID300) + +#define usb_device_t IOUSBDeviceInterface300 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID300 +#define DeviceVersion 300 + +#elif defined (kIOUSBDeviceInterfaceID245) + +#define usb_device_t IOUSBDeviceInterface245 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID245 +#define DeviceVersion 245 + +#elif defined (kIOUSBDeviceInterfaceID197) + +#define usb_device_t IOUSBDeviceInterface197 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID197 +#define DeviceVersion 197 + +#elif defined (kIOUSBDeviceInterfaceID187) + +#define usb_device_t IOUSBDeviceInterface187 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID187 +#define DeviceVersion 187 + +#elif defined (kIOUSBDeviceInterfaceID182) + +#define usb_device_t IOUSBDeviceInterface182 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID182 +#define DeviceVersion 182 + +#else + +#error "IOUSBFamily is too old. Please upgrade your OS" + +#endif + +#if !defined(IO_OBJECT_NULL) +#define IO_OBJECT_NULL ((io_object_t) 0) +#endif + +typedef IOCFPlugInInterface *io_cf_plugin_ref_t; +typedef IONotificationPortRef io_notification_port_t; + +/* private structures */ +struct darwin_device_priv { + IOUSBDeviceDescriptor dev_descriptor; + UInt32 location; + char sys_path[21]; + usb_device_t **device; + int open_count; + UInt8 first_config, active_config; +}; + +struct darwin_device_handle_priv { + int is_open; + CFRunLoopSourceRef cfSource; + int fds[2]; + + struct darwin_interface { + usb_interface_t **interface; + uint8_t num_endpoints; + CFRunLoopSourceRef cfSource; + uint64_t frames[256]; + uint8_t endpoint_addrs[USB_MAXENDPOINTS]; + } interfaces[USB_MAXINTERFACES]; +}; + +struct darwin_transfer_priv { + /* Isoc */ + IOUSBIsocFrame *isoc_framelist; + size_t num_iso_packets; + + /* Control */ +#if !defined (LIBUSB_NO_TIMEOUT_DEVICE) + IOUSBDevRequestTO req; +#else + IOUSBDevRequest req; +#endif + + /* Bulk */ +}; + +enum { + MESSAGE_DEVICE_GONE, + MESSAGE_ASYNC_IO_COMPLETE +}; + + + +#endif diff --git a/interface/external/LibUSB/include/os/linux_usbfs.h b/interface/external/LibUSB/include/os/linux_usbfs.h new file mode 100644 index 0000000000..487acb5ac2 --- /dev/null +++ b/interface/external/LibUSB/include/os/linux_usbfs.h @@ -0,0 +1,139 @@ +/* + * usbfs header structures + * Copyright (C) 2007 Daniel Drake + * Copyright (c) 2001 Johannes Erdfelt + * + * 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 + */ + +#ifndef LIBUSB_USBFS_H +#define LIBUSB_USBFS_H + +#define SYSFS_DEVICE_PATH "/sys/bus/usb/devices" + +struct usbfs_ctrltransfer { + /* keep in sync with usbdevice_fs.h:usbdevfs_ctrltransfer */ + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + + uint32_t timeout; /* in milliseconds */ + + /* pointer to data */ + void *data; +}; + +struct usbfs_bulktransfer { + /* keep in sync with usbdevice_fs.h:usbdevfs_bulktransfer */ + unsigned int ep; + unsigned int len; + unsigned int timeout; /* in milliseconds */ + + /* pointer to data */ + void *data; +}; + +struct usbfs_setinterface { + /* keep in sync with usbdevice_fs.h:usbdevfs_setinterface */ + unsigned int interface; + unsigned int altsetting; +}; + +#define USBFS_MAXDRIVERNAME 255 + +struct usbfs_getdriver { + unsigned int interface; + char driver[USBFS_MAXDRIVERNAME + 1]; +}; + +#define USBFS_URB_SHORT_NOT_OK 0x01 +#define USBFS_URB_ISO_ASAP 0x02 +#define USBFS_URB_BULK_CONTINUATION 0x04 +#define USBFS_URB_QUEUE_BULK 0x10 +#define USBFS_URB_ZERO_PACKET 0x40 + +enum usbfs_urb_type { + USBFS_URB_TYPE_ISO = 0, + USBFS_URB_TYPE_INTERRUPT = 1, + USBFS_URB_TYPE_CONTROL = 2, + USBFS_URB_TYPE_BULK = 3, +}; + +struct usbfs_iso_packet_desc { + unsigned int length; + unsigned int actual_length; + unsigned int status; +}; + +#define MAX_ISO_BUFFER_LENGTH 32768 +#define MAX_BULK_BUFFER_LENGTH 16384 +#define MAX_CTRL_BUFFER_LENGTH 4096 + +struct usbfs_urb { + unsigned char type; + unsigned char endpoint; + int status; + unsigned int flags; + void *buffer; + int buffer_length; + int actual_length; + int start_frame; + int number_of_packets; + int error_count; + unsigned int signr; + void *usercontext; + struct usbfs_iso_packet_desc iso_frame_desc[0]; +}; + +struct usbfs_connectinfo { + unsigned int devnum; + unsigned char slow; +}; + +struct usbfs_ioctl { + int ifno; /* interface 0..N ; negative numbers reserved */ + int ioctl_code; /* MUST encode size + direction of data so the + * macros in give correct values */ + void *data; /* param buffer (in, or out) */ +}; + +struct usbfs_hub_portinfo { + unsigned char numports; + unsigned char port[127]; /* port to device num mapping */ +}; + +#define IOCTL_USBFS_CONTROL _IOWR('U', 0, struct usbfs_ctrltransfer) +#define IOCTL_USBFS_BULK _IOWR('U', 2, struct usbfs_bulktransfer) +#define IOCTL_USBFS_RESETEP _IOR('U', 3, unsigned int) +#define IOCTL_USBFS_SETINTF _IOR('U', 4, struct usbfs_setinterface) +#define IOCTL_USBFS_SETCONFIG _IOR('U', 5, unsigned int) +#define IOCTL_USBFS_GETDRIVER _IOW('U', 8, struct usbfs_getdriver) +#define IOCTL_USBFS_SUBMITURB _IOR('U', 10, struct usbfs_urb) +#define IOCTL_USBFS_DISCARDURB _IO('U', 11) +#define IOCTL_USBFS_REAPURB _IOW('U', 12, void *) +#define IOCTL_USBFS_REAPURBNDELAY _IOW('U', 13, void *) +#define IOCTL_USBFS_CLAIMINTF _IOR('U', 15, unsigned int) +#define IOCTL_USBFS_RELEASEINTF _IOR('U', 16, unsigned int) +#define IOCTL_USBFS_CONNECTINFO _IOW('U', 17, struct usbfs_connectinfo) +#define IOCTL_USBFS_IOCTL _IOWR('U', 18, struct usbfs_ioctl) +#define IOCTL_USBFS_HUB_PORTINFO _IOR('U', 19, struct usbfs_hub_portinfo) +#define IOCTL_USBFS_RESET _IO('U', 20) +#define IOCTL_USBFS_CLEAR_HALT _IOR('U', 21, unsigned int) +#define IOCTL_USBFS_DISCONNECT _IO('U', 22) +#define IOCTL_USBFS_CONNECT _IO('U', 23) + +#endif diff --git a/interface/external/LibUSB/include/os/poll_posix.h b/interface/external/LibUSB/include/os/poll_posix.h new file mode 100644 index 0000000000..0e5e7f5b72 --- /dev/null +++ b/interface/external/LibUSB/include/os/poll_posix.h @@ -0,0 +1,10 @@ +#ifndef LIBUSB_POLL_POSIX_H +#define LIBUSB_POLL_POSIX_H + +#define usbi_write write +#define usbi_read read +#define usbi_close close +#define usbi_pipe pipe +#define usbi_poll poll + +#endif /* LIBUSB_POLL_POSIX_H */ diff --git a/interface/external/LibUSB/include/os/poll_windows.h b/interface/external/LibUSB/include/os/poll_windows.h new file mode 100644 index 0000000000..6e5bf2bcef --- /dev/null +++ b/interface/external/LibUSB/include/os/poll_windows.h @@ -0,0 +1,115 @@ +/* + * Windows compat: POSIX compatibility wrapper + * Copyright (C) 2009-2010 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of poll implementation from libusb-win32, by Stephan Meyer et al. + * + * 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 + * + */ +#pragma once + +#if defined(_MSC_VER) +// disable /W4 MSVC warnings that are benign +#pragma warning(disable:4127) // conditional expression is constant +#endif + +// Handle synchronous completion through the overlapped structure +#if !defined(STATUS_REPARSE) // reuse the REPARSE status code +#define STATUS_REPARSE ((LONG)0x00000104L) +#endif +#define STATUS_COMPLETED_SYNCHRONOUSLY STATUS_REPARSE +#define HasOverlappedIoCompletedSync(lpOverlapped) (((DWORD)(lpOverlapped)->Internal) == STATUS_COMPLETED_SYNCHRONOUSLY) + +#define DUMMY_HANDLE ((HANDLE)(LONG_PTR)-2) + +enum windows_version { + WINDOWS_UNSUPPORTED, + WINDOWS_XP, + WINDOWS_2003, // also includes XP 64 + WINDOWS_VISTA_AND_LATER, +}; +extern enum windows_version windows_version; + +#define MAX_FDS 256 + +#define POLLIN 0x0001 /* There is data to read */ +#define POLLPRI 0x0002 /* There is urgent data to read */ +#define POLLOUT 0x0004 /* Writing now will not block */ +#define POLLERR 0x0008 /* Error condition */ +#define POLLHUP 0x0010 /* Hung up */ +#define POLLNVAL 0x0020 /* Invalid request: fd not open */ + +struct pollfd { + int fd; /* file descriptor */ + short events; /* requested events */ + short revents; /* returned events */ +}; + +// access modes +enum rw_type { + RW_NONE, + RW_READ, + RW_WRITE, +}; + +// fd struct that can be used for polling on Windows +struct winfd { + int fd; // what's exposed to libusb core + HANDLE handle; // what we need to attach overlapped to the I/O op, so we can poll it + OVERLAPPED* overlapped; // what will report our I/O status + enum rw_type rw; // I/O transfer direction: read *XOR* write (NOT BOTH) +}; +extern const struct winfd INVALID_WINFD; + +int usbi_pipe(int pipefd[2]); +int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout); +ssize_t usbi_write(int fd, const void *buf, size_t count); +ssize_t usbi_read(int fd, void *buf, size_t count); +int usbi_close(int fd); + +void init_polling(void); +void exit_polling(void); +struct winfd usbi_create_fd(HANDLE handle, int access_mode); +void usbi_free_fd(int fd); +struct winfd fd_to_winfd(int fd); +struct winfd handle_to_winfd(HANDLE handle); +struct winfd overlapped_to_winfd(OVERLAPPED* overlapped); + +/* + * Timeval operations + */ +#if defined(DDKBUILD) +#include // defines timeval functions on DDK +#endif + +#if !defined(TIMESPEC_TO_TIMEVAL) +#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ + (tv)->tv_sec = (long)(ts)->tv_sec; \ + (tv)->tv_usec = (long)(ts)->tv_nsec / 1000; \ +} +#endif +#if !defined(timersub) +#define timersub(a, b, result) \ +do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ +} while (0) +#endif + diff --git a/interface/external/LibUSB/include/os/threads_posix.h b/interface/external/LibUSB/include/os/threads_posix.h new file mode 100644 index 0000000000..9752208936 --- /dev/null +++ b/interface/external/LibUSB/include/os/threads_posix.h @@ -0,0 +1,48 @@ +/* + * libusb synchronization using POSIX Threads + * + * Copyright (C) 2010 Peter Stuge + * + * 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 + */ + +#ifndef LIBUSB_THREADS_POSIX_H +#define LIBUSB_THREADS_POSIX_H + +#include + +#define usbi_mutex_static_t pthread_mutex_t +#define USBI_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#define usbi_mutex_static_lock pthread_mutex_lock +#define usbi_mutex_static_unlock pthread_mutex_unlock + +#define usbi_mutex_t pthread_mutex_t +#define usbi_mutex_init pthread_mutex_init +#define usbi_mutex_lock pthread_mutex_lock +#define usbi_mutex_unlock pthread_mutex_unlock +#define usbi_mutex_trylock pthread_mutex_trylock +#define usbi_mutex_destroy pthread_mutex_destroy + +#define usbi_cond_t pthread_cond_t +#define usbi_cond_init pthread_cond_init +#define usbi_cond_wait pthread_cond_wait +#define usbi_cond_timedwait pthread_cond_timedwait +#define usbi_cond_broadcast pthread_cond_broadcast +#define usbi_cond_destroy pthread_cond_destroy +#define usbi_cond_signal pthread_cond_signal + +extern int usbi_mutex_init_recursive(pthread_mutex_t *mutex, pthread_mutexattr_t *attr); + +#endif /* LIBUSB_THREADS_POSIX_H */ diff --git a/interface/external/LibUSB/include/os/threads_windows.h b/interface/external/LibUSB/include/os/threads_windows.h new file mode 100644 index 0000000000..7bb144af58 --- /dev/null +++ b/interface/external/LibUSB/include/os/threads_windows.h @@ -0,0 +1,86 @@ +/* + * libusb synchronization on Microsoft Windows + * + * Copyright (C) 2010 Michael Plante + * + * 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 + */ + +#ifndef LIBUSB_THREADS_WINDOWS_H +#define LIBUSB_THREADS_WINDOWS_H + +#define usbi_mutex_static_t volatile LONG +#define USBI_MUTEX_INITIALIZER 0 + +#define usbi_mutex_t HANDLE + +struct usbi_cond_perthread { + struct list_head list; + DWORD tid; + HANDLE event; +}; +struct usbi_cond_t_ { + // Every time a thread touches the CV, it winds up in one of these lists. + // It stays there until the CV is destroyed, even if the thread + // terminates. + struct list_head waiters; + struct list_head not_waiting; +}; +typedef struct usbi_cond_t_ usbi_cond_t; + +// We *were* getting timespec from pthread.h: +#if (!defined(HAVE_STRUCT_TIMESPEC) && !defined(_TIMESPEC_DEFINED)) +#define HAVE_STRUCT_TIMESPEC 1 +#define _TIMESPEC_DEFINED 1 +struct timespec { + long tv_sec; + long tv_nsec; +}; +#endif /* HAVE_STRUCT_TIMESPEC | _TIMESPEC_DEFINED */ + +// We *were* getting ETIMEDOUT from pthread.h: +#ifndef ETIMEDOUT +# define ETIMEDOUT 10060 /* This is the value in winsock.h. */ +#endif + +#define usbi_mutexattr_t void +#define usbi_condattr_t void + +// all Windows mutexes are recursive +#define usbi_mutex_init_recursive(mutex, attr) usbi_mutex_init((mutex), (attr)) + +int usbi_mutex_static_lock(usbi_mutex_static_t *mutex); +int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex); + + +int usbi_mutex_init(usbi_mutex_t *mutex, + const usbi_mutexattr_t *attr); +int usbi_mutex_lock(usbi_mutex_t *mutex); +int usbi_mutex_unlock(usbi_mutex_t *mutex); +int usbi_mutex_trylock(usbi_mutex_t *mutex); +int usbi_mutex_destroy(usbi_mutex_t *mutex); + +int usbi_cond_init(usbi_cond_t *cond, + const usbi_condattr_t *attr); +int usbi_cond_destroy(usbi_cond_t *cond); +int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex); +int usbi_cond_timedwait(usbi_cond_t *cond, + usbi_mutex_t *mutex, + const struct timespec *abstime); +int usbi_cond_broadcast(usbi_cond_t *cond); +int usbi_cond_signal(usbi_cond_t *cond); + +#endif /* LIBUSB_THREADS_WINDOWS_H */ + diff --git a/interface/external/LibUSB/include/os/windows_usb.h b/interface/external/LibUSB/include/os/windows_usb.h new file mode 100644 index 0000000000..ddbd68075b --- /dev/null +++ b/interface/external/LibUSB/include/os/windows_usb.h @@ -0,0 +1,608 @@ +/* + * Windows backend for libusb 1.0 + * Copyright (C) 2009-2010 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * Major code testing contribution by Xiaofan Chen + * + * 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 + */ + +#pragma once + +#if defined(_MSC_VER) +// disable /W4 MSVC warnings that are benign +#pragma warning(disable:4127) // conditional expression is constant +#pragma warning(disable:4100) // unreferenced formal parameter +#pragma warning(disable:4214) // bit field types other than int +#pragma warning(disable:4201) // nameless struct/union +#endif + +// Windows API default is uppercase - ugh! +#if !defined(bool) +#define bool BOOL +#endif +#if !defined(true) +#define true TRUE +#endif +#if !defined(false) +#define false FALSE +#endif + +// Missing from MSVC6 setupapi.h +#if !defined(SPDRP_ADDRESS) +#define SPDRP_ADDRESS 28 +#endif +#if !defined(SPDRP_INSTALL_STATE) +#define SPDRP_INSTALL_STATE 34 +#endif + +#if defined(__CYGWIN__ ) +// cygwin produces a warning unless these prototypes are defined +extern int _snprintf(char *buffer, size_t count, const char *format, ...); +extern char *_strdup(const char *strSource); +// _beginthreadex is MSVCRT => unavailable for cygwin. Fallback to using CreateThread +#define _beginthreadex(a, b, c, d, e, f) CreateThread(a, b, (LPTHREAD_START_ROUTINE)c, d, e, f) +#endif +#define safe_free(p) do {if (p != NULL) {free((void*)p); p = NULL;}} while(0) +#define safe_closehandle(h) do {if (h != INVALID_HANDLE_VALUE) {CloseHandle(h); h = INVALID_HANDLE_VALUE;}} while(0) +#define safe_min(a, b) min((size_t)(a), (size_t)(b)) +#define safe_strcp(dst, dst_max, src, count) do {memcpy(dst, src, safe_min(count, dst_max)); \ + ((char*)dst)[safe_min(count, dst_max)-1] = 0;} while(0) +#define safe_strcpy(dst, dst_max, src) safe_strcp(dst, dst_max, src, safe_strlen(src)+1) +#define safe_strncat(dst, dst_max, src, count) strncat(dst, src, safe_min(count, dst_max - safe_strlen(dst) - 1)) +#define safe_strcat(dst, dst_max, src) safe_strncat(dst, dst_max, src, safe_strlen(src)+1) +#define safe_strcmp(str1, str2) strcmp(((str1==NULL)?"":str1), ((str2==NULL)?"":str2)) +#define safe_strncmp(str1, str2, count) strncmp(((str1==NULL)?"":str1), ((str2==NULL)?"":str2), count) +#define safe_strlen(str) ((str==NULL)?0:strlen(str)) +#define safe_sprintf _snprintf +#define safe_unref_device(dev) do {if (dev != NULL) {libusb_unref_device(dev); dev = NULL;}} while(0) +#define wchar_to_utf8_ms(wstr, str, strlen) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, strlen, NULL, NULL) +static inline void upperize(char* str) { + size_t i; + if (str == NULL) return; + for (i=0; ios_priv; +} + +static inline void windows_device_priv_init(libusb_device* dev) { + struct windows_device_priv* p = _device_priv(dev); + int i; + p->depth = 0; + p->port = 0; + p->parent_dev = NULL; + p->path = NULL; + p->apib = &usb_api_backend[USB_API_UNSUPPORTED]; + p->composite_api_flags = 0; + p->active_config = 0; + p->config_descriptor = NULL; + memset(&(p->dev_descriptor), 0, sizeof(USB_DEVICE_DESCRIPTOR)); + for (i=0; iusb_interface[i].path = NULL; + p->usb_interface[i].apib = &usb_api_backend[USB_API_UNSUPPORTED]; + p->usb_interface[i].nb_endpoints = 0; + p->usb_interface[i].endpoint = NULL; + } +} + +static inline void windows_device_priv_release(libusb_device* dev) { + struct windows_device_priv* p = _device_priv(dev); + int i; + safe_free(p->path); + if ((dev->num_configurations > 0) && (p->config_descriptor != NULL)) { + for (i=0; i < dev->num_configurations; i++) + safe_free(p->config_descriptor[i]); + } + safe_free(p->config_descriptor); + for (i=0; iusb_interface[i].path); + safe_free(p->usb_interface[i].endpoint); + } +} + +struct interface_handle_t { + HANDLE dev_handle; // WinUSB needs an extra handle for the file + HANDLE api_handle; // used by the API to communicate with the device +}; + +struct windows_device_handle_priv { + int active_interface; + struct interface_handle_t interface_handle[USB_MAXINTERFACES]; + int autoclaim_count[USB_MAXINTERFACES]; // For auto-release +}; + +static inline struct windows_device_handle_priv *_device_handle_priv( + struct libusb_device_handle *handle) +{ + return (struct windows_device_handle_priv *) handle->os_priv; +} + +// used for async polling functions +struct windows_transfer_priv { + struct winfd pollable_fd; + uint8_t interface_number; +}; + +// used to match a device driver (including filter drivers) against a supported API +struct driver_lookup { + char list[MAX_KEY_LENGTH+1];// REG_MULTI_SZ list of services (driver) names + const DWORD reg_prop; // SPDRP registry key to use to retreive list + const char* designation; // internal designation (for debug output) +}; + +/* + * API macros - from libusb-win32 1.x + */ +#define DLL_DECLARE_PREFIXNAME(api, ret, prefixname, name, args) \ + typedef ret (api * __dll_##name##_t)args; \ + static __dll_##name##_t prefixname = NULL + +#define DLL_LOAD_PREFIXNAME(dll, prefixname, name, ret_on_failure) \ + do { \ + HMODULE h = GetModuleHandleA(#dll); \ + if (!h) \ + h = LoadLibraryA(#dll); \ + if (!h) { \ + if (ret_on_failure) { return LIBUSB_ERROR_NOT_FOUND; }\ + else { break; } \ + } \ + prefixname = (__dll_##name##_t)GetProcAddress(h, #name); \ + if (prefixname) break; \ + prefixname = (__dll_##name##_t)GetProcAddress(h, #name "A"); \ + if (prefixname) break; \ + prefixname = (__dll_##name##_t)GetProcAddress(h, #name "W"); \ + if (prefixname) break; \ + if(ret_on_failure) \ + return LIBUSB_ERROR_NOT_FOUND; \ + } while(0) + +#define DLL_DECLARE(api, ret, name, args) DLL_DECLARE_PREFIXNAME(api, ret, name, name, args) +#define DLL_LOAD(dll, name, ret_on_failure) DLL_LOAD_PREFIXNAME(dll, name, name, ret_on_failure) +#define DLL_DECLARE_PREFIXED(api, ret, prefix, name, args) DLL_DECLARE_PREFIXNAME(api, ret, prefix##name, name, args) +#define DLL_LOAD_PREFIXED(dll, prefix, name, ret_on_failure) DLL_LOAD_PREFIXNAME(dll, prefix##name, name, ret_on_failure) + +/* OLE32 dependency */ +DLL_DECLARE_PREFIXED(WINAPI, HRESULT, p, CLSIDFromString, (LPCOLESTR, LPCLSID)); + +/* SetupAPI dependencies */ +DLL_DECLARE_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (const GUID*, PCSTR, HWND, DWORD)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInfo, (HDEVINFO, DWORD, PSP_DEVINFO_DATA)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInterfaces, (HDEVINFO, PSP_DEVINFO_DATA, + const GUID*, DWORD, PSP_DEVICE_INTERFACE_DATA)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceInterfaceDetailA, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, + PSP_DEVICE_INTERFACE_DETAIL_DATA_A, DWORD, PDWORD, PSP_DEVINFO_DATA)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiDestroyDeviceInfoList, (HDEVINFO)); +DLL_DECLARE_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDevRegKey, (HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceRegistryPropertyA, (HDEVINFO, + PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD)); +DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegQueryValueExW, (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD)); +DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY)); + +/* + * Windows DDK API definitions. Most of it copied from MinGW's includes + */ +typedef DWORD DEVNODE, DEVINST; +typedef DEVNODE *PDEVNODE, *PDEVINST; +typedef DWORD RETURN_TYPE; +typedef RETURN_TYPE CONFIGRET; + +#define CR_SUCCESS 0x00000000 +#define CR_NO_SUCH_DEVNODE 0x0000000D + +#define USB_DEVICE_DESCRIPTOR_TYPE LIBUSB_DT_DEVICE +#define USB_CONFIGURATION_DESCRIPTOR_TYPE LIBUSB_DT_CONFIG +#define USB_STRING_DESCRIPTOR_TYPE LIBUSB_DT_STRING +#define USB_INTERFACE_DESCRIPTOR_TYPE LIBUSB_DT_INTERFACE +#define USB_ENDPOINT_DESCRIPTOR_TYPE LIBUSB_DT_ENDPOINT + +#define USB_REQUEST_GET_STATUS LIBUSB_REQUEST_GET_STATUS +#define USB_REQUEST_CLEAR_FEATURE LIBUSB_REQUEST_CLEAR_FEATURE +#define USB_REQUEST_SET_FEATURE LIBUSB_REQUEST_SET_FEATURE +#define USB_REQUEST_SET_ADDRESS LIBUSB_REQUEST_SET_ADDRESS +#define USB_REQUEST_GET_DESCRIPTOR LIBUSB_REQUEST_GET_DESCRIPTOR +#define USB_REQUEST_SET_DESCRIPTOR LIBUSB_REQUEST_SET_DESCRIPTOR +#define USB_REQUEST_GET_CONFIGURATION LIBUSB_REQUEST_GET_CONFIGURATION +#define USB_REQUEST_SET_CONFIGURATION LIBUSB_REQUEST_SET_CONFIGURATION +#define USB_REQUEST_GET_INTERFACE LIBUSB_REQUEST_GET_INTERFACE +#define USB_REQUEST_SET_INTERFACE LIBUSB_REQUEST_SET_INTERFACE +#define USB_REQUEST_SYNC_FRAME LIBUSB_REQUEST_SYNCH_FRAME + +#define USB_GET_NODE_INFORMATION 258 +#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260 +#define USB_GET_NODE_CONNECTION_NAME 261 +#define USB_GET_HUB_CAPABILITIES 271 +#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX) +#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274 +#endif +#if !defined(USB_GET_HUB_CAPABILITIES_EX) +#define USB_GET_HUB_CAPABILITIES_EX 276 +#endif + +#ifndef METHOD_BUFFERED +#define METHOD_BUFFERED 0 +#endif +#ifndef FILE_ANY_ACCESS +#define FILE_ANY_ACCESS 0x00000000 +#endif +#ifndef FILE_DEVICE_UNKNOWN +#define FILE_DEVICE_UNKNOWN 0x00000022 +#endif +#ifndef FILE_DEVICE_USB +#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN +#endif + +#ifndef CTL_CODE +#define CTL_CODE(DeviceType, Function, Method, Access)( \ + ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) +#endif + +typedef enum USB_CONNECTION_STATUS { + NoDeviceConnected, + DeviceConnected, + DeviceFailedEnumeration, + DeviceGeneralFailure, + DeviceCausedOvercurrent, + DeviceNotEnoughPower, + DeviceNotEnoughBandwidth, + DeviceHubNestedTooDeeply, + DeviceInLegacyHub +} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS; + +typedef enum USB_HUB_NODE { + UsbHub, + UsbMIParent +} USB_HUB_NODE; + +/* Cfgmgr32.dll interface */ +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG)); + +#define IOCTL_USB_GET_HUB_CAPABILITIES_EX \ + CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_HUB_CAPABILITIES \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_ROOT_HUB_NAME \ + CTL_CODE(FILE_DEVICE_USB, HCD_GET_ROOT_HUB_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_INFORMATION \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_ATTRIBUTES, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_NAME \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) + +// Most of the structures below need to be packed +#pragma pack(push, 1) + +typedef struct USB_INTERFACE_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bInterfaceNumber; + UCHAR bAlternateSetting; + UCHAR bNumEndpoints; + UCHAR bInterfaceClass; + UCHAR bInterfaceSubClass; + UCHAR bInterfaceProtocol; + UCHAR iInterface; +} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR; + +typedef struct USB_CONFIGURATION_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + USHORT wTotalLength; + UCHAR bNumInterfaces; + UCHAR bConfigurationValue; + UCHAR iConfiguration; + UCHAR bmAttributes; + UCHAR MaxPower; +} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR; + +typedef struct USB_CONFIGURATION_DESCRIPTOR_SHORT { + struct { + ULONG ConnectionIndex; + struct { + UCHAR bmRequest; + UCHAR bRequest; + USHORT wValue; + USHORT wIndex; + USHORT wLength; + } SetupPacket; + } req; + USB_CONFIGURATION_DESCRIPTOR data; +} USB_CONFIGURATION_DESCRIPTOR_SHORT; + +typedef struct USB_ENDPOINT_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bEndpointAddress; + UCHAR bmAttributes; + USHORT wMaxPacketSize; + UCHAR bInterval; +} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR; + +typedef struct USB_DESCRIPTOR_REQUEST { + ULONG ConnectionIndex; + struct { + UCHAR bmRequest; + UCHAR bRequest; + USHORT wValue; + USHORT wIndex; + USHORT wLength; + } SetupPacket; +// UCHAR Data[0]; +} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST; + +typedef struct USB_HUB_DESCRIPTOR { + UCHAR bDescriptorLength; + UCHAR bDescriptorType; + UCHAR bNumberOfPorts; + USHORT wHubCharacteristics; + UCHAR bPowerOnToPowerGood; + UCHAR bHubControlCurrent; + UCHAR bRemoveAndPowerMask[64]; +} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR; + +typedef struct USB_ROOT_HUB_NAME { + ULONG ActualLength; + WCHAR RootHubName[1]; +} USB_ROOT_HUB_NAME, *PUSB_ROOT_HUB_NAME; + +typedef struct USB_ROOT_HUB_NAME_FIXED { + ULONG ActualLength; + WCHAR RootHubName[MAX_PATH_LENGTH]; +} USB_ROOT_HUB_NAME_FIXED; + +typedef struct USB_NODE_CONNECTION_NAME { + ULONG ConnectionIndex; + ULONG ActualLength; + WCHAR NodeName[1]; +} USB_NODE_CONNECTION_NAME, *PUSB_NODE_CONNECTION_NAME; + +typedef struct USB_NODE_CONNECTION_NAME_FIXED { + ULONG ConnectionIndex; + ULONG ActualLength; + WCHAR NodeName[MAX_PATH_LENGTH]; +} USB_NODE_CONNECTION_NAME_FIXED; + +typedef struct USB_HUB_NAME_FIXED { + union { + USB_ROOT_HUB_NAME_FIXED root; + USB_NODE_CONNECTION_NAME_FIXED node; + } u; +} USB_HUB_NAME_FIXED; + +typedef struct USB_HUB_INFORMATION { + USB_HUB_DESCRIPTOR HubDescriptor; + BOOLEAN HubIsBusPowered; +} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION; + +typedef struct USB_MI_PARENT_INFORMATION { + ULONG NumberOfInterfaces; +} USB_MI_PARENT_INFORMATION, *PUSB_MI_PARENT_INFORMATION; + +typedef struct USB_NODE_INFORMATION { + USB_HUB_NODE NodeType; + union { + USB_HUB_INFORMATION HubInformation; + USB_MI_PARENT_INFORMATION MiParentInformation; + } u; +} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION; + +typedef struct USB_PIPE_INFO { + USB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + ULONG ScheduleOffset; +} USB_PIPE_INFO, *PUSB_PIPE_INFO; + +typedef struct USB_NODE_CONNECTION_INFORMATION_EX { + ULONG ConnectionIndex; + USB_DEVICE_DESCRIPTOR DeviceDescriptor; + UCHAR CurrentConfigurationValue; + UCHAR Speed; + BOOLEAN DeviceIsHub; + USHORT DeviceAddress; + ULONG NumberOfOpenPipes; + USB_CONNECTION_STATUS ConnectionStatus; +// USB_PIPE_INFO PipeList[0]; +} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX; + +typedef struct USB_HUB_CAP_FLAGS { + ULONG HubIsHighSpeedCapable:1; + ULONG HubIsHighSpeed:1; + ULONG HubIsMultiTtCapable:1; + ULONG HubIsMultiTt:1; + ULONG HubIsRoot:1; + ULONG HubIsArmedWakeOnConnect:1; + ULONG ReservedMBZ:26; +} USB_HUB_CAP_FLAGS, *PUSB_HUB_CAP_FLAGS; + +typedef struct USB_HUB_CAPABILITIES { + ULONG HubIs2xCapable : 1; +} USB_HUB_CAPABILITIES, *PUSB_HUB_CAPABILITIES; + +typedef struct USB_HUB_CAPABILITIES_EX { + USB_HUB_CAP_FLAGS CapabilityFlags; +} USB_HUB_CAPABILITIES_EX, *PUSB_HUB_CAPABILITIES_EX; + +#pragma pack(pop) + +/* winusb.dll interface */ + +#define SHORT_PACKET_TERMINATE 0x01 +#define AUTO_CLEAR_STALL 0x02 +#define PIPE_TRANSFER_TIMEOUT 0x03 +#define IGNORE_SHORT_PACKETS 0x04 +#define ALLOW_PARTIAL_READS 0x05 +#define AUTO_FLUSH 0x06 +#define RAW_IO 0x07 +#define MAXIMUM_TRANSFER_SIZE 0x08 +#define AUTO_SUSPEND 0x81 +#define SUSPEND_DELAY 0x83 +#define DEVICE_SPEED 0x01 +#define LowSpeed 0x01 +#define FullSpeed 0x02 +#define HighSpeed 0x03 + +typedef enum USBD_PIPE_TYPE { + UsbdPipeTypeControl, + UsbdPipeTypeIsochronous, + UsbdPipeTypeBulk, + UsbdPipeTypeInterrupt +} USBD_PIPE_TYPE; + +typedef struct { + USBD_PIPE_TYPE PipeType; + UCHAR PipeId; + USHORT MaximumPacketSize; + UCHAR Interval; +} WINUSB_PIPE_INFORMATION, *PWINUSB_PIPE_INFORMATION; + +#pragma pack(1) +typedef struct { + UCHAR request_type; + UCHAR request; + USHORT value; + USHORT index; + USHORT length; +} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET; +#pragma pack() + +typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; + +DLL_DECLARE(WINAPI, BOOL, WinUsb_Initialize, (HANDLE, PWINUSB_INTERFACE_HANDLE)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_Free, (WINUSB_INTERFACE_HANDLE)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_GetAssociatedInterface, (WINUSB_INTERFACE_HANDLE, UCHAR, PWINUSB_INTERFACE_HANDLE)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_GetDescriptor, (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, USHORT, PUCHAR, ULONG, PULONG)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryInterfaceSettings, (WINUSB_INTERFACE_HANDLE, UCHAR, PUSB_INTERFACE_DESCRIPTOR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryDeviceInformation, (WINUSB_INTERFACE_HANDLE, ULONG, PULONG, PVOID)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_SetCurrentAlternateSetting, (WINUSB_INTERFACE_HANDLE, UCHAR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_GetCurrentAlternateSetting, (WINUSB_INTERFACE_HANDLE, PUCHAR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryPipe, (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, PWINUSB_PIPE_INFORMATION)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_SetPipePolicy, (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, ULONG, PVOID)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_GetPipePolicy, (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, PULONG, PVOID)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_ReadPipe, (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_WritePipe, (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_ControlTransfer, (WINUSB_INTERFACE_HANDLE, WINUSB_SETUP_PACKET, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_ResetPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_AbortPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_FlushPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); diff --git a/interface/external/LibUSB/include/version.h b/interface/external/LibUSB/include/version.h new file mode 100644 index 0000000000..62446da871 --- /dev/null +++ b/interface/external/LibUSB/include/version.h @@ -0,0 +1,18 @@ +/* This file is parsed by m4 and windres and RC.EXE so please keep it simple. */ +#ifndef LIBUSB_MAJOR +#define LIBUSB_MAJOR 1 +#endif +#ifndef LIBUSB_MINOR +#define LIBUSB_MINOR 0 +#endif +#ifndef LIBUSB_MICRO +#define LIBUSB_MICRO 9 +#endif +/* LIBUSB_NANO may be used for Windows internal versioning. 0 means unused. */ +#ifndef LIBUSB_NANO +#define LIBUSB_NANO 0 +#endif +/* LIBUSB_RC is the release candidate suffix. Should normally be empty. */ +#ifndef LIBUSB_RC +#define LIBUSB_RC "" +#endif diff --git a/interface/external/LibUSB/lib/UNIX/libusb-1.0.a b/interface/external/LibUSB/lib/UNIX/libusb-1.0.a new file mode 100644 index 0000000000000000000000000000000000000000..871391de51946ef9a5ef085809f7b5727e1df978 GIT binary patch literal 372942 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 +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// Structure to represent a single 16-bit signed little-endian PCM sample. +typedef struct { + int16_t left; + int16_t right; + int16_t center; + int16_t lfe; + int16_t surround_left; + int16_t surround_right; +} freenect_sample_51; + +/** + * Typedef for "you wanted this microphone data, here it is" event callbacks. + * TODO: Timestamp details + * The format of the unknown stream is as of yet undetermined. + * + * @param dev Device which triggered this callback + * @param num_samples Number of samples provided in each of the audio data arrays (mic[1-4] and cancelled) + * @param mic1 Microphone data for the leftmost microphone: 32-bit PCM little-endian samples at 16kHz. + * @param mic2 Microphone data for the left-middle microphone: 32-bit PCM little-endian samples at 16kHz. + * @param mic3 Microphone data for the right-middle microphone: 32-bit PCM little-endian samples at 16kHz. + * @param mic4 Microphone data for the rightmost microphone: 32-bit PCM little-endian samples at 16kHz. + * @param cancelled Noise-cancelled audio data: 16-bit PCM little-endian samples at 16kHz. + */ +typedef void (*freenect_audio_in_cb)(freenect_device *dev, int num_samples, + int32_t* mic1, int32_t* mic2, + int32_t* mic3, int32_t* mic4, + int16_t* cancelled, void *unknown/*, timestamp_t timestamp*/); + +/** + * Typedef for "you're playing audio, the library needs you to fill up the outgoing audio buffer" event callbacks + * The library will request samples at a rate of 48000Hz. + * + * @param dev Device this callback was triggered for + * @param samples Pointer to the memory where the library expects you to copy the next sample_count freenect_sample_51's to. + * @param sample_count Bidirectional. in: maximum number of samples the driver wants (don't copy in more than this, you'll clobber memory). out: actual number of samples provided to the driver. + */ +typedef void (*freenect_audio_out_cb)(freenect_device *dev, freenect_sample_51* samples, int* sample_count); + +/** + * Set the audio in callback. This is the function called when the library + * has new microphone samples. It will be called approximately 62.5 times per + * second (16kHz sample rate, expect 512 samples/callback) + * + * @param dev Device for which to set the callback + * @param callback Callback function to set + */ +FREENECTAPI void freenect_set_audio_in_callback(freenect_device *dev, freenect_audio_in_cb callback); + +/** + * Set the audio out callback. This is the "tell me what audio you're about + * to play through the speakers so the Kinect can subtract it out" callback for + * a given device. If you choose not set an audio_out_callback, the library + * will send silence to the Kinect for you - it requires data either way. + * + * @param dev Device for which to set the callback + * @param callback Callback function to set + */ +FREENECTAPI void freenect_set_audio_out_callback(freenect_device *dev, freenect_audio_out_cb callback); + +/** + * Start streaming audio for the specified device. + * + * @param dev Device for which to start audio streaming + * + * @return 0 on success, < 0 if error + */ +FREENECTAPI int freenect_start_audio(freenect_device* dev); + +/** + * Stop streaming audio for the specified device. + * + * @param dev Device for which to stop audio streaming + * + * @return 0 on success, < 0 if error + */ +FREENECTAPI int freenect_stop_audio(freenect_device* dev); + +#ifdef __cplusplus +} +#endif diff --git a/interface/external/freenect/include/libfreenect-registration.h b/interface/external/freenect/include/libfreenect-registration.h new file mode 100644 index 0000000000..7ad2b5bb80 --- /dev/null +++ b/interface/external/freenect/include/libfreenect-registration.h @@ -0,0 +1,126 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2011 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// Internal Kinect registration parameters. +/// Structure matches that of the line protocol +/// of the Kinect. +typedef struct { + int32_t dx_center; // not used by mapping algorithm + + int32_t ax; + int32_t bx; + int32_t cx; + int32_t dx; + + int32_t dx_start; + + int32_t ay; + int32_t by; + int32_t cy; + int32_t dy; + + int32_t dy_start; + + int32_t dx_beta_start; + int32_t dy_beta_start; + + int32_t rollout_blank; // not used by mapping algorithm + int32_t rollout_size; // not used by mapping algorithm + + int32_t dx_beta_inc; + int32_t dy_beta_inc; + + int32_t dxdx_start; + int32_t dxdy_start; + int32_t dydx_start; + int32_t dydy_start; + + int32_t dxdxdx_start; + int32_t dydxdx_start; + int32_t dxdxdy_start; + int32_t dydxdy_start; + + int32_t back_comp1; // not used by mapping algorithm + + int32_t dydydx_start; + + int32_t back_comp2; // not used by mapping algorithm + + int32_t dydydy_start; +} freenect_reg_info; + +/// registration padding info (?) +typedef struct { + uint16_t start_lines; + uint16_t end_lines; + uint16_t cropping_lines; +} freenect_reg_pad_info; + +/// internal Kinect zero plane data +typedef struct { + float dcmos_emitter_dist; // Distance between IR camera and IR emitter, in cm. + float dcmos_rcmos_dist; // Distance between IR camera and RGB camera, in cm. + float reference_distance; // The focal length of the IR camera, in mm. + float reference_pixel_size; // The size of a single pixel on the zero plane, in mm. +} freenect_zero_plane_info; + +/// all data needed for depth->RGB mapping +typedef struct { + freenect_reg_info reg_info; + freenect_reg_pad_info reg_pad_info; + freenect_zero_plane_info zero_plane_info; + + double const_shift; + + uint16_t* raw_to_mm_shift; + int32_t* depth_to_rgb_shift; + int32_t (*registration_table)[2]; // A table of 640*480 pairs of x,y values. + // Index first by pixel, then x:0 and y:1. +} freenect_registration; + + +// These allow clients to export registration parameters; proper docs will +// come later +FREENECTAPI freenect_registration freenect_copy_registration(freenect_device* dev); +FREENECTAPI int freenect_destroy_registration(freenect_registration* reg); + +// convenience function to convert a single x-y coordinate pair from camera +// to world coordinates +FREENECTAPI void freenect_camera_to_world(freenect_device* dev, + int cx, int cy, int wz, double* wx, double* wy); + +#ifdef __cplusplus +} +#endif diff --git a/interface/external/freenect/include/libfreenect.h b/interface/external/freenect/include/libfreenect.h new file mode 100644 index 0000000000..13c7c4c538 --- /dev/null +++ b/interface/external/freenect/include/libfreenect.h @@ -0,0 +1,619 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2010 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +#pragma once + +#include + +/* We need struct timeval */ +#ifdef _WIN32 +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define FREENECT_COUNTS_PER_G 819 /**< Ticks per G for accelerometer as set per http://www.kionix.com/Product%20Sheets/KXSD9%20Product%20Brief.pdf */ + +/// Maximum value that a uint16_t pixel will take on in the buffer of any of the FREENECT_DEPTH_MM or FREENECT_DEPTH_REGISTERED frame callbacks +#define FREENECT_DEPTH_MM_MAX_VALUE 10000 +/// Value indicating that this pixel has no data, when using FREENECT_DEPTH_MM or FREENECT_DEPTH_REGISTERED depth modes +#define FREENECT_DEPTH_MM_NO_VALUE 0 +/// Maximum value that a uint16_t pixel will take on in the buffer of any of the FREENECT_DEPTH_11BIT, FREENECT_DEPTH_10BIT, FREENECT_DEPTH_11BIT_PACKED, or FREENECT_DEPTH_10BIT_PACKED frame callbacks +#define FREENECT_DEPTH_RAW_MAX_VALUE 2048 +/// Value indicating that this pixel has no data, when using FREENECT_DEPTH_11BIT, FREENECT_DEPTH_10BIT, FREENECT_DEPTH_11BIT_PACKED, or FREENECT_DEPTH_10BIT_PACKED +#define FREENECT_DEPTH_RAW_NO_VALUE 2047 + +/// Flags representing devices to open when freenect_open_device() is called. +/// In particular, this allows libfreenect to grab only a subset of the devices +/// in the Kinect, so you could (for instance) use libfreenect to handle audio +/// and motor support while letting OpenNI have access to the cameras. +/// If a device is not supported on a particular platform, its flag will be ignored. +typedef enum { + FREENECT_DEVICE_MOTOR = 0x01, + FREENECT_DEVICE_CAMERA = 0x02, + FREENECT_DEVICE_AUDIO = 0x04, +} freenect_device_flags; + +/// A struct used in enumeration to give access to serial numbers, so you can +/// open a particular device by serial rather than depending on index. This +/// is most useful if you have more than one Kinect. +struct freenect_device_attributes; +struct freenect_device_attributes { + struct freenect_device_attributes *next; /**< Next device in the linked list */ + const char* camera_serial; /**< Serial number of this device's camera subdevice */ +}; + +/// Enumeration of available resolutions. +/// Not all available resolutions are actually supported for all video formats. +/// Frame modes may not perfectly match resolutions. For instance, +/// FREENECT_RESOLUTION_MEDIUM is 640x488 for the IR camera. +typedef enum { + FREENECT_RESOLUTION_LOW = 0, /**< QVGA - 320x240 */ + FREENECT_RESOLUTION_MEDIUM = 1, /**< VGA - 640x480 */ + FREENECT_RESOLUTION_HIGH = 2, /**< SXGA - 1280x1024 */ + FREENECT_RESOLUTION_DUMMY = 2147483647, /**< Dummy value to force enum to be 32 bits wide */ +} freenect_resolution; + +/// Enumeration of video frame information states. +/// See http://openkinect.org/wiki/Protocol_Documentation#RGB_Camera for more information. +typedef enum { + FREENECT_VIDEO_RGB = 0, /**< Decompressed RGB mode (demosaicing done by libfreenect) */ + FREENECT_VIDEO_BAYER = 1, /**< Bayer compressed mode (raw information from camera) */ + FREENECT_VIDEO_IR_8BIT = 2, /**< 8-bit IR mode */ + FREENECT_VIDEO_IR_10BIT = 3, /**< 10-bit IR mode */ + FREENECT_VIDEO_IR_10BIT_PACKED = 4, /**< 10-bit packed IR mode */ + FREENECT_VIDEO_YUV_RGB = 5, /**< YUV RGB mode */ + FREENECT_VIDEO_YUV_RAW = 6, /**< YUV Raw mode */ + FREENECT_VIDEO_DUMMY = 2147483647, /**< Dummy value to force enum to be 32 bits wide */ +} freenect_video_format; + +/// Enumeration of depth frame states +/// See http://openkinect.org/wiki/Protocol_Documentation#RGB_Camera for more information. +typedef enum { + FREENECT_DEPTH_11BIT = 0, /**< 11 bit depth information in one uint16_t/pixel */ + FREENECT_DEPTH_10BIT = 1, /**< 10 bit depth information in one uint16_t/pixel */ + FREENECT_DEPTH_11BIT_PACKED = 2, /**< 11 bit packed depth information */ + FREENECT_DEPTH_10BIT_PACKED = 3, /**< 10 bit packed depth information */ + FREENECT_DEPTH_REGISTERED = 4, /**< processed depth data in mm, aligned to 640x480 RGB */ + FREENECT_DEPTH_MM = 5, /**< depth to each pixel in mm, but left unaligned to RGB image */ + FREENECT_DEPTH_DUMMY = 2147483647, /**< Dummy value to force enum to be 32 bits wide */ +} freenect_depth_format; + +/// Structure to give information about the width, height, bitrate, +/// framerate, and buffer size of a frame in a particular mode, as +/// well as the total number of bytes needed to hold a single frame. +typedef struct { + uint32_t reserved; /**< unique ID used internally. The meaning of values may change without notice. Don't touch or depend on the contents of this field. We mean it. */ + freenect_resolution resolution; /**< Resolution this freenect_frame_mode describes, should you want to find it again with freenect_find_*_frame_mode(). */ + union { + int32_t dummy; + freenect_video_format video_format; + freenect_depth_format depth_format; + }; /**< The video or depth format that this freenect_frame_mode describes. The caller should know which of video_format or depth_format to use, since they called freenect_get_*_frame_mode() */ + int32_t bytes; /**< Total buffer size in bytes to hold a single frame of data. Should be equivalent to width * height * (data_bits_per_pixel+padding_bits_per_pixel) / 8 */ + int16_t width; /**< Width of the frame, in pixels */ + int16_t height; /**< Height of the frame, in pixels */ + int8_t data_bits_per_pixel; /**< Number of bits of information needed for each pixel */ + int8_t padding_bits_per_pixel; /**< Number of bits of padding for alignment used for each pixel */ + int8_t framerate; /**< Approximate expected frame rate, in Hz */ + int8_t is_valid; /**< If 0, this freenect_frame_mode is invalid and does not describe a supported mode. Otherwise, the frame_mode is valid. */ +} freenect_frame_mode; + +/// Enumeration of LED states +/// See http://openkinect.org/wiki/Protocol_Documentation#Setting_LED for more information. +typedef enum { + LED_OFF = 0, /**< Turn LED off */ + LED_GREEN = 1, /**< Turn LED to Green */ + LED_RED = 2, /**< Turn LED to Red */ + LED_YELLOW = 3, /**< Turn LED to Yellow */ + LED_BLINK_GREEN = 4, /**< Make LED blink Green */ + // 5 is same as 4, LED blink Green + LED_BLINK_RED_YELLOW = 6, /**< Make LED blink Red/Yellow */ +} freenect_led_options; + + +/// Enumeration of tilt motor status +typedef enum { + TILT_STATUS_STOPPED = 0x00, /**< Tilt motor is stopped */ + TILT_STATUS_LIMIT = 0x01, /**< Tilt motor has reached movement limit */ + TILT_STATUS_MOVING = 0x04, /**< Tilt motor is currently moving to new position */ +} freenect_tilt_status_code; + +/// Data from the tilt motor and accelerometer +typedef struct { + int16_t accelerometer_x; /**< Raw accelerometer data for X-axis, see FREENECT_COUNTS_PER_G for conversion */ + int16_t accelerometer_y; /**< Raw accelerometer data for Y-axis, see FREENECT_COUNTS_PER_G for conversion */ + int16_t accelerometer_z; /**< Raw accelerometer data for Z-axis, see FREENECT_COUNTS_PER_G for conversion */ + int8_t tilt_angle; /**< Raw tilt motor angle encoder information */ + freenect_tilt_status_code tilt_status; /**< State of the tilt motor (stopped, moving, etc...) */ +} freenect_raw_tilt_state; + +struct _freenect_context; +typedef struct _freenect_context freenect_context; /**< Holds information about the usb context. */ + +struct _freenect_device; +typedef struct _freenect_device freenect_device; /**< Holds device information. */ + +// usb backend specific section +typedef void freenect_usb_context; /**< Holds libusb-1.0 context */ +// + +/// If Win32, export all functions for DLL usage +#ifndef _WIN32 + #define FREENECTAPI /**< DLLExport information for windows, set to nothing on other platforms */ +#else + /**< DLLExport information for windows, set to nothing on other platforms */ + #ifdef __cplusplus + #define FREENECTAPI extern "C" __declspec(dllexport) + #else + // this is required when building from a Win32 port of gcc without being + // forced to compile all of the library files (.c) with g++... + #define FREENECTAPI __declspec(dllexport) + #endif +#endif + +/// Enumeration of message logging levels +typedef enum { + FREENECT_LOG_FATAL = 0, /**< Log for crashing/non-recoverable errors */ + FREENECT_LOG_ERROR, /**< Log for major errors */ + FREENECT_LOG_WARNING, /**< Log for warning messages */ + FREENECT_LOG_NOTICE, /**< Log for important messages */ + FREENECT_LOG_INFO, /**< Log for normal messages */ + FREENECT_LOG_DEBUG, /**< Log for useful development messages */ + FREENECT_LOG_SPEW, /**< Log for slightly less useful messages */ + FREENECT_LOG_FLOOD, /**< Log EVERYTHING. May slow performance. */ +} freenect_loglevel; + +/** + * Initialize a freenect context and do any setup required for + * platform specific USB libraries. + * + * @param ctx Address of pointer to freenect context struct to allocate and initialize + * @param usb_ctx USB context to initialize. Can be NULL if not using multiple contexts. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_init(freenect_context **ctx, freenect_usb_context *usb_ctx); + +/** + * Closes the device if it is open, and frees the context + * + * @param ctx freenect context to close/free + * + * @return 0 on success + */ +FREENECTAPI int freenect_shutdown(freenect_context *ctx); + +/// Typedef for logging callback functions +typedef void (*freenect_log_cb)(freenect_context *dev, freenect_loglevel level, const char *msg); + +/** + * Set the log level for the specified freenect context + * + * @param ctx context to set log level for + * @param level log level to use (see freenect_loglevel enum) + */ +FREENECTAPI void freenect_set_log_level(freenect_context *ctx, freenect_loglevel level); + +/** + * Callback for log messages (i.e. for rerouting to a file instead of + * stdout) + * + * @param ctx context to set log callback for + * @param cb callback function pointer + */ +FREENECTAPI void freenect_set_log_callback(freenect_context *ctx, freenect_log_cb cb); + +/** + * Calls the platform specific usb event processor + * + * @param ctx context to process events for + * + * @return 0 on success, other values on error, platform/library dependant + */ +FREENECTAPI int freenect_process_events(freenect_context *ctx); + +/** + * Calls the platform specific usb event processor until either an event occurs + * or the timeout parameter time has passed. If a zero timeval is passed, this + * function will handle any already-pending events, then return immediately. + * + * @param ctx Context to process events for + * @param timeout Pointer to a timeval containing the maximum amount of time to block waiting for events, or zero for nonblocking mode + * + * @return 0 on success, other values on error, platform/library dependant + */ +FREENECTAPI int freenect_process_events_timeout(freenect_context *ctx, struct timeval* timeout); + +/** + * Return the number of kinect devices currently connected to the + * system + * + * @param ctx Context to access device count through + * + * @return Number of devices connected, < 0 on error + */ +FREENECTAPI int freenect_num_devices(freenect_context *ctx); + +/** + * Scans for kinect devices and produces a linked list of their attributes + * (namely, serial numbers), returning the number of devices. + * + * @param ctx Context to scan for kinect devices with + * @param attribute_list Pointer to where this function will store the resultant linked list + * + * @return Number of devices connected, < 0 on error + */ +FREENECTAPI int freenect_list_device_attributes(freenect_context *ctx, struct freenect_device_attributes** attribute_list); + +/** + * Free the linked list produced by freenect_list_device_attributes(). + * + * @param attribute_list Linked list of attributes to free. + */ +FREENECTAPI void freenect_free_device_attributes(struct freenect_device_attributes* attribute_list); + +/** + * Answer which subdevices this library supports. This is most useful for + * wrappers trying to determine whether the underlying library was built with + * audio support or not, so the wrapper can avoid calling functions that do not + * exist. + * + * @return Flags representing the subdevices that the library supports opening (see freenect_device_flags) + */ +FREENECTAPI int freenect_supported_subdevices(void); + +/** + * Set which subdevices any subsequent calls to freenect_open_device() + * should open. This will not affect devices which have already been + * opened. The default behavior, should you choose not to call this + * function at all, is to open all supported subdevices - motor, cameras, + * and audio, if supported on the platform. + * + * @param ctx Context to set future subdevice selection for + * @param subdevs Flags representing the subdevices to select + */ +FREENECTAPI void freenect_select_subdevices(freenect_context *ctx, freenect_device_flags subdevs); + +/** + * Opens a kinect device via a context. Index specifies the index of + * the device on the current state of the bus. Bus resets may cause + * indexes to shift. + * + * @param ctx Context to open device through + * @param dev Device structure to assign opened device to + * @param index Index of the device on the bus + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_open_device(freenect_context *ctx, freenect_device **dev, int index); + +/** + * Opens a kinect device (via a context) associated with a particular camera + * subdevice serial number. This function will fail if no device with a + * matching serial number is found. + * + * @param ctx Context to open device through + * @param dev Device structure to assign opened device to + * @param camera_serial Null-terminated ASCII string containing the serial number of the camera subdevice in the device to open + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_open_device_by_camera_serial(freenect_context *ctx, freenect_device **dev, const char* camera_serial); + +/** + * Closes a device that is currently open + * + * @param dev Device to close + * + * @return 0 on success + */ +FREENECTAPI int freenect_close_device(freenect_device *dev); + +/** + * Set the device user data, for passing generic information into + * callbacks + * + * @param dev Device to attach user data to + * @param user User data to attach + */ +FREENECTAPI void freenect_set_user(freenect_device *dev, void *user); + +/** + * Retrieve the pointer to user data from the device struct + * + * @param dev Device from which to get user data + * + * @return Pointer to user data + */ +FREENECTAPI void *freenect_get_user(freenect_device *dev); + +/// Typedef for depth image received event callbacks +typedef void (*freenect_depth_cb)(freenect_device *dev, void *depth, uint32_t timestamp); +/// Typedef for video image received event callbacks +typedef void (*freenect_video_cb)(freenect_device *dev, void *video, uint32_t timestamp); + +/** + * Set callback for depth information received event + * + * @param dev Device to set callback for + * @param cb Function pointer for processing depth information + */ +FREENECTAPI void freenect_set_depth_callback(freenect_device *dev, freenect_depth_cb cb); + +/** + * Set callback for video information received event + * + * @param dev Device to set callback for + * @param cb Function pointer for processing video information + */ +FREENECTAPI void freenect_set_video_callback(freenect_device *dev, freenect_video_cb cb); + +/** + * Set the buffer to store depth information to. Size of buffer is + * dependant on depth format. See FREENECT_DEPTH_*_SIZE defines for + * more information. + * + * @param dev Device to set depth buffer for. + * @param buf Buffer to store depth information to. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_set_depth_buffer(freenect_device *dev, void *buf); + +/** + * Set the buffer to store depth information to. Size of buffer is + * dependant on video format. See FREENECT_VIDEO_*_SIZE defines for + * more information. + * + * @param dev Device to set video buffer for. + * @param buf Buffer to store video information to. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_set_video_buffer(freenect_device *dev, void *buf); + +/** + * Start the depth information stream for a device. + * + * @param dev Device to start depth information stream for. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_start_depth(freenect_device *dev); + +/** + * Start the video information stream for a device. + * + * @param dev Device to start video information stream for. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_start_video(freenect_device *dev); + +/** + * Stop the depth information stream for a device + * + * @param dev Device to stop depth information stream on. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_stop_depth(freenect_device *dev); + +/** + * Stop the video information stream for a device + * + * @param dev Device to stop video information stream on. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_stop_video(freenect_device *dev); + +/** + * Updates the accelerometer state using a blocking control message + * call. + * + * @param dev Device to get accelerometer data from + * + * @return 0 on success, < 0 on error. Accelerometer data stored to + * device struct. + */ +FREENECTAPI int freenect_update_tilt_state(freenect_device *dev); + +/** + * Retrieve the tilt state from a device + * + * @param dev Device to retrieve tilt state from + * + * @return The tilt state struct of the device + */ +FREENECTAPI freenect_raw_tilt_state* freenect_get_tilt_state(freenect_device *dev); + +/** + * Return the tilt state, in degrees with respect to the horizon + * + * @param state The tilt state struct from a device + * + * @return Current degree of tilt of the device + */ +FREENECTAPI double freenect_get_tilt_degs(freenect_raw_tilt_state *state); + +/** + * Set the tilt state of the device, in degrees with respect to the + * horizon. Uses blocking control message call to update + * device. Function return does not reflect state of device, device + * may still be moving to new position after the function returns. Use + * freenect_get_tilt_status() to find current movement state. + * + * @param dev Device to set tilt state + * @param angle Angle the device should tilt to + * + * @return 0 on success, < 0 on error. + */ +FREENECTAPI int freenect_set_tilt_degs(freenect_device *dev, double angle); + +/** + * Return the movement state of the tilt motor (moving, stopped, etc...) + * + * @param state Raw state struct to get the tilt status code from + * + * @return Status code of the tilt device. See + * freenect_tilt_status_code enum for more info. + */ +FREENECTAPI freenect_tilt_status_code freenect_get_tilt_status(freenect_raw_tilt_state *state); + +/** + * Set the state of the LED. Uses blocking control message call to + * update device. + * + * @param dev Device to set the LED state + * @param option LED state to set on device. See freenect_led_options enum. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_set_led(freenect_device *dev, freenect_led_options option); + +/** + * Get the axis-based gravity adjusted accelerometer state, as laid + * out via the accelerometer data sheet, which is available at + * + * http://www.kionix.com/Product%20Sheets/KXSD9%20Product%20Brief.pdf + * + * @param state State to extract accelerometer data from + * @param x Stores X-axis accelerometer state + * @param y Stores Y-axis accelerometer state + * @param z Stores Z-axis accelerometer state + */ +FREENECTAPI void freenect_get_mks_accel(freenect_raw_tilt_state *state, double* x, double* y, double* z); + +/** + * Get the number of video camera modes supported by the driver. This includes both RGB and IR modes. + * + * @return Number of video modes supported by the driver + */ +FREENECTAPI int freenect_get_video_mode_count(); + +/** + * Get the frame descriptor of the nth supported video mode for the + * video camera. + * + * @param mode_num Which of the supported modes to return information about + * + * @return A freenect_frame_mode describing the nth video mode + */ +FREENECTAPI freenect_frame_mode freenect_get_video_mode(int mode_num); + +/** + * Get the frame descriptor of the current video mode for the specified + * freenect device. + * + * @param dev Which device to return the currently-set video mode for + * + * @return A freenect_frame_mode describing the current video mode of the specified device + */ +FREENECTAPI freenect_frame_mode freenect_get_current_video_mode(freenect_device *dev); + +/** + * Convenience function to return a mode descriptor matching the + * specified resolution and video camera pixel format, if one exists. + * + * @param res Resolution desired + * @param fmt Pixel format desired + * + * @return A freenect_frame_mode that matches the arguments specified, if such a valid mode exists; otherwise, an invalid freenect_frame_mode. + */ +FREENECTAPI freenect_frame_mode freenect_find_video_mode(freenect_resolution res, freenect_video_format fmt); + +/** + * Sets the current video mode for the specified device. If the + * freenect_frame_mode specified is not one provided by the driver + * e.g. from freenect_get_video_mode() or freenect_find_video_mode() + * then behavior is undefined. The current video mode cannot be + * changed while streaming is active. + * + * @param dev Device for which to set the video mode + * @param mode Frame mode to set + * + * @return 0 on success, < 0 if error + */ +FREENECTAPI int freenect_set_video_mode(freenect_device* dev, freenect_frame_mode mode); + +/** + * Get the number of depth camera modes supported by the driver. This includes both RGB and IR modes. + * + * @return Number of depth modes supported by the driver + */ +FREENECTAPI int freenect_get_depth_mode_count(); + +/** + * Get the frame descriptor of the nth supported depth mode for the + * depth camera. + * + * @param mode_num Which of the supported modes to return information about + * + * @return A freenect_frame_mode describing the nth depth mode + */ +FREENECTAPI freenect_frame_mode freenect_get_depth_mode(int mode_num); + +/** + * Get the frame descriptor of the current depth mode for the specified + * freenect device. + * + * @param dev Which device to return the currently-set depth mode for + * + * @return A freenect_frame_mode describing the current depth mode of the specified device + */ +FREENECTAPI freenect_frame_mode freenect_get_current_depth_mode(freenect_device *dev); + +/** + * Convenience function to return a mode descriptor matching the + * specified resolution and depth camera pixel format, if one exists. + * + * @param res Resolution desired + * @param fmt Pixel format desired + * + * @return A freenect_frame_mode that matches the arguments specified, if such a valid mode exists; otherwise, an invalid freenect_frame_mode. + */ +FREENECTAPI freenect_frame_mode freenect_find_depth_mode(freenect_resolution res, freenect_depth_format fmt); + +/** + * Sets the current depth mode for the specified device. The mode + * cannot be changed while streaming is active. + * + * @param dev Device for which to set the depth mode + * @param mode Frame mode to set + * + * @return 0 on success, < 0 if error + */ +FREENECTAPI int freenect_set_depth_mode(freenect_device* dev, const freenect_frame_mode mode); + +#ifdef __cplusplus +} +#endif diff --git a/interface/external/freenect/lib/UNIX/libfreenect.a b/interface/external/freenect/lib/UNIX/libfreenect.a new file mode 100644 index 0000000000000000000000000000000000000000..604faaee0ad87514725d76ef9c2d7291b9f31de7 GIT binary patch literal 65072 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 - Creator diff --git a/interface/external/skeltrack/CMakeLists.txt b/interface/external/skeltrack/CMakeLists.txt new file mode 100644 index 0000000000..21ef04931b --- /dev/null +++ b/interface/external/skeltrack/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 2.8) +include_directories(include) + +# grab the implementation and header files from src dirs +file(GLOB SKELTRACK_SRCS src/*.c include/*.h) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(GLIB2 glib-2.0) + +string(REPLACE ";" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GLIB2_STATIC_CFLAGS} ${GLIB2_STATIC_LDFLAGS}") +message("${CMAKE_C_FLAGS}") + +add_library(skeltrack ${SKELTRACK_SRCS}) + diff --git a/interface/external/skeltrack/COPYING b/interface/external/skeltrack/COPYING new file mode 100644 index 0000000000..65c5ca88a6 --- /dev/null +++ b/interface/external/skeltrack/COPYING @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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/interface/external/skeltrack/include/skeltrack-joint.h b/interface/external/skeltrack/include/skeltrack-joint.h new file mode 100644 index 0000000000..4a9e0ba28c --- /dev/null +++ b/interface/external/skeltrack/include/skeltrack-joint.h @@ -0,0 +1,90 @@ +/* + * skeltrak-joint.h + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 3, or (at your option) any later version as published by + * the Free Software Foundation. + * + * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +#ifndef __SKELTRACK_JOINT_H__ +#define __SKELTRACK_JOINT_H__ + +#include +#include + +G_BEGIN_DECLS + +#define SKELTRACK_TYPE_JOINT (skeltrack_joint_get_type ()) +#define SKELTRACK_JOINT_MAX_JOINTS 7 + +typedef struct _SkeltrackJoint SkeltrackJoint; +typedef SkeltrackJoint **SkeltrackJointList; + +/** + * SkeltrackJointId: + * @SKELTRACK_JOINT_ID_HEAD: The head + * @SKELTRACK_JOINT_ID_LEFT_SHOULDER: The left shoulder + * @SKELTRACK_JOINT_ID_RIGHT_SHOULDER: The right shoulder + * @SKELTRACK_JOINT_ID_LEFT_ELBOW: The left elbow + * @SKELTRACK_JOINT_ID_RIGHT_ELBOW: The right elbow + * @SKELTRACK_JOINT_ID_LEFT_HAND: The left hand + * @SKELTRACK_JOINT_ID_RIGHT_HAND: The right hand + * + * Available joint ids. + **/ +typedef enum { + SKELTRACK_JOINT_ID_HEAD, + SKELTRACK_JOINT_ID_LEFT_SHOULDER, + SKELTRACK_JOINT_ID_RIGHT_SHOULDER, + SKELTRACK_JOINT_ID_LEFT_ELBOW, + SKELTRACK_JOINT_ID_RIGHT_ELBOW, + SKELTRACK_JOINT_ID_LEFT_HAND, + SKELTRACK_JOINT_ID_RIGHT_HAND +} SkeltrackJointId; + +/** + * SkeltrackJoint: + * @id: The id of the joint + * @x: The x coordinate of the joint in the space (in mm) + * @y: The y coordinate of the joint in the space (in mm) + * @z: The z coordinate of the joint in the space (in mm) + * @screen_x: The x coordinate of the joint in the screen (in pixels) + * @screen_y: The y coordinate of the joint in the screen (in pixels) + **/ +struct _SkeltrackJoint +{ + SkeltrackJointId id; + + gint x; + gint y; + gint z; + + gint screen_x; + gint screen_y; +}; + +GType skeltrack_joint_get_type (void); +gpointer skeltrack_joint_copy (SkeltrackJoint *joint); +void skeltrack_joint_free (SkeltrackJoint *joint); +void skeltrack_joint_list_free (SkeltrackJointList list); +SkeltrackJointList skeltrack_joint_list_new (void); +SkeltrackJoint * skeltrack_joint_list_get_joint (SkeltrackJointList list, + SkeltrackJointId id); + +G_END_DECLS + +#endif /* __SKELTRACK_JOINT_H__ */ diff --git a/interface/external/skeltrack/include/skeltrack-skeleton.h b/interface/external/skeltrack/include/skeltrack-skeleton.h new file mode 100644 index 0000000000..11e4344025 --- /dev/null +++ b/interface/external/skeltrack/include/skeltrack-skeleton.h @@ -0,0 +1,95 @@ +/* + * skeltrack.h + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 3, or (at your option) any later version as published by + * the Free Software Foundation. + * + * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +#ifndef __SKELTRACK_SKELETON_H__ +#define __SKELTRACK_SKELETON_H__ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define SKELTRACK_TYPE_SKELETON (skeltrack_skeleton_get_type ()) +#define SKELTRACK_SKELETON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SKELTRACK_TYPE_SKELETON, SkeltrackSkeleton)) +#define SKELTRACK_SKELETON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SKELTRACK_TYPE_SKELETON, SkeltrackSkeletonClass)) +#define SKELTRACK_IS_SKELETON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SKELTRACK_TYPE_SKELETON)) +#define SKELTRACK_IS_SKELETON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SKELTRACK_TYPE_SKELETON)) +#define SKELTRACK_SKELETON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SKELTRACK_TYPE_SKELETON, SkeltrackSkeletonClass)) + +typedef struct _SkeltrackSkeleton SkeltrackSkeleton; +typedef struct _SkeltrackSkeletonClass SkeltrackSkeletonClass; +typedef struct _SkeltrackSkeletonPrivate SkeltrackSkeletonPrivate; + +struct _SkeltrackSkeleton +{ + GObject parent; + + /*< private >*/ + SkeltrackSkeletonPrivate *priv; +}; + +/** + * SkeltrackSkeletonClass: + **/ +struct _SkeltrackSkeletonClass +{ + GObjectClass parent_class; +}; + +GType skeltrack_skeleton_get_type (void) G_GNUC_CONST; + +SkeltrackSkeleton * skeltrack_skeleton_new (void); + +void skeltrack_skeleton_track_joints (SkeltrackSkeleton *self, + guint16 *buffer, + guint width, + guint height, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +SkeltrackJointList skeltrack_skeleton_track_joints_finish (SkeltrackSkeleton *self, + GAsyncResult *result, + GError **error); + +SkeltrackJointList skeltrack_skeleton_track_joints_sync (SkeltrackSkeleton *self, + guint16 *buffer, + guint width, + guint height, + GCancellable *cancellable, + GError **error); + +void skeltrack_skeleton_get_focus_point (SkeltrackSkeleton *self, + gint *x, + gint *y, + gint *z); + +void skeltrack_skeleton_set_focus_point (SkeltrackSkeleton *self, + gint x, + gint y, + gint z); + +G_END_DECLS + +#endif /* __SKELTRACK_SKELETON_H__ */ diff --git a/interface/external/skeltrack/include/skeltrack-smooth.h b/interface/external/skeltrack/include/skeltrack-smooth.h new file mode 100644 index 0000000000..ea99bfc7ae --- /dev/null +++ b/interface/external/skeltrack/include/skeltrack-smooth.h @@ -0,0 +1,36 @@ +/* + * skeltrack-smooth.h + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * 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 3 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 at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +#include "skeltrack-joint.h" + +typedef struct { + SkeltrackJointList smoothed_joints; + SkeltrackJointList trend_joints; + guint joints_persistency; + gfloat smoothing_factor; + guint joints_persistency_counter[SKELTRACK_JOINT_MAX_JOINTS]; +} SmoothData; + +void reset_joints_persistency_counter (SmoothData *smooth_data); + +void smooth_joints (SmoothData *data, + SkeltrackJointList new_joints); diff --git a/interface/external/skeltrack/include/skeltrack-util.h b/interface/external/skeltrack/include/skeltrack-util.h new file mode 100644 index 0000000000..540aee7f33 --- /dev/null +++ b/interface/external/skeltrack/include/skeltrack-util.h @@ -0,0 +1,127 @@ +/* + * skeltrack-util.h + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * 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 3 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 at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +#include +#include "skeltrack-joint.h" + +typedef struct _Label Label; +typedef struct _Node Node; + +struct _Label { + gint index; + Label *parent; + GList *nodes; + Node *bridge_node; + Node *to_node; + gint lower_screen_y; + gint higher_z; + gint lower_z; + gdouble normalized_num_nodes; +}; + +struct _Node { + gint i; + gint j; + gint x; + gint y; + gint z; + GList *neighbors; + GList *linked_nodes; + Label *label; +}; + +Node * get_closest_node_to_joint (GList *extremas, + SkeltrackJoint *joint, + gint *distance); + +Node * get_closest_node (GList *node_list, Node *from); + +Node * get_closest_torso_node (GList *node_list, + Node *from, + Node *head); + +Label * get_main_component (GList *node_list, + Node *from, + gdouble min_normalized_nr_nodes); + +Label * label_find (Label *label); + +void label_union (Label *a, Label *b); + +gint get_distance (Node *a, Node *b); + +void free_label (Label *label); + +void clean_labels (GList *labels); + +void free_node (Node *node, + gboolean unlink_node_first); + +void clean_nodes (GList *nodes); + +GList * remove_nodes_with_label (GList *nodes, + Node **node_matrix, + gint width, + Label *label); + +Label * get_lowest_index_label (Label **neighbor_labels); + +Label * new_label (gint index); + +void join_components_to_main (GList *nodes, + Label *lowest_component_label, + guint horizontal_max_distance, + guint depth_max_distance, + guint graph_distance_threshold); + +void set_joint_from_node (SkeltrackJointList *joints, + Node *node, + SkeltrackJointId id, + gint dimension_reduction); + +gint * create_new_dist_matrix (gint matrix_size); + +gboolean dijkstra_to (GList *nodes, + Node *source, + Node *target, + gint width, + gint height, + gint *distances, + Node **previous); + +void convert_screen_coords_to_mm (guint width, + guint height, + guint dimension_reduction, + guint i, + guint j, + gint z, + gint *x, + gint *y); + +void convert_mm_to_screen_coords (guint width, + guint height, + guint dimension_reduction, + gint x, + gint y, + gint z, + guint *i, + guint *j); diff --git a/interface/external/skeltrack/include/skeltrack.h b/interface/external/skeltrack/include/skeltrack.h new file mode 100644 index 0000000000..8e1c572303 --- /dev/null +++ b/interface/external/skeltrack/include/skeltrack.h @@ -0,0 +1,28 @@ +/* + * skeltrack.h + * + * skeltrack - A GObject wrapper of the libfreenect library + * Copyright (C) 2011 Igalia S.L. + * + * Authors: + * Joaquim Manuel Pereira Rocha + * Eduardo Lima Mitev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 3, or (at your option) any later version as published by + * the Free Software Foundation. + * + * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +#ifndef __SKELTRACK_H__ +#define __SKELTRACK_H__ + +#include + +#endif /* __SKELTRACK_H__ */ diff --git a/interface/external/skeltrack/lib/UNIX/libskeltrack.a b/interface/external/skeltrack/lib/UNIX/libskeltrack.a new file mode 100644 index 0000000000000000000000000000000000000000..f34c83224bfaeed07d09a6a02e47e802ba075ea5 GIT binary patch literal 53760 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 literal 0 HcmV?d00001 diff --git a/interface/external/skeltrack/src/skeltrack-joint.c b/interface/external/skeltrack/src/skeltrack-joint.c new file mode 100644 index 0000000000..6310c6a789 --- /dev/null +++ b/interface/external/skeltrack/src/skeltrack-joint.c @@ -0,0 +1,159 @@ +/* + * skeltrack-joint.c + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * 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 3 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 at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +/** + * SECTION:skeltrack-joint + * @short_description: Data structure that holds information about + * a skeleton joint. + * + * A #SkeltrackJoint is built automatically by #SkeltrackSkeleton when + * it finds a skeleton joint and can be used to get information about it. + * Each #SkeltrackJoint holds an id, given by #SkeltrackJointId that indicates + * which of the human skeleton joints it represents. + * + * Spacial information about a joint is given by the @x, @y and @z coordinates. + * To represent the joint in a 2D, the variables @screen_x and + * @screen_y will indicate the joint's position in the screen and are calculated + * taking into account the #SkeltrackSkeleton:dimension-reduction (it will + * be multiplied by this value). + * + * The tracked list of joints is represented by #SkeltrackJointList and given + * by skeltrack_skeleton_track_joints_finish(). + * To get a #SkeltrackJoint from a #SkeltrackJointList object, use the + * skeltrack_joint_list_get_joint() indicating the needed #SkeltrackJointId. + * + * A #SkeltrackJointList can be freed by using skeltrack_joint_list_free(). + * A #SkeltrackJoint can be copied by skeltrack_joint_copy() and freed by + * skeltrack_joint_free(). + **/ + +#include +#include "skeltrack-joint.h" + +/** + * skeltrack_joint_get_type: + * + * Returns: The registered #GType for #SkeltrackJoint boxed type + **/ +GType +skeltrack_joint_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + type = g_boxed_type_register_static ("SkeltrackJoint", + (GBoxedCopyFunc) skeltrack_joint_copy, + (GBoxedFreeFunc) skeltrack_joint_free); + return type; +} + +/** + * skeltrack_joint_copy: + * @joint: The #SkeltrackJoint to copy + * + * Makes an exact copy of a #SkeltrackJoint object. + * + * Returns: (transfer full): A newly created #SkeltrackJoint. Use + * skeltrack_joint_free() to free it. + **/ +gpointer +skeltrack_joint_copy (SkeltrackJoint *joint) +{ + SkeltrackJoint *new_joint; + + if (joint == NULL) + return NULL; + + new_joint = g_slice_new0 (SkeltrackJoint); + memcpy (new_joint, joint, sizeof (SkeltrackJoint)); + + return new_joint; +} + +/** + * skeltrack_joint_free: + * @joint: The #SkeltrackJoint to free + * + * Frees a #SkeltrackJoint object. + **/ +void +skeltrack_joint_free (SkeltrackJoint *joint) +{ + g_slice_free (SkeltrackJoint, joint); +} + + +/** + * skeltrack_joint_list_free: + * @list: The #SkeltrackJointList to free + * + * Frees a #SkeltrackJointList object and each #SkeltrackJoint + * in it. + **/ +void +skeltrack_joint_list_free (SkeltrackJointList list) +{ + gint i; + + if (list == NULL) + return; + + for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) + { + g_slice_free (SkeltrackJoint, list[i]); + } + g_slice_free1 (SKELTRACK_JOINT_MAX_JOINTS * sizeof (SkeltrackJoint *), list); +} + +/** + * skeltrack_joint_list_get_joint: + * @list: The #SkeltrackJointList + * @id: The #SkeltrackJointId of the joint to get + * + * Gets a joint from a list of skeleton joints. The joint + * returned needs to be freed by using skeltrack_joint_free() or, + * alternatively, the whole list and its joints can be freed by using + * skeltrack_joint_list_free(). + * + * Returns: (transfer full): The #SkeltrackJoint that corresponds to + * the given @id or %NULL if that joint wasn't found. + **/ +SkeltrackJoint * +skeltrack_joint_list_get_joint (SkeltrackJointList list, SkeltrackJointId id) +{ + return list[id]; +} + +/** + * skeltrack_joint_list_new: + * + * Created a new list of #SkeltrackJointsList with its joints as #NULL. + * When it is no longer needed, free it with skeltrack_joint_list_free(). + * + * Returns: (transfer full): A newly allocated #SkeltrackJointList + **/ +SkeltrackJointList +skeltrack_joint_list_new (void) +{ + return (SkeltrackJointList) g_slice_alloc0 (SKELTRACK_JOINT_MAX_JOINTS * + sizeof (SkeltrackJoint *)); +} diff --git a/interface/external/skeltrack/src/skeltrack-skeleton.c b/interface/external/skeltrack/src/skeltrack-skeleton.c new file mode 100644 index 0000000000..9373af56a8 --- /dev/null +++ b/interface/external/skeltrack/src/skeltrack-skeleton.c @@ -0,0 +1,2027 @@ +/* + * skeltrack-skeleton.c + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * 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 3 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 at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +/** + * SECTION:skeltrack-skeleton + * @short_description: Object that tracks the joints in a human skeleton + * + * This object tries to detect joints of the human skeleton. + * + * To track the joints, first create an instance of #SkeltrackSkeleton using + * skeltrack_skeleton_new() and then set a buffer from where the joints will + * be retrieved using the asynchronous function + * skeltrack_skeleton_track_joints() and get the list of joints using + * skeltrack_skeleton_track_joints_finish(). + * + * A common use case is to use this library together with a Kinect device so + * an easy way to retrieve the needed buffer is to use the GFreenect library. + * + * It currently tracks the joints identified by #SkeltrackJointId . + * + * Tracking the skeleton joints can be computational heavy so it is advised that + * the given buffer's dimension is reduced before setting it. To do it, + * simply choose the reduction factor and loop through the original buffer + * (using this factor as a step) and set the reduced buffer's values accordingly. + * The #SkeltrackSkeleton:dimension-reduction property holds this reduction + * value and should be changed to the reduction factor used (alternatively you + * can retrieve its default value and use it in the reduction, if it fits your + * needs). + * + * The skeleton tracking uses a few heuristics that proved to work well for + * tested cases but they can be tweaked by changing the following properties: + * #SkeltrackSkeleton:graph-distance-threshold , + * #SkeltrackSkeleton:graph-minimum-number-nodes , + * #SkeltrackSkeleton:hands-minimum-distance , + * #SkeltrackSkeleton:shoulders-arc-start-point , + * #SkeltrackSkeleton:shoulders-arc-length , + * #SkeltrackSkeleton:shoulders-circumference-radius , + * #SkeltrackSkeleton:shoulders-search-step . + **/ +#include +#include +#include + +#include "skeltrack-skeleton.h" +#include "skeltrack-smooth.h" +#include "skeltrack-util.h" + +#define SKELTRACK_SKELETON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + SKELTRACK_TYPE_SKELETON, \ + SkeltrackSkeletonPrivate)) + +#define DIMENSION_REDUCTION 16 +#define GRAPH_DISTANCE_THRESHOLD 150 +#define GRAPH_MINIMUM_NUMBER_OF_NODES 5 +#define HANDS_MINIMUM_DISTANCE 550 +#define SHOULDERS_CIRCUMFERENCE_RADIUS 300 +#define SHOULDERS_ARC_START_POINT 100 +#define SHOULDERS_ARC_LENGTH 250 +#define SHOULDERS_SEARCH_STEP 0.05 +#define JOINTS_PERSISTENCY_DEFAULT 3 +#define SMOOTHING_FACTOR_DEFAULT .5 +#define ENABLE_SMOOTHING_DEFAULT TRUE +#define DEFAULT_FOCUS_POINT_Z 1000 +#define TORSO_MINIMUM_NUMBER_NODES_DEFAULT 16.0 +#define EXTREMA_SPHERE_RADIUS 300 + +/* private data */ +struct _SkeltrackSkeletonPrivate +{ + guint16 *buffer; + guint buffer_width; + guint buffer_height; + + GAsyncResult *track_joints_result; + GMutex track_joints_mutex; + + GList *graph; + GList *labels; + Node **node_matrix; + gint *distances_matrix; + GList *main_component; + + guint16 dimension_reduction; + guint16 distance_threshold; + guint16 min_nr_nodes; + + guint16 hands_minimum_distance; + + guint16 shoulders_circumference_radius; + guint16 shoulders_arc_start_point; + guint16 shoulders_arc_length; + gfloat shoulders_search_step; + + guint16 extrema_sphere_radius; + + Node *focus_node; + + gboolean enable_smoothing; + SmoothData smooth_data; + + gfloat torso_minimum_number_nodes; + + SkeltrackJoint *previous_head; +}; + +/* Currently searches for head and hands */ +static const guint NR_EXTREMAS_TO_SEARCH = 3; + +/* properties */ +enum + { + PROP_0, + PROP_DIMENSION_REDUCTION, + PROP_GRAPH_DISTANCE_THRESHOLD, + PROP_GRAPH_MIN_NR_NODES, + PROP_HANDS_MINIMUM_DISTANCE, + PROP_SHOULDERS_CIRCUMFERENCE_RADIUS, + PROP_SHOULDERS_ARC_START_POINT, + PROP_SHOULDERS_ARC_LENGTH, + PROP_SHOULDERS_SEARCH_STEP, + PROP_EXTREMA_SPHERE_RADIUS, + PROP_SMOOTHING_FACTOR, + PROP_JOINTS_PERSISTENCY, + PROP_ENABLE_SMOOTHING, + PROP_TORSO_MINIMUM_NUMBER_NODES + }; + + +static void skeltrack_skeleton_class_init (SkeltrackSkeletonClass *class); +static void skeltrack_skeleton_init (SkeltrackSkeleton *self); +static void skeltrack_skeleton_finalize (GObject *obj); +static void skeltrack_skeleton_dispose (GObject *obj); + +static void skeltrack_skeleton_set_property (GObject *obj, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void skeltrack_skeleton_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec); + + +static void clean_tracking_resources (SkeltrackSkeleton *self); + +G_DEFINE_TYPE (SkeltrackSkeleton, skeltrack_skeleton, G_TYPE_OBJECT) + +static void +skeltrack_skeleton_class_init (SkeltrackSkeletonClass *class) +{ + GObjectClass *obj_class; + + obj_class = G_OBJECT_CLASS (class); + + obj_class->dispose = skeltrack_skeleton_dispose; + obj_class->finalize = skeltrack_skeleton_finalize; + obj_class->get_property = skeltrack_skeleton_get_property; + obj_class->set_property = skeltrack_skeleton_set_property; + + /* install properties */ + + /** + * SkeltrackSkeleton:dimension-reduction + * + * The value by which the dimension of the buffer was reduced + * (in case it was). + **/ + g_object_class_install_property (obj_class, + PROP_DIMENSION_REDUCTION, + g_param_spec_uint ("dimension-reduction", + "Dimension reduction", + "The dimension reduction value", + 1, + 1024, + DIMENSION_REDUCTION, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:graph-distance-threshold + * + * The value (in mm) for the distance threshold between each node and its + * neighbors. This means that a node in the graph will only be connected + * to another if they aren't farther apart then this value. + **/ + g_object_class_install_property (obj_class, + PROP_GRAPH_DISTANCE_THRESHOLD, + g_param_spec_uint ("graph-distance-threshold", + "Graph's distance threshold", + "The distance threshold between " + "each node.", + 1, + G_MAXUINT16, + GRAPH_DISTANCE_THRESHOLD, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:graph-minimum-number-nodes + * + * The minimum number of nodes each of the graph's components + * should have (when it is not fully connected). + **/ + g_object_class_install_property (obj_class, + PROP_GRAPH_MIN_NR_NODES, + g_param_spec_uint ("graph-minimum-number-nodes", + "Graph's minimum number of nodes", + "The minimum number of nodes " + "of the graph's components ", + 1, + G_MAXUINT16, + GRAPH_MINIMUM_NUMBER_OF_NODES, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:hands-minimum-distance + * + * The minimum distance (in mm) that each hand should be from its + * respective shoulder. + **/ + g_object_class_install_property (obj_class, + PROP_HANDS_MINIMUM_DISTANCE, + g_param_spec_uint ("hands-minimum-distance", + "Hands' minimum distance from the " + "shoulders", + "The minimum distance (in mm) that " + "each hand should be from its " + "respective shoulder.", + 300, + G_MAXUINT, + HANDS_MINIMUM_DISTANCE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:shoulders-circumference-radius + * + * The radius of the circumference (in mm) from the head with which + * to look for the shoulders. + **/ + g_object_class_install_property (obj_class, + PROP_SHOULDERS_CIRCUMFERENCE_RADIUS, + g_param_spec_uint ("shoulders-circumference-radius", + "Shoulders' circumference radius", + "The radius of the circumference " + "(in mm) from the head with which " + "to look for the shoulders.", + 1, + G_MAXUINT16, + SHOULDERS_CIRCUMFERENCE_RADIUS, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:shoulders-arc-start-point + * + * The starting point (in mm) of the arc (from the bottom of the + * shoulders' circumference) where the shoulders will be searched for. + * This point is used together with the + * SkeltrackSkeleton::shoulders-arc-length to determine the arc + * where the shoulders' points will be looked for. + **/ + g_object_class_install_property (obj_class, + PROP_SHOULDERS_ARC_START_POINT, + g_param_spec_uint ("shoulders-arc-start-point", + "Shoulders' arc start point", + "The starting point (in mm) of the " + "arc from the bottom of the " + "shoulders' circumference where " + "the shoulders will be searched for.", + 1, + G_MAXUINT16, + SHOULDERS_ARC_START_POINT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:shoulders-arc-length + * + * The length (in mm) of the arc where the shoulders will be searched. + * This length is used together with the + * SkeltrackSkeleton::shoulders-arc-start-point to determine the arc + * where the shoulders' points will be looked for. + **/ + g_object_class_install_property (obj_class, + PROP_SHOULDERS_ARC_LENGTH, + g_param_spec_uint ("shoulders-arc-length", + "Shoulders' arc length", + "The length (in mm) of the arc " + "where the shoulders will be " + "searched.", + 1, + G_MAXUINT16, + SHOULDERS_ARC_LENGTH, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + + /** + * SkeltrackSkeleton:shoulders-search-step + * + * The step considered for sampling the shoulders' circumference + * when searching for the shoulders. + **/ + g_object_class_install_property (obj_class, + PROP_SHOULDERS_SEARCH_STEP, + g_param_spec_float ("shoulders-search-step", + "Shoulders' search step", + "The step considered for sampling " + "the shoulders' circumference " + "when searching for the shoulders.", + .01, + M_PI, + .01, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:smoothing-factor + * + * The factor by which the joints should be smoothed. This refers to + * Holt's Double Exponential Smoothing and determines how the current and + * previous data and trend will be used. A value closer to 0 will produce smoother + * results but increases latency. + **/ + g_object_class_install_property (obj_class, + PROP_SMOOTHING_FACTOR, + g_param_spec_float ("smoothing-factor", + "Smoothing factor", + "The factor by which the joints values" + "should be smoothed.", + .0, + 1.0, + .5, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:joints-persistency + * + * The number of times that a joint can be null until its previous + * value is discarded. For example, if this property is 3, the last value for + * a joint will keep being used until the new value for this joint is null for + * 3 consecutive times. + * + **/ + g_object_class_install_property (obj_class, + PROP_JOINTS_PERSISTENCY, + g_param_spec_uint ("joints-persistency", + "Joints persistency", + "The number of times that a joint " + "can be null until its previous " + "value is discarded", + 0, + G_MAXUINT16, + JOINTS_PERSISTENCY_DEFAULT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:enable-smoothing + * + * Whether smoothing the joints should be applied or not. + * + **/ + g_object_class_install_property (obj_class, + PROP_ENABLE_SMOOTHING, + g_param_spec_boolean ("enable-smoothing", + "Enable smoothing", + "Whether smoothing should be " + "applied or not", + ENABLE_SMOOTHING_DEFAULT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:torso-minimum-number-nodes + * + * Minimum number of nodes for a component to be considered torso. + * + **/ + g_object_class_install_property (obj_class, + PROP_TORSO_MINIMUM_NUMBER_NODES, + g_param_spec_float ("torso-minimum-number-nodes", + "Torso minimum number of nodes", + "Minimum number of nodes for a " + "component to be considered " + "torso", + 0, + G_MAXUINT16, + TORSO_MINIMUM_NUMBER_NODES_DEFAULT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:extrema-sphere-radius + * + * The radius of the sphere around the extremas (in mm). + * + * Points inside this sphere are considered for calculating the average position + * of the extrema. If the value is 0, no averaging is done. + **/ + g_object_class_install_property (obj_class, + PROP_EXTREMA_SPHERE_RADIUS, + g_param_spec_uint ("extrema-sphere-radius", + "Extrema sphere radius", + "The radius of the sphere around " + "the extremas (in mm).", + 0, + G_MAXUINT16, + EXTREMA_SPHERE_RADIUS, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + + /* add private structure */ + g_type_class_add_private (obj_class, sizeof (SkeltrackSkeletonPrivate)); +} + +static void +skeltrack_skeleton_init (SkeltrackSkeleton *self) +{ + guint i; + SkeltrackSkeletonPrivate *priv; + + priv = SKELTRACK_SKELETON_GET_PRIVATE (self); + self->priv = priv; + + priv->buffer = NULL; + priv->buffer_width = 0; + priv->buffer_height = 0; + + priv->graph = NULL; + priv->labels = NULL; + priv->main_component = NULL; + priv->node_matrix = NULL; + priv->distances_matrix = NULL; + + priv->dimension_reduction = DIMENSION_REDUCTION; + priv->distance_threshold = GRAPH_DISTANCE_THRESHOLD; + + priv->min_nr_nodes = GRAPH_MINIMUM_NUMBER_OF_NODES; + + priv->hands_minimum_distance = HANDS_MINIMUM_DISTANCE; + + priv->shoulders_circumference_radius = SHOULDERS_CIRCUMFERENCE_RADIUS; + priv->shoulders_arc_start_point = SHOULDERS_ARC_START_POINT; + priv->shoulders_arc_length = SHOULDERS_ARC_LENGTH; + priv->shoulders_search_step = SHOULDERS_SEARCH_STEP; + + priv->extrema_sphere_radius = EXTREMA_SPHERE_RADIUS; + + priv->focus_node = g_slice_new0 (Node); + priv->focus_node->x = 0; + priv->focus_node->y = 0; + priv->focus_node->z = DEFAULT_FOCUS_POINT_Z; + + priv->track_joints_result = NULL; + + g_mutex_init (&priv->track_joints_mutex); + + priv->enable_smoothing = ENABLE_SMOOTHING_DEFAULT; + priv->smooth_data.smoothing_factor = SMOOTHING_FACTOR_DEFAULT; + priv->smooth_data.smoothed_joints = NULL; + priv->smooth_data.trend_joints = NULL; + priv->smooth_data.joints_persistency = JOINTS_PERSISTENCY_DEFAULT; + for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) + priv->smooth_data.joints_persistency_counter[i] = JOINTS_PERSISTENCY_DEFAULT; + + priv->torso_minimum_number_nodes = TORSO_MINIMUM_NUMBER_NODES_DEFAULT; + + priv->previous_head = NULL; +} + +static void +skeltrack_skeleton_dispose (GObject *obj) +{ + /* TODO: cancel any cancellable to interrupt joints tracking operation */ + + G_OBJECT_CLASS (skeltrack_skeleton_parent_class)->dispose (obj); +} + +static void +skeltrack_skeleton_finalize (GObject *obj) +{ + SkeltrackSkeleton *self = SKELTRACK_SKELETON (obj); + + g_mutex_clear (&self->priv->track_joints_mutex); + + skeltrack_joint_list_free (self->priv->smooth_data.smoothed_joints); + skeltrack_joint_list_free (self->priv->smooth_data.trend_joints); + + skeltrack_joint_free (self->priv->previous_head); + + clean_tracking_resources (self); + + g_slice_free (Node, self->priv->focus_node); + + G_OBJECT_CLASS (skeltrack_skeleton_parent_class)->finalize (obj); +} + +static void +skeltrack_skeleton_set_property (GObject *obj, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SkeltrackSkeleton *self; + + self = SKELTRACK_SKELETON (obj); + + switch (prop_id) + { + case PROP_DIMENSION_REDUCTION: + self->priv->dimension_reduction = g_value_get_uint (value); + break; + + case PROP_GRAPH_DISTANCE_THRESHOLD: + self->priv->distance_threshold = g_value_get_uint (value); + break; + + case PROP_GRAPH_MIN_NR_NODES: + self->priv->min_nr_nodes = g_value_get_uint (value); + break; + + case PROP_HANDS_MINIMUM_DISTANCE: + self->priv->hands_minimum_distance = g_value_get_uint (value); + break; + + case PROP_SHOULDERS_CIRCUMFERENCE_RADIUS: + self->priv->shoulders_circumference_radius = g_value_get_uint (value); + break; + + case PROP_SHOULDERS_ARC_START_POINT: + self->priv->shoulders_arc_start_point = g_value_get_uint (value); + break; + + case PROP_SHOULDERS_ARC_LENGTH: + self->priv->shoulders_arc_length = g_value_get_uint (value); + break; + + case PROP_SHOULDERS_SEARCH_STEP: + self->priv->shoulders_circumference_radius = g_value_get_float (value); + break; + + case PROP_EXTREMA_SPHERE_RADIUS: + self->priv->extrema_sphere_radius = g_value_get_uint (value); + break; + + case PROP_SMOOTHING_FACTOR: + self->priv->smooth_data.smoothing_factor = g_value_get_float (value); + break; + + case PROP_JOINTS_PERSISTENCY: + self->priv->smooth_data.joints_persistency = g_value_get_uint (value); + reset_joints_persistency_counter (&self->priv->smooth_data); + break; + + case PROP_ENABLE_SMOOTHING: + self->priv->enable_smoothing = g_value_get_boolean (value); + break; + + case PROP_TORSO_MINIMUM_NUMBER_NODES: + self->priv->torso_minimum_number_nodes = g_value_get_float (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +skeltrack_skeleton_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SkeltrackSkeleton *self; + + self = SKELTRACK_SKELETON (obj); + + switch (prop_id) + { + case PROP_DIMENSION_REDUCTION: + g_value_set_uint (value, self->priv->dimension_reduction); + break; + + case PROP_GRAPH_DISTANCE_THRESHOLD: + g_value_set_uint (value, self->priv->distance_threshold); + break; + + case PROP_GRAPH_MIN_NR_NODES: + g_value_set_uint (value, self->priv->min_nr_nodes); + break; + + case PROP_HANDS_MINIMUM_DISTANCE: + g_value_set_uint (value, self->priv->hands_minimum_distance); + break; + + case PROP_SHOULDERS_CIRCUMFERENCE_RADIUS: + g_value_set_uint (value, self->priv->shoulders_circumference_radius); + break; + + case PROP_SHOULDERS_ARC_START_POINT: + g_value_set_uint (value, self->priv->shoulders_arc_start_point); + break; + + case PROP_SHOULDERS_ARC_LENGTH: + g_value_set_uint (value, self->priv->shoulders_arc_length); + break; + + case PROP_SHOULDERS_SEARCH_STEP: + g_value_set_float (value, self->priv->shoulders_search_step); + break; + + case PROP_EXTREMA_SPHERE_RADIUS: + g_value_set_uint (value, self->priv->extrema_sphere_radius); + break; + + case PROP_SMOOTHING_FACTOR: + g_value_set_float (value, self->priv->smooth_data.smoothing_factor); + break; + + case PROP_JOINTS_PERSISTENCY: + g_value_set_uint (value, self->priv->smooth_data.joints_persistency); + break; + + case PROP_ENABLE_SMOOTHING: + g_value_set_boolean (value, self->priv->enable_smoothing); + break; + + case PROP_TORSO_MINIMUM_NUMBER_NODES: + g_value_set_float (value, self->priv->torso_minimum_number_nodes); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static gint +join_neighbor (SkeltrackSkeleton *self, + Node *node, + Label **neighbor_labels, + gint index, + gint i, + gint j) +{ + Node *neighbor; + if (i < 0 || i >= self->priv->buffer_width || + j < 0 || j >= self->priv->buffer_height) + { + return index; + } + + neighbor = self->priv->node_matrix[self->priv->buffer_width * j + i]; + if (neighbor != NULL) + { + gint distance; + distance = get_distance (neighbor, node); + if (distance < self->priv->distance_threshold) + { + neighbor->neighbors = g_list_append (neighbor->neighbors, + node); + node->neighbors = g_list_append (node->neighbors, + neighbor); + neighbor_labels[index] = neighbor->label; + index++; + } + } + return index; +} + +GList * +make_graph (SkeltrackSkeleton *self, GList **label_list) +{ + SkeltrackSkeletonPrivate *priv; + gint i, j, n; + Node *node; + GList *nodes = NULL; + GList *labels = NULL; + GList *current_label; + Label *main_component_label = NULL; + gint index = 0; + gint next_label = -1; + guint16 value; + guint16 *buffer; + gint width, height; + + buffer = self->priv->buffer; + width = self->priv->buffer_width; + height = self->priv->buffer_height; + + priv = self->priv; + + + if (priv->node_matrix == NULL) + { + priv->node_matrix = g_slice_alloc0 (width * height * sizeof (Node *)); + } + else + { + memset (self->priv->node_matrix, + 0, + width * height * sizeof (Node *)); + } + + for (i = 0; i < width; i++) + { + for (j = 0; j < height; j++) + { + gint south, north, west; + Label *lowest_index_label = NULL; + Label *neighbor_labels[4] = {NULL, NULL, NULL, NULL}; + + value = buffer[j * width + i]; + if (value == 0) + continue; + + node = g_slice_new0 (Node); + node->i = i; + node->j = j; + node->z = value; + convert_screen_coords_to_mm (self->priv->buffer_width, + self->priv->buffer_height, + self->priv->dimension_reduction, + i, j, + node->z, + &(node->x), + &(node->y)); + node->neighbors = NULL; + node->linked_nodes = NULL; + + index = 0; + + south = j + 1; + north = j - 1; + west = i - 1; + + /* West */ + index = join_neighbor (self, + node, + neighbor_labels, + index, + west, j); + /* South West*/ + index = join_neighbor (self, + node, + neighbor_labels, + index, + west, south); + /* North */ + index = join_neighbor (self, + node, + neighbor_labels, + index, + i, north); + + /* North West */ + index = join_neighbor (self, + node, + neighbor_labels, + index, + west, north); + + lowest_index_label = get_lowest_index_label (neighbor_labels); + + /* No neighbors */ + if (lowest_index_label == NULL) + { + Label *label; + next_label++; + label = new_label (next_label); + labels = g_list_append (labels, label); + lowest_index_label = label; + } + else + { + for (index = 0; index < 4; index++) + { + if (neighbor_labels[index] != NULL) + { + label_union (neighbor_labels[index], lowest_index_label); + } + } + } + + node->label = lowest_index_label; + nodes = g_list_append(nodes, node); + priv->node_matrix[width * node->j + node->i] = node; + } + } + + for (n = 0; n < g_list_length (nodes); n++) + { + Node *node = (Node *) g_list_nth_data (nodes, n); + node->label = label_find (node->label); + node->label->nodes = g_list_append (node->label->nodes, + node); + + /* Assign lower node so we can extract the + lower graph's component */ + if (node->label->lower_screen_y == -1 || + node->j > node->label->lower_screen_y) + { + node->label->lower_screen_y = node->j; + } + + /* Assign farther to the camera node so we + can extract the main graph component */ + if (node->label->higher_z == -1 || + node->z > node->label->higher_z) + { + node->label->higher_z = node->z; + } + + /* Assign closer to the camera node so we + can extract the main graph component */ + if (node->label->lower_z == -1 || + node->z < node->label->lower_z) + { + node->label->lower_z = node->z; + } + } + + for (current_label = g_list_first (labels); + current_label != NULL; + current_label = g_list_next (current_label)) + { + Label *label; + GList *current_nodes; + + label = (Label *) current_label->data; + current_nodes = label->nodes; + + label->normalized_num_nodes = g_list_length (current_nodes) * + ((label->higher_z - label->lower_z)/2 + + label->lower_z) * + (pow (DIMENSION_REDUCTION, 2)/2) / + 1000000; + } + + main_component_label = get_main_component (nodes, + priv->focus_node, + priv->torso_minimum_number_nodes); + + current_label = g_list_first (labels); + while (current_label != NULL) + { + Label *label; + label = (Label *) current_label->data; + + /* Remove label if number of nodes is less than + the minimum required */ + if (g_list_length (label->nodes) < priv->min_nr_nodes) + { + nodes = remove_nodes_with_label (nodes, + priv->node_matrix, + priv->buffer_width, + label); + + GList *link = current_label; + current_label = g_list_next (current_label); + labels = g_list_delete_link (labels, link); + free_label (label); + continue; + } + + current_label = g_list_next (current_label); + } + + if (main_component_label) + { + join_components_to_main (labels, + main_component_label, + priv->distance_threshold, + priv->hands_minimum_distance, + priv->distance_threshold); + + current_label = g_list_first (labels); + while (current_label != NULL) + { + Label *label; + label = (Label *) current_label->data; + if (label == main_component_label) + { + current_label = g_list_next (current_label); + continue; + } + + if (label->bridge_node == NULL) + { + nodes = remove_nodes_with_label (nodes, + priv->node_matrix, + priv->buffer_width, + label); + + GList *link = current_label; + current_label = g_list_next (current_label); + labels = g_list_delete_link (labels, link); + free_label (label); + continue; + } + + label->bridge_node->neighbors = + g_list_append (label->bridge_node->neighbors, label->to_node); + label->to_node->neighbors = g_list_append (label->to_node->neighbors, + label->bridge_node); + + current_label = g_list_next (current_label); + } + + priv->main_component = main_component_label->nodes; + } + + *label_list = labels; + + return nodes; +} + +Node * +get_centroid (SkeltrackSkeleton *self) +{ + gint avg_x = 0; + gint avg_y = 0; + gint avg_z = 0; + gint length; + GList *node_list; + Node *cent = NULL; + Node *centroid = NULL; + + if (self->priv->main_component == NULL) + return NULL; + + for (node_list = g_list_first (self->priv->main_component); + node_list != NULL; + node_list = g_list_next (node_list)) + { + Node *node; + node = (Node *) node_list->data; + avg_x += node->x; + avg_y += node->y; + avg_z += node->z; + } + + length = g_list_length (self->priv->main_component); + cent = g_slice_new0 (Node); + cent->x = avg_x / length; + cent->y = avg_y / length; + cent->z = avg_z / length; + cent->linked_nodes = NULL; + + centroid = get_closest_node (self->priv->graph, cent); + + g_slice_free (Node, cent); + + return centroid; +} + +static Node * +get_lowest (SkeltrackSkeleton *self, Node *centroid) +{ + Node *lowest = NULL; + /* @TODO: Use the node_matrix instead of the lowest + component to look for the lowest node as it's faster. */ + if (self->priv->main_component != NULL) + { + GList *node_list; + for (node_list = g_list_first (self->priv->main_component); + node_list != NULL; + node_list = g_list_next (node_list)) + { + Node *node; + node = (Node *) node_list->data; + if (node->i != centroid->i) + continue; + if (lowest == NULL || + lowest->j < node->j) + { + lowest = node; + } + } + } + return lowest; +} + +static Node * +get_longer_distance (SkeltrackSkeleton *self, gint *distances) +{ + GList *current; + Node *farthest_node; + + current = g_list_first (self->priv->graph); + farthest_node = (Node *) current->data; + current = g_list_next (current); + + while (current != NULL) + { + Node *node; + node = (Node *) current->data; + if (node != NULL && + (distances[farthest_node->j * + self->priv->buffer_width + + farthest_node->i] != -1 && + distances[farthest_node->j * + self->priv->buffer_width + + farthest_node->i] < distances[node->j * + self->priv->buffer_width + + node->i])) + { + farthest_node = node; + } + current = g_list_next (current); + } + return farthest_node; +} + +static void +set_average_extremas (SkeltrackSkeletonPrivate *priv, GList *extremas) +{ + GList *current_extrema, *averaged_extremas = NULL; + + for (current_extrema = g_list_first (extremas); + current_extrema != NULL; + current_extrema = g_list_next (current_extrema)) + { + GList *current_node; + Node *extrema, *node = NULL, *cent = NULL, *node_centroid = NULL; + gint avg_x = 0, avg_y = 0, avg_z = 0, length = 0; + + extrema = (Node *) current_extrema->data; + + for (current_node = g_list_first (priv->graph); + current_node != NULL; + current_node = g_list_next (current_node)) + { + node = (Node *) current_node->data; + + if ((get_distance (extrema, node) < + priv->extrema_sphere_radius)) + { + avg_x += node->x; + avg_y += node->y; + avg_z += node->z; + + length++; + } + } + + /* if the length is 1 then it is because no other + nodes were considered for the average */ + if (length > 1) + { + cent = g_slice_new0 (Node); + cent->x = avg_x / length; + cent->y = avg_y / length; + cent->z = avg_z / length; + cent->linked_nodes = NULL; + + node_centroid = get_closest_node (priv->graph, cent); + + /* If the new averaged extrema is not already an extrema + set it for addition */ + if (g_list_find (averaged_extremas, node_centroid) == NULL && + g_list_find (extremas, node_centroid) == NULL) + { + current_extrema->data = node_centroid; + } + + g_slice_free (Node, cent); + } + } +} + +static GList * +get_extremas (SkeltrackSkeleton *self, Node *centroid) +{ + SkeltrackSkeletonPrivate *priv; + gint i, nr_nodes, matrix_size; + Node *lowest, *source, *node; + GList *extremas = NULL; + + priv = self->priv; + lowest = get_lowest (self, centroid); + source = lowest; + + matrix_size = priv->buffer_width * priv->buffer_height; + if (priv->distances_matrix == NULL) + { + priv->distances_matrix = g_slice_alloc0 (matrix_size * sizeof (gint)); + } + + for (i = 0; i < matrix_size; i++) + { + priv->distances_matrix[i] = -1; + } + + for (nr_nodes = NR_EXTREMAS_TO_SEARCH; + source != NULL && nr_nodes > 0; + nr_nodes--) + { + dijkstra_to (priv->graph, + source, + NULL, + priv->buffer_width, + priv->buffer_height, + priv->distances_matrix, + NULL); + + node = get_longer_distance (self, priv->distances_matrix); + + if (node == NULL) + continue; + + if (node != source) + { + priv->distances_matrix[node->j * priv->buffer_width + node->i] = 0; + source->linked_nodes = g_list_append (source->linked_nodes, node); + node->linked_nodes = g_list_append (node->linked_nodes, source); + source = node; + extremas = g_list_append (extremas, node); + } + } + + if (self->priv->extrema_sphere_radius != 0) + { + set_average_extremas (priv, extremas); + } + + return extremas; +} + +static Node * +get_shoulder_node (SkeltrackSkeletonPrivate *priv, + gfloat alpha, + gfloat step, + gint x_node, + gint y_node, + gint z_centroid) +{ + guint radius, arc_start_point, arc_length, current_i, current_j; + gfloat start_angle, last_node_arc, current_arc, angle, current_x, current_y; + Node *current_node = NULL; + Node *last_node = NULL; + + radius = priv->shoulders_circumference_radius; + arc_start_point = priv->shoulders_arc_start_point; + arc_length = priv->shoulders_arc_length; + + start_angle = M_PI_2; + + angle = start_angle + alpha; + current_x = x_node + radius * cos (angle); + current_y = y_node + radius * sin (angle); + current_arc = 0; + last_node_arc = 0; + current_node = NULL; + last_node = NULL; + + while (current_arc <= (arc_start_point + arc_length)) + { + convert_mm_to_screen_coords (priv->buffer_width, + priv->buffer_height, + priv->dimension_reduction, + current_x, + current_y, + z_centroid, + ¤t_i, + ¤t_j); + + if (current_i >= priv->buffer_width || current_j >= priv->buffer_height) + break; + + current_node = priv->node_matrix[current_j * priv->buffer_width + + current_i]; + + if (current_node != NULL) + { + last_node = current_node; + last_node_arc = current_arc; + } + + angle += step; + current_x = x_node + radius * cos (angle); + current_y = y_node + radius * sin (angle); + current_arc = ABS (angle - start_angle) * radius; + } + + if (last_node_arc < arc_start_point) + return NULL; + + return last_node; +} + +static gboolean +check_if_node_can_be_head (SkeltrackSkeleton *self, + Node *node, + Node *centroid, + Node **left_shoulder, + Node **right_shoulder) +{ + gfloat alpha; + Node *found_right_shoulder = NULL, *found_left_shoulder = NULL; + + SkeltrackSkeletonPrivate *priv; + + *left_shoulder = NULL; + *right_shoulder = NULL; + + priv = self->priv; + + if (node->j > centroid->j) + return FALSE; + + if ((node->y - centroid->y) != 0) + alpha = atan( ABS (node->x - centroid->x) / ABS (node->y - centroid->y)); + else + return FALSE; + + /* too much tilt, cannot be the head */ + if (alpha >= M_PI_4) + return FALSE; + + if (node->x < centroid->x) + alpha = -alpha; + + found_right_shoulder = get_shoulder_node (priv, + alpha, + priv->shoulders_search_step, + node->x, + node->y, + centroid->z); + if (found_right_shoulder == NULL) + return FALSE; + + found_left_shoulder = get_shoulder_node (priv, + alpha, + -priv->shoulders_search_step, + node->x, + node->y, + centroid->z); + + if (found_left_shoulder == NULL) + return FALSE; + + *right_shoulder = found_right_shoulder; + *left_shoulder = found_left_shoulder; + + return TRUE; +} + +static gboolean +get_head_and_shoulders (SkeltrackSkeleton *self, + GList *extremas, + Node *centroid, + Node **head, + Node **left_shoulder, + Node **right_shoulder) +{ + Node *node; + GList *current_extrema; + + for (current_extrema = g_list_first (extremas); + current_extrema != NULL; + current_extrema = g_list_next (current_extrema)) + { + node = (Node *) current_extrema->data; + + if (check_if_node_can_be_head (self, + node, + centroid, + left_shoulder, + right_shoulder)) + { + *head = node; + return TRUE; + } + } + return FALSE; +} + +static void +identify_arm_extrema (gint *distances, + Node **previous_nodes, + gint width, + gint hand_distance, + Node *extrema, + Node **elbow_extrema, + Node **hand_extrema) +{ + gint total_dist; + + if (extrema == NULL) + return; + + total_dist = distances[width * extrema->j + extrema->i]; + if (total_dist < hand_distance) + { + *elbow_extrema = extrema; + *hand_extrema = NULL; + } + else + { + Node *previous; + gint elbow_dist; + + previous = previous_nodes[extrema->j * width + extrema->i]; + elbow_dist = total_dist / 2; + while (previous && + distances[previous->j * width + previous->i] > elbow_dist) + { + previous = previous_nodes[previous->j * width + previous->i]; + } + *elbow_extrema = previous; + *hand_extrema = extrema; + } +} + +static void +set_left_and_right_from_extremas (SkeltrackSkeleton *self, + GList *extremas, + Node *head, + Node *left_shoulder, + Node *right_shoulder, + SkeltrackJointList *joints) +{ + gint *dist_left_a = NULL; + gint *dist_left_b = NULL; + gint *dist_right_a = NULL; + gint *dist_right_b = NULL; + gint total_dist_left_a = -1; + gint total_dist_right_a = -1; + gint total_dist_left_b = -1; + gint total_dist_right_b = -1; + gint *distances_left[2] = {NULL, NULL}; + gint *distances_right[2] = {NULL, NULL}; + gint index_left = -1; + gint index_right = -1; + Node *elbow_extrema, *hand_extrema; + Node **previous_left_a = NULL; + Node **previous_left_b = NULL; + Node **previous_right_a = NULL; + Node **previous_right_b = NULL; + Node **previous_left[2] = {NULL, NULL}; + Node **previous_right[2] = {NULL, NULL}; + Node *ext_a = NULL; + Node *ext_b = NULL; + Node *left_extrema[2] = {NULL, NULL}; + Node *right_extrema[2] = {NULL, NULL}; + GList *current_extrema; + gint width, height, matrix_size; + + for (current_extrema = g_list_first (extremas); + current_extrema != NULL; + current_extrema = g_list_next (current_extrema)) + { + Node *node; + node = (Node *) current_extrema->data; + if (node != head) + { + if (ext_a == NULL) + ext_a = node; + else + ext_b = node; + } + } + + if (head == NULL) + return; + + width = self->priv->buffer_width; + height = self->priv->buffer_height; + matrix_size = width * height; + + previous_left_a = g_slice_alloc0 (matrix_size * sizeof (Node *)); + previous_left_b = g_slice_alloc0 (matrix_size * sizeof (Node *)); + previous_right_a = g_slice_alloc0 (matrix_size * sizeof (Node *)); + previous_right_b = g_slice_alloc0 (matrix_size * sizeof (Node *)); + + dist_left_a = create_new_dist_matrix(matrix_size); + dijkstra_to (self->priv->graph, + left_shoulder, + ext_a, + width, + height, + dist_left_a, + previous_left_a); + + dist_left_b = create_new_dist_matrix(matrix_size); + dijkstra_to (self->priv->graph, + left_shoulder, + ext_b, + width, + height, + dist_left_b, + previous_left_b); + + dist_right_a = create_new_dist_matrix(matrix_size); + dijkstra_to (self->priv->graph, + right_shoulder, + ext_a, + width, + height, + dist_right_a, previous_right_a); + + dist_right_b = create_new_dist_matrix(matrix_size); + dijkstra_to (self->priv->graph, + right_shoulder, + ext_b, + width, + height, + dist_right_b, + previous_right_b); + + total_dist_left_a = dist_left_a[ext_a->j * width + ext_a->i]; + total_dist_right_a = dist_right_a[ext_a->j * width + ext_a->i]; + total_dist_left_b = dist_left_b[ext_b->j * width + ext_b->i]; + total_dist_right_b = dist_right_b[ext_b->j * width + ext_b->i]; + + if (total_dist_left_a < total_dist_right_a) + { + index_left++; + left_extrema[index_left] = ext_a; + distances_left[index_left] = dist_left_a; + previous_left[index_left] = previous_left_a; + } + else + { + index_right++; + right_extrema[index_right] = ext_a; + distances_right[index_right] = dist_right_a; + previous_right[index_right] = previous_right_a; + } + + if (total_dist_left_b < total_dist_right_b) + { + index_left++; + left_extrema[index_left] = ext_b; + distances_left[index_left] = dist_left_b; + previous_left[index_left] = previous_left_b; + } + else + { + index_right++; + right_extrema[index_right] = ext_b; + distances_right[index_right] = dist_right_b; + previous_right[index_right] = previous_right_b; + } + + elbow_extrema = NULL; + hand_extrema = NULL; + identify_arm_extrema (distances_left[0], + previous_left[0], + width, + self->priv->hands_minimum_distance, + left_extrema[0], + &elbow_extrema, + &hand_extrema); + + /* Two left extremas */ + if (index_left == 1) + { + if (hand_extrema == NULL) + { + hand_extrema = left_extrema[1]; + elbow_extrema = left_extrema[0]; + } + else + { + hand_extrema = left_extrema[0]; + elbow_extrema = left_extrema[1]; + } + } + + set_joint_from_node (joints, + elbow_extrema, + SKELTRACK_JOINT_ID_LEFT_ELBOW, + self->priv->dimension_reduction); + set_joint_from_node (joints, + hand_extrema, + SKELTRACK_JOINT_ID_LEFT_HAND, + self->priv->dimension_reduction); + + + elbow_extrema = NULL; + hand_extrema = NULL; + identify_arm_extrema (distances_right[0], + previous_right[0], + width, + self->priv->hands_minimum_distance, + right_extrema[0], + &elbow_extrema, + &hand_extrema); + + /* Two right extremas */ + if (index_right == 1) + { + if (hand_extrema == NULL) + { + hand_extrema = right_extrema[1]; + elbow_extrema = right_extrema[0]; + } + else + { + hand_extrema = right_extrema[0]; + elbow_extrema = right_extrema[1]; + } + } + + set_joint_from_node (joints, + elbow_extrema, + SKELTRACK_JOINT_ID_RIGHT_ELBOW, + self->priv->dimension_reduction); + set_joint_from_node (joints, + hand_extrema, + SKELTRACK_JOINT_ID_RIGHT_HAND, + self->priv->dimension_reduction); + + g_slice_free1 (matrix_size * sizeof (Node *), previous_left_a); + g_slice_free1 (matrix_size * sizeof (Node *), previous_left_b); + g_slice_free1 (matrix_size * sizeof (Node *), previous_right_a); + g_slice_free1 (matrix_size * sizeof (Node *), previous_right_b); + + g_slice_free1 (matrix_size * sizeof (gint), dist_left_a); + g_slice_free1 (matrix_size * sizeof (gint), dist_left_b); + g_slice_free1 (matrix_size * sizeof (gint), dist_right_a); + g_slice_free1 (matrix_size * sizeof (gint), dist_right_b); +} + +static Node * +get_adjusted_shoulder (guint buffer_width, + guint buffer_height, + guint dimension_reduction, + GList *graph, + Node *centroid, + Node *head, + Node *shoulder) +{ + Node *virtual_shoulder, *adjusted_shoulder = NULL; + virtual_shoulder = g_slice_new (Node); + virtual_shoulder->x = shoulder->x; + virtual_shoulder->y = shoulder->y; + virtual_shoulder->z = centroid->z; + + convert_mm_to_screen_coords (buffer_width, + buffer_height, + dimension_reduction, + virtual_shoulder->x, + virtual_shoulder->y, + virtual_shoulder->z, + (guint *) &virtual_shoulder->i, + (guint *) &virtual_shoulder->j); + + adjusted_shoulder = get_closest_torso_node (graph, + virtual_shoulder, + head); + g_slice_free (Node, virtual_shoulder); + + return adjusted_shoulder; +} + +static SkeltrackJoint ** +track_joints (SkeltrackSkeleton *self) +{ + Node * centroid; + Node *head = NULL; + Node *right_shoulder = NULL; + Node *left_shoulder = NULL; + GList *extremas; + SkeltrackJointList joints = NULL; + SkeltrackJointList smoothed = NULL; + + self->priv->graph = make_graph (self, &self->priv->labels); + centroid = get_centroid (self); + extremas = get_extremas (self, centroid); + + if (g_list_length (extremas) > 2) + { + if (self->priv->previous_head) + { + gint distance; + gboolean can_be_head = FALSE; + head = get_closest_node_to_joint (extremas, + self->priv->previous_head, + &distance); + if (head != NULL && + distance < GRAPH_DISTANCE_THRESHOLD) + { + can_be_head = check_if_node_can_be_head (self, + head, + centroid, + &left_shoulder, + &right_shoulder); + } + + if (!can_be_head) + head = NULL; + } + + if (head == NULL) + { + get_head_and_shoulders (self, + extremas, + centroid, + &head, + &left_shoulder, + &right_shoulder); + } + + if (joints == NULL) + joints = skeltrack_joint_list_new (); + + set_joint_from_node (&joints, + head, + SKELTRACK_JOINT_ID_HEAD, + self->priv->dimension_reduction); + + if (left_shoulder && head && head->z > left_shoulder->z) + { + Node *adjusted_shoulder; + adjusted_shoulder = get_adjusted_shoulder (self->priv->buffer_width, + self->priv->buffer_height, + self->priv->dimension_reduction, + self->priv->graph, + centroid, + head, + left_shoulder); + + if (adjusted_shoulder) + left_shoulder = adjusted_shoulder; + } + + set_joint_from_node (&joints, + left_shoulder, + SKELTRACK_JOINT_ID_LEFT_SHOULDER, + self->priv->dimension_reduction); + + if (right_shoulder && head && head->z > right_shoulder->z) + { + Node *adjusted_shoulder; + adjusted_shoulder = get_adjusted_shoulder (self->priv->buffer_width, + self->priv->buffer_height, + self->priv->dimension_reduction, + self->priv->graph, + centroid, + head, + right_shoulder); + + if (adjusted_shoulder) + right_shoulder = adjusted_shoulder; + } + set_joint_from_node (&joints, + right_shoulder, + SKELTRACK_JOINT_ID_RIGHT_SHOULDER, + self->priv->dimension_reduction); + + set_left_and_right_from_extremas (self, + extremas, + head, + left_shoulder, + right_shoulder, + &joints); + } + + self->priv->buffer = NULL; + + self->priv->main_component = NULL; + + clean_nodes (self->priv->graph); + g_list_free (self->priv->graph); + self->priv->graph = NULL; + + clean_labels (self->priv->labels); + g_list_free (self->priv->labels); + self->priv->labels = NULL; + + if (self->priv->enable_smoothing) + { + smooth_joints (&self->priv->smooth_data, joints); + + if (self->priv->smooth_data.smoothed_joints != NULL) + { + guint i; + smoothed = skeltrack_joint_list_new (); + for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) + { + SkeltrackJoint *smoothed_joint, *smooth, *trend; + smoothed_joint = NULL; + smooth = self->priv->smooth_data.smoothed_joints[i]; + if (smooth != NULL) + { + if (self->priv->smooth_data.trend_joints != NULL) + { + trend = self->priv->smooth_data.trend_joints[i]; + if (trend != NULL) + { + smoothed_joint = g_slice_new0 (SkeltrackJoint); + smoothed_joint->x = smooth->x + trend->x; + smoothed_joint->y = smooth->y + trend->y; + smoothed_joint->z = smooth->z + trend->z; + smoothed_joint->screen_x = smooth->screen_x + trend->screen_x; + smoothed_joint->screen_y = smooth->screen_y + trend->screen_y; + } + else + smoothed_joint = skeltrack_joint_copy (smooth); + } + else + smoothed_joint = skeltrack_joint_copy (smooth); + } + smoothed[i] = smoothed_joint; + } + } + skeltrack_joint_list_free (joints); + + joints = smoothed; + } + + if (joints) + { + SkeltrackJoint *joint = skeltrack_joint_list_get_joint (joints, + SKELTRACK_JOINT_ID_HEAD); + if (joint != NULL) + { + skeltrack_joint_free (self->priv->previous_head); + self->priv->previous_head = skeltrack_joint_copy (joint); + } + } + + g_list_free (extremas); + + return joints; +} + +static void +clean_tracking_resources (SkeltrackSkeleton *self) +{ + g_slice_free1 (self->priv->buffer_width * + self->priv->buffer_height * sizeof (gint), + self->priv->distances_matrix); + self->priv->distances_matrix = NULL; + + g_slice_free1 (self->priv->buffer_width * + self->priv->buffer_height * sizeof (Node *), + self->priv->node_matrix); + self->priv->node_matrix = NULL; +} + +static void +track_joints_in_thread (GSimpleAsyncResult *res, + GObject *object, + GCancellable *cancellable) +{ + SkeltrackSkeleton *self = SKELTRACK_SKELETON (object); + SkeltrackJointList joints; + + joints = track_joints (self); + + g_mutex_lock (&self->priv->track_joints_mutex); + self->priv->track_joints_result = NULL; + g_mutex_unlock (&self->priv->track_joints_mutex); + + g_simple_async_result_set_op_res_gpointer (res, + joints, + NULL); + + g_object_unref (res); +} + +/* public methods */ + +/** + * skeltrack_skeleton_new: + * + * Constructs and returns a new #SkeltrackSkeleton instance. + * + * Returns: (transfer full): The newly created #SkeltrackSkeleton. + */ +SkeltrackSkeleton * +skeltrack_skeleton_new (void) +{ + return g_object_new (SKELTRACK_TYPE_SKELETON, NULL); +} + +/** + * skeltrack_skeleton_set_focus_point: + * @self: The #SkeltrackSkeleton + * @x: The x coordinate of the focus point. + * @y: The y coordinate of the focus point. + * @z: The z coordinate of the focus point. + * + * Gets the focus point which is the origin from where the tracking will + * start. The coordinates will be in mm. + * + **/ +void +skeltrack_skeleton_get_focus_point (SkeltrackSkeleton *self, + gint *x, + gint *y, + gint *z) +{ + *x = self->priv->focus_node->x; + *y = self->priv->focus_node->y; + *z = self->priv->focus_node->z; +} + +/** + * skeltrack_skeleton_set_focus_point: + * @self: The #SkeltrackSkeleton + * @x: The x coordinate of the focus point. + * @y: The y coordinate of the focus point. + * @z: The z coordinate of the focus point. + * + * Sets the focus point which is the origin from where the tracking will + * start. The coordinates should be in mm. + * + * If this method is not called the default values are @x = 0, @y = 0, + * @z = 1000, that is, in the center of the screen and at 1m from the + * camera. + * + **/ +void +skeltrack_skeleton_set_focus_point (SkeltrackSkeleton *self, + gint x, + gint y, + gint z) +{ + self->priv->focus_node->x = x; + self->priv->focus_node->y = y; + self->priv->focus_node->z = z; +} + +/** + * skeltrack_skeleton_track_joints: + * @self: The #SkeltrackSkeleton + * @buffer: The buffer containing the depth information, from which + * all the information will be retrieved. + * @width: The width of the @buffer + * @height: The height of the @buffer + * @cancellable: (allow-none): A cancellable object, or %NULL (currently + * unused) + * @callback: (scope async): The function to call when the it is finished + * tracking the joints + * @user_data: (allow-none): An arbitrary user data to pass in @callback, + * or %NULL + * + * Tracks the skeleton's joints. + * + * It uses the depth information contained in the given @buffer and tries to + * track the skeleton joints. The @buffer's depth values should be in mm. + * Use skeltrack_skeleton_track_joints_finish() to get a list of the joints + * found. + * + * If this method is called while a previous attempt of tracking the joints + * is still running, a %G_IO_ERROR_PENDING error occurs. + * + **/ +void +skeltrack_skeleton_track_joints (SkeltrackSkeleton *self, + guint16 *buffer, + guint width, + guint height, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result = NULL; + + g_return_if_fail (SKELTRACK_IS_SKELETON (self) && + callback != NULL && + buffer != NULL); + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + skeltrack_skeleton_track_joints); + + if (self->priv->track_joints_result != NULL) + { + g_simple_async_result_set_error (result, + G_IO_ERROR, + G_IO_ERROR_PENDING, + "Currently tracking joints"); + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); + return; + } + + g_mutex_lock (&self->priv->track_joints_mutex); + + self->priv->track_joints_result = G_ASYNC_RESULT (result); + + /* @TODO: Set the cancellable */ + + self->priv->buffer = buffer; + + if (self->priv->buffer_width != width || + self->priv->buffer_height != height) + { + clean_tracking_resources (self); + + self->priv->buffer_width = width; + self->priv->buffer_height = height; + } + + g_simple_async_result_run_in_thread (result, + track_joints_in_thread, + G_PRIORITY_DEFAULT, + cancellable); + + g_mutex_unlock (&self->priv->track_joints_mutex); +} + +/** + * skeltrack_skeleton_track_joints_finish: + * @self: The #SkeltrackSkeleton + * @result: The #GAsyncResult provided in the callback + * @error: (allow-none): A pointer to a #GError, or %NULL + * + * Gets the list of joints that were retrieved by a + * skeltrack_skeleton_track_joints() operation. + * + * Use skeltrack_joint_list_get_joint() with #SkeltrackJointId + * to get the respective joints from the list. + * Joints that could not be found will appear as %NULL in the list. + * + * The list should be freed using skeltrack_joint_list_free(). + * + * Returns: (transfer full): The #SkeltrackJointList with the joints found. + */ +SkeltrackJointList +skeltrack_skeleton_track_joints_finish (SkeltrackSkeleton *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *res; + + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); + + res = G_SIMPLE_ASYNC_RESULT (result); + + if (! g_simple_async_result_propagate_error (res, error)) + { + SkeltrackJointList joints = NULL; + joints = g_simple_async_result_get_op_res_gpointer (res); + return joints; + } + else + return NULL; +} + +/** + * skeltrack_skeleton_track_joints_sync: + * @self: The #SkeltrackSkeleton + * @buffer: The buffer containing the depth information, from which + * all the information will be retrieved. + * @width: The width of the @buffer + * @height: The height of the @buffer + * @cancellable: (allow-none): A cancellable object, or %NULL (currently + * unused) + * @error: (allow-none): A pointer to a #GError, or %NULL + * + * Tracks the skeleton's joints synchronously. + * + * Does the same as skeltrack_skeleton_track_joints() but synchronously + * and returns the list of joints found. + * Ideal for off-line skeleton tracking. + * + * If this method is called while a previous attempt of asynchronously + * tracking the joints is still running, a %G_IO_ERROR_PENDING error occurs. + * + * The joints list should be freed using skeltrack_joint_list_free(). + * + * Returns: (transfer full): The #SkeltrackJointList with the joints found. + **/ +SkeltrackJointList +skeltrack_skeleton_track_joints_sync (SkeltrackSkeleton *self, + guint16 *buffer, + guint width, + guint height, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (SKELTRACK_IS_SKELETON (self), NULL); + + if (self->priv->track_joints_result != NULL && error != NULL) + { + *error = g_error_new (G_IO_ERROR, + G_IO_ERROR_PENDING, + "Currently tracking joints"); + return NULL; + } + + self->priv->buffer = buffer; + + if (self->priv->buffer_width != width || + self->priv->buffer_height != height) + { + clean_tracking_resources (self); + + self->priv->buffer_width = width; + self->priv->buffer_height = height; + } + + return track_joints (self); +} diff --git a/interface/external/skeltrack/src/skeltrack-smooth.c b/interface/external/skeltrack/src/skeltrack-smooth.c new file mode 100644 index 0000000000..1e6658f578 --- /dev/null +++ b/interface/external/skeltrack/src/skeltrack-smooth.c @@ -0,0 +1,226 @@ +/* + * skeltrack-smooth.c + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * 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 3 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 at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +#include + +#include "skeltrack-smooth.h" + +static gfloat +holt_double_exp_formula_st (gfloat alpha, + gfloat previous_trend, + gfloat current_value, + gfloat previous_smoothed_value) +{ + return alpha * current_value + + (1.0 - alpha) * (previous_smoothed_value + previous_trend); +} + +static gfloat +holt_double_exp_formula_bt (gfloat beta, + gfloat previous_trend, + gfloat current_smoothed_value, + gfloat previous_smoothed_value) +{ + return beta * (current_smoothed_value - previous_smoothed_value) + + (1.0 - beta) * previous_trend; +} + +static void +holt_double_exp_joint (gfloat alpha, + gfloat beta, + SkeltrackJoint *smoothed_joint, + SkeltrackJoint *current_joint, + SkeltrackJoint *trend_joint) +{ + gfloat new_x, new_y, new_z, new_screen_x, new_screen_y; + new_x = holt_double_exp_formula_st (alpha, + trend_joint->x, + current_joint->x, + smoothed_joint->x); + new_y = holt_double_exp_formula_st (alpha, + trend_joint->y, + current_joint->y, + smoothed_joint->y); + new_z = holt_double_exp_formula_st (alpha, + trend_joint->z, + current_joint->z, + smoothed_joint->z); + new_screen_x = holt_double_exp_formula_st (alpha, + trend_joint->screen_x, + current_joint->screen_x, + smoothed_joint->screen_x); + new_screen_y = holt_double_exp_formula_st (alpha, + trend_joint->screen_y, + current_joint->screen_y, + smoothed_joint->screen_y); + trend_joint->x = holt_double_exp_formula_bt (beta, + trend_joint->x, + new_x, + smoothed_joint->x); + trend_joint->y = holt_double_exp_formula_bt (beta, + trend_joint->y, + new_y, + smoothed_joint->y); + trend_joint->z = holt_double_exp_formula_bt (beta, + trend_joint->z, + new_z, + smoothed_joint->z); + trend_joint->screen_x = holt_double_exp_formula_bt (beta, + trend_joint->screen_x, + new_screen_x, + smoothed_joint->screen_x); + trend_joint->screen_y = holt_double_exp_formula_bt (beta, + trend_joint->screen_y, + new_screen_y, + smoothed_joint->screen_y); + smoothed_joint->x = new_x; + smoothed_joint->y = new_y; + smoothed_joint->z = new_z; + smoothed_joint->screen_x = new_screen_x; + smoothed_joint->screen_y = new_screen_y; +} + +void +reset_joints_persistency_counter (SmoothData *smooth_data) +{ + guint i; + for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) + { + smooth_data->joints_persistency_counter[i] = + smooth_data->joints_persistency; + } +} + +static void +decrease_joints_persistency (SmoothData *smooth_data) +{ + guint i; + for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) + { + SkeltrackJoint *smoothed_joint = NULL; + SkeltrackJoint *trend_joint = NULL; + + if (smooth_data->smoothed_joints) + smoothed_joint = smooth_data->smoothed_joints[i]; + if (smooth_data->trend_joints) + trend_joint = smooth_data->trend_joints[i]; + + if (smoothed_joint != NULL || trend_joint != NULL) + { + if (smooth_data->joints_persistency_counter[i] > 0) + smooth_data->joints_persistency_counter[i]--; + else + { + skeltrack_joint_free (smoothed_joint); + skeltrack_joint_free (trend_joint); + if (smoothed_joint) + smooth_data->smoothed_joints[i] = NULL; + if (trend_joint) + smooth_data->trend_joints[i] = NULL; + smooth_data->joints_persistency_counter[i] = smooth_data->joints_persistency; + } + } + } +} + +void +smooth_joints (SmoothData *data, + SkeltrackJointList new_joints) +{ + guint i; + + if (new_joints == NULL) + { + decrease_joints_persistency (data); + return; + } + + if (data->smoothed_joints == NULL) + { + data->smoothed_joints = skeltrack_joint_list_new (); + for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) + { + data->smoothed_joints[i] = skeltrack_joint_copy (new_joints[i]); + } + return; + } + if (data->trend_joints == NULL) + { + data->trend_joints = skeltrack_joint_list_new (); + } + + for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) + { + SkeltrackJoint *joint, *smoothed_joint, *trend_joint; + + smoothed_joint = data->smoothed_joints[i]; + trend_joint = data->trend_joints[i]; + joint = new_joints[i]; + if (joint == NULL) + { + if (smoothed_joint != NULL) + { + if (data->joints_persistency_counter[i] > 0) + data->joints_persistency_counter[i]--; + else + { + skeltrack_joint_free (smoothed_joint); + skeltrack_joint_free (trend_joint); + data->smoothed_joints[i] = NULL; + data->trend_joints[i] = NULL; + data->joints_persistency_counter[i] = data->joints_persistency; + } + } + continue; + } + data->joints_persistency_counter[i] = data->joints_persistency; + + if (smoothed_joint == NULL) + { + data->smoothed_joints[i] = skeltrack_joint_copy (joint); + continue; + } + + if (trend_joint == NULL) + { + /* First case (when there are only initial values) */ + trend_joint = g_slice_new0 (SkeltrackJoint); + trend_joint->x = joint->x - smoothed_joint->x; + trend_joint->y = joint->y - smoothed_joint->y; + trend_joint->z = joint->z - smoothed_joint->z; + trend_joint->screen_x = joint->screen_x - smoothed_joint->screen_x; + trend_joint->screen_y = joint->screen_y - smoothed_joint->screen_y; + data->trend_joints[i] = trend_joint; + } + else + { + /* @TODO: Check if we should give the control of each factor + independently (data-smoothing-factor and trend-smoothing-factor). + */ + holt_double_exp_joint (data->smoothing_factor, + data->smoothing_factor, + smoothed_joint, + joint, + trend_joint); + } + } +} diff --git a/interface/external/skeltrack/src/skeltrack-util.c b/interface/external/skeltrack/src/skeltrack-util.c new file mode 100644 index 0000000000..482f26069a --- /dev/null +++ b/interface/external/skeltrack/src/skeltrack-util.c @@ -0,0 +1,626 @@ +/* + * skeltrack-util.c + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * 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 3 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 at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +#include + +#include "skeltrack-util.h" + +/* @TODO: Expose these to the user */ +static const gfloat SCALE_FACTOR = .0021; +static const gint MIN_DISTANCE = -10.0; + +static SkeltrackJoint * +node_to_joint (Node *node, SkeltrackJointId id, gint dimension_reduction) +{ + SkeltrackJoint *joint; + + if (node == NULL) + return NULL; + + joint = g_slice_new0 (SkeltrackJoint); + joint->id = id; + joint->x = node->x; + joint->y = node->y; + joint->z = node->z; + joint->screen_x = node->i * dimension_reduction; + joint->screen_y = node->j * dimension_reduction; + + return joint; +} + +static guint +get_distance_from_joint (Node *node, SkeltrackJoint *joint) +{ + guint dx, dy, dz; + dx = ABS (node->x - joint->x); + dy = ABS (node->y - joint->y); + dz = ABS (node->z - joint->z); + return sqrt (dx * dx + dy * dy + dz * dz); +} + +static void +unlink_node (Node *node) +{ + Node *neighbor; + GList *current_neighbor; + + for (current_neighbor = g_list_first (node->neighbors); + current_neighbor != NULL; + current_neighbor = g_list_next (current_neighbor)) + { + neighbor = (Node *) current_neighbor->data; + neighbor->neighbors = g_list_remove (neighbor->neighbors, node); + } + + for (current_neighbor = g_list_first (node->linked_nodes); + current_neighbor != NULL; + current_neighbor = g_list_next (current_neighbor)) + { + neighbor = (Node *) current_neighbor->data; + neighbor->linked_nodes = g_list_remove (neighbor->linked_nodes, node); + } + + g_list_free (node->neighbors); + g_list_free (node->linked_nodes); + node->neighbors = NULL; + node->linked_nodes = NULL; +} + +static Node * +get_closest_node_with_distances (GList *node_list, + Node *from, + guint x_dist, + guint y_dist, + guint z_dist, + gint *closest_node_dist) +{ + Node *closest = NULL; + gint distance = -1; + GList *current_node; + + /* @TODO: Replace this and use closest pair of points + algorithm and ensure O(n log n) instead of brute-force */ + + for (current_node = g_list_first (node_list); + current_node != NULL; + current_node = g_list_next (current_node)) + { + guint dx, dy, dz; + Node *node; + gint current_distance; + node = (Node *) current_node->data; + + dx = ABS (from->x - node->x); + dy = ABS (from->y - node->y); + dz = ABS (from->z - node->z); + + if (dx > x_dist || dy > y_dist || dz > z_dist) + continue; + + current_distance = sqrt (dx * dx + dy * dy + dz * dz); + if (closest == NULL || distance > current_distance) + { + closest = node; + distance = current_distance; + } + } + + *closest_node_dist = distance; + return closest; +} + +Node * +get_closest_node_to_joint (GList *extremas, + SkeltrackJoint *joint, + gint *distance) +{ + GList *current_node; + gint dist = -1; + Node *closest_node = NULL; + + for (current_node = g_list_first (extremas); + current_node != NULL; + current_node = g_list_next (current_node)) + { + guint current_dist; + Node *node = (Node *) current_node->data; + if (node == NULL) + continue; + + current_dist = get_distance_from_joint (node, joint); + if (dist == -1 || current_dist < dist) + { + closest_node = node; + dist = current_dist; + } + } + *distance = dist; + return closest_node; +} + +gint +get_distance (Node *a, Node *b) +{ + guint dx, dy, dz; + dx = ABS (a->x - b->x); + dy = ABS (a->y - b->y); + dz = ABS (a->z - b->z); + return sqrt (dx * dx + dy * dy + dz * dz); +} + +Node * +get_closest_torso_node (GList *node_list, Node *from, Node *head) +{ + Node *closest = NULL; + gint distance = -1; + GList *current_node; + + /* @TODO: Replace this and use closest pair of points + algorithm and ensure O(n log n) instead of brute-force */ + + for (current_node = g_list_first (node_list); + current_node != NULL; + current_node = g_list_next (current_node)) + { + Node *node; + gint current_distance; + node = (Node *) current_node->data; + if (node->z >= head->z && + node->y >= from->y) + { + current_distance = get_distance (node, from); + if (closest == NULL || current_distance < distance) + { + closest = node; + distance = current_distance; + } + } + } + return closest; +} + +Node * +get_closest_node (GList *node_list, Node *from) +{ + Node *closest = NULL; + gint distance = -1; + GList *current_node; + + /* @TODO: Replace this and use closest pair of points + algorithm and ensure O(n log n) instead of brute-force */ + + for (current_node = g_list_first (node_list); + current_node != NULL; + current_node = g_list_next (current_node)) + { + Node *node; + gint current_distance; + node = (Node *) current_node->data; + if (closest == NULL) + { + closest = node; + distance = get_distance (node, from); + continue; + } + current_distance = get_distance (node, from); + if (current_distance < distance) + { + closest = node; + distance = current_distance; + } + } + return closest; +} + +Label * +get_main_component (GList *node_list, Node *from, gdouble min_normalized_nr_nodes) +{ + Label *main_component = NULL; + gint distance = -1; + GList *current_node; + + for (current_node = g_list_first (node_list); + current_node != NULL; + current_node = g_list_next (current_node)) + { + Node *node; + Label *label; + gint current_distance; + node = (Node *) current_node->data; + label = node->label; + + if (main_component == NULL && + label->normalized_num_nodes > min_normalized_nr_nodes) + { + main_component = label; + distance = get_distance (node, from); + continue; + } + + current_distance = get_distance (node, from); + if (current_distance < distance && + label->normalized_num_nodes > min_normalized_nr_nodes) + { + main_component = label; + distance = current_distance; + } + } + + return main_component; +} + +Label * +label_find (Label *label) +{ + Label *parent; + + g_return_val_if_fail (label != NULL, NULL); + + parent = label->parent; + if (parent == label) + return parent; + else + return label_find (parent); +} + +void +label_union (Label *a, Label *b) +{ + Label *root_a, *root_b; + root_a = label_find (a); + root_b = label_find (b); + if (root_a->index < root_b->index) + { + b->parent = root_a; + } + else + { + a->parent = root_b; + } +} + +void +free_label (Label *label) +{ + g_list_free (label->nodes); + label->nodes = NULL; + g_slice_free (Label, label); +} + +void +clean_labels (GList *labels) +{ + GList *current = g_list_first (labels); + while (current != NULL) + { + Label *label; + label = (Label *) current->data; + free_label (label); + current = g_list_next (current); + } +} + +void +free_node (Node *node, gboolean unlink_node_first) +{ + if (unlink_node_first) + { + unlink_node (node); + } + else + { + g_list_free (node->neighbors); + g_list_free (node->linked_nodes); + node->neighbors = NULL; + node->linked_nodes = NULL; + } + g_slice_free (Node, node); +} + +void +clean_nodes (GList *nodes) +{ + GList *current = g_list_first (nodes); + while (current != NULL) + { + Node *node; + node = (Node *) current->data; + free_node (node, FALSE); + current = g_list_next (current); + } +} + +GList * +remove_nodes_with_label (GList *nodes, + Node **node_matrix, + gint width, + Label *label) +{ + Node *node; + GList *link_to_delete, *current_node; + + current_node = g_list_first (nodes); + while (current_node != NULL) + { + node = (Node *) current_node->data; + if (node->label == label) + { + link_to_delete = current_node; + current_node = g_list_next (current_node); + nodes = g_list_delete_link (nodes, link_to_delete); + node_matrix[width * node->j + node->i] = NULL; + free_node (node, TRUE); + continue; + } + current_node = g_list_next (current_node); + } + return nodes; +} + +Label * +get_lowest_index_label (Label **neighbor_labels) +{ + guint index; + Label *lowest_index_label = NULL; + + lowest_index_label = neighbor_labels[0]; + for (index = 1; index < 4; index++) + { + if (neighbor_labels[index] == NULL) + continue; + + if (lowest_index_label == NULL || + lowest_index_label->index < neighbor_labels[index]->index) + { + lowest_index_label = neighbor_labels[index]; + } + } + + return lowest_index_label; +} + +Label * +new_label (gint index) +{ + Label *label = g_slice_new (Label); + label->index = index; + label->parent = label; + label->nodes = NULL; + label->bridge_node = NULL; + label->to_node = NULL; + label->lower_screen_y = -1; + label->higher_z = -1; + label->lower_z = -1; + label->normalized_num_nodes = -1; + + return label; +} + +void +join_components_to_main (GList *labels, + Label *main_component_label, + guint horizontal_max_distance, + guint depth_max_distance, + guint graph_distance_threshold) +{ + GList *current_label; + + for (current_label = g_list_first (labels); + current_label != NULL; + current_label = g_list_next (current_label)) + { + gint closer_distance = -1; + Label *label; + GList *current_node, *nodes; + + label = (Label *) current_label->data; + if (label == main_component_label) + continue; + + /* Skip nodes behind main component */ + if (label->higher_z > main_component_label->higher_z + + graph_distance_threshold) + continue; + + nodes = label->nodes; + for (current_node = g_list_first (nodes); + current_node != NULL; + current_node = g_list_next (current_node)) + { + Node *node; + gint current_distance; + node = (Node *) current_node->data; + /* Skip nodes that belong to the same component or + that a not in the edge of their component */ + if (g_list_length (node->neighbors) == 8) + continue; + + Node *closest_node = + get_closest_node_with_distances (main_component_label->nodes, + node, + horizontal_max_distance, + horizontal_max_distance, + depth_max_distance, + ¤t_distance); + if (closest_node && + (current_distance < closer_distance || + closer_distance == -1)) + { + node->label->bridge_node = node; + node->label->to_node = closest_node; + closer_distance = current_distance; + } + } + } +} + +void +set_joint_from_node (SkeltrackJointList *joints, + Node *node, + SkeltrackJointId id, + gint dimension_reduction) +{ + (*joints)[id] = node_to_joint (node, id, dimension_reduction); +} + +gint * +create_new_dist_matrix (gint matrix_size) +{ + guint i; + gint *distances; + + distances = g_slice_alloc0 (matrix_size * sizeof (gint)); + for (i = 0; i < matrix_size; i++) + { + distances[i] = -1; + } + + return distances; +} + +gboolean +dijkstra_to (GList *nodes, Node *source, Node *target, + gint width, gint height, + gint *distances, Node **previous) +{ + gint nr; + GList *unvisited_nodes, *current; + + for (current = g_list_first (nodes); + previous != NULL && current != NULL; + current = g_list_next (current)) + { + Node *node; + node = (Node *) current->data; + previous[node->j * width + node->i] = NULL; + } + distances[source->j * width + source->i] = 0; + + unvisited_nodes = g_list_copy (nodes); + nr = 0; + while (unvisited_nodes != NULL) + { + Node *node; + GList *current_neighbor, *shorter_dist_node, *cur_node; + + shorter_dist_node = g_list_first (unvisited_nodes); + cur_node = g_list_next (shorter_dist_node); + while (cur_node != NULL) + { + Node *value, *shorter_dist; + value = (Node *) cur_node->data; + shorter_dist = (Node *) shorter_dist_node->data; + if (distances[shorter_dist->j * width + shorter_dist->i] == -1 || + (distances[value->j * width + value->i] != -1 && + distances[value->j * width + + value->i] < distances[shorter_dist->j * width + + shorter_dist->i])) + { + shorter_dist_node = cur_node; + } + cur_node = g_list_next (cur_node); + } + + node = (Node *) shorter_dist_node->data; + if (distances[node->j * width + node->i] == -1) + { + break; + } + + current_neighbor = g_list_first (node->neighbors); + while (current_neighbor) + { + gint dist; + Node *neighbor; + + neighbor = (Node *) current_neighbor->data; + dist = get_distance (node, neighbor) + + distances[node->j * width + node->i]; + + if (distances[neighbor->j * width + neighbor->i] == -1 || + dist < distances[neighbor->j * width + neighbor->i]) + { + distances[neighbor->j * width + neighbor->i] = dist; + if (previous != NULL) + { + previous[neighbor->j * width + neighbor->i] = node; + } + nr++; + } + if (target != NULL && neighbor == target) + { + g_list_free (unvisited_nodes); + return TRUE; + } + + current_neighbor = g_list_next (current_neighbor); + } + unvisited_nodes = g_list_delete_link (unvisited_nodes, shorter_dist_node); + } + g_list_free (unvisited_nodes); + return FALSE; +} + +void +convert_screen_coords_to_mm (guint width, + guint height, + guint dimension_reduction, + guint i, + guint j, + gint z, + gint *x, + gint *y) +{ + gfloat width_height_relation = + width > height ? (gfloat) width / height : (gfloat) height / width; + /* Formula from http://openkinect.org/wiki/Imaging_Information */ + *x = round((i * dimension_reduction - width * dimension_reduction / 2.0) * + (z + MIN_DISTANCE) * SCALE_FACTOR * width_height_relation); + *y = round((j * dimension_reduction - height * dimension_reduction / 2.0) * + (z + MIN_DISTANCE) * SCALE_FACTOR); +} + +void +convert_mm_to_screen_coords (guint width, + guint height, + guint dimension_reduction, + gint x, + gint y, + gint z, + guint *i, + guint *j) +{ + gfloat width_height_relation = + width > height ? (gfloat) width / height : (gfloat) height / width; + + if (z + MIN_DISTANCE == 0) + { + *i = 0; + *j = 0; + return; + } + + *i = round (width / 2.0 + x / ((gfloat) (z + MIN_DISTANCE) * SCALE_FACTOR * + dimension_reduction * width_height_relation)); + *j = round (height / 2.0 + y / ((gfloat) (z + MIN_DISTANCE) * SCALE_FACTOR * + dimension_reduction)); +} diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 586e806ce3..f01b9a5804 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -176,11 +176,15 @@ void Webcam::setFrame(const Mat& frame, const RotatedRect& faceRect) { QTimer::singleShot(qMax((int)remaining / 1000, 0), _grabber, SLOT(grabFrame())); } -FrameGrabber::FrameGrabber() : _capture(0), _searchWindow(0, 0, 0, 0) { +FrameGrabber::FrameGrabber() : _capture(0), _searchWindow(0, 0, 0, 0), _freenectContext(0) { } FrameGrabber::~FrameGrabber() { - if (_capture != 0) { + if (_freenectContext != 0) { + freenect_close_device(_freenectDevice); + freenect_shutdown(_freenectContext); + + } else if (_capture != 0) { cvReleaseCapture(&_capture); } } @@ -190,32 +194,14 @@ void FrameGrabber::reset() { } void FrameGrabber::grabFrame() { - if (_capture == 0) { - if ((_capture = cvCaptureFromCAM(-1)) == 0) { - printLog("Failed to open webcam.\n"); - return; - } - const int IDEAL_FRAME_WIDTH = 320; - const int IDEAL_FRAME_HEIGHT = 240; - cvSetCaptureProperty(_capture, CV_CAP_PROP_FRAME_WIDTH, IDEAL_FRAME_WIDTH); - cvSetCaptureProperty(_capture, CV_CAP_PROP_FRAME_HEIGHT, IDEAL_FRAME_HEIGHT); - -#ifdef __APPLE__ - configureCamera(0x5ac, 0x8510, false, 0.975, 0.5, 1.0, 0.5, true, 0.5); -#else - cvSetCaptureProperty(_capture, CV_CAP_PROP_EXPOSURE, 0.5); - cvSetCaptureProperty(_capture, CV_CAP_PROP_CONTRAST, 0.5); - cvSetCaptureProperty(_capture, CV_CAP_PROP_SATURATION, 0.5); - cvSetCaptureProperty(_capture, CV_CAP_PROP_BRIGHTNESS, 0.5); - cvSetCaptureProperty(_capture, CV_CAP_PROP_HUE, 0.5); - cvSetCaptureProperty(_capture, CV_CAP_PROP_GAIN, 0.5); -#endif - - switchToResourcesParentIfRequired(); - if (!_faceCascade.load("resources/haarcascades/haarcascade_frontalface_alt.xml")) { - printLog("Failed to load Haar cascade for face tracking.\n"); - } + if (_capture == 0 && _freenectContext == 0 && !init()) { + return; } + if (_freenectContext != 0) { + + return; + } + IplImage* image = cvQueryFrame(_capture); if (image == 0) { // try again later @@ -264,6 +250,47 @@ void FrameGrabber::grabFrame() { Q_ARG(cv::Mat, frame), Q_ARG(cv::RotatedRect, faceRect)); } +bool FrameGrabber::init() { + // first try for a Kinect + if (freenect_init(&_freenectContext, 0) >= 0) { + if (freenect_num_devices(_freenectContext) > 0) { + if (freenect_open_device(_freenectContext, &_freenectDevice, 0) >= 0) { + return true; + } + } + freenect_shutdown(_freenectContext); + _freenectContext = 0; + } + + // next, an ordinary webcam + if ((_capture = cvCaptureFromCAM(-1)) == 0) { + printLog("Failed to open webcam.\n"); + return false; + } + const int IDEAL_FRAME_WIDTH = 320; + const int IDEAL_FRAME_HEIGHT = 240; + cvSetCaptureProperty(_capture, CV_CAP_PROP_FRAME_WIDTH, IDEAL_FRAME_WIDTH); + cvSetCaptureProperty(_capture, CV_CAP_PROP_FRAME_HEIGHT, IDEAL_FRAME_HEIGHT); + +#ifdef __APPLE__ + configureCamera(0x5ac, 0x8510, false, 0.975, 0.5, 1.0, 0.5, true, 0.5); +#else + cvSetCaptureProperty(_capture, CV_CAP_PROP_EXPOSURE, 0.5); + cvSetCaptureProperty(_capture, CV_CAP_PROP_CONTRAST, 0.5); + cvSetCaptureProperty(_capture, CV_CAP_PROP_SATURATION, 0.5); + cvSetCaptureProperty(_capture, CV_CAP_PROP_BRIGHTNESS, 0.5); + cvSetCaptureProperty(_capture, CV_CAP_PROP_HUE, 0.5); + cvSetCaptureProperty(_capture, CV_CAP_PROP_GAIN, 0.5); +#endif + + switchToResourcesParentIfRequired(); + if (!_faceCascade.load("resources/haarcascades/haarcascade_frontalface_alt.xml")) { + printLog("Failed to load Haar cascade for face tracking.\n"); + return false; + } + return true; +} + void FrameGrabber::updateHSVFrame(const Mat& frame) { cvtColor(frame, _hsvFrame, CV_BGR2HSV); inRange(_hsvFrame, Scalar(0, 55, 65), Scalar(180, 256, 256), _mask); diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index 4bf6dea53e..59110d35a2 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -15,6 +15,8 @@ #include +#include + #include #include "InterfaceConfig.h" @@ -82,6 +84,7 @@ public slots: private: + bool init(); void updateHSVFrame(const cv::Mat& frame); CvCapture* _capture; @@ -91,6 +94,9 @@ private: cv::SparseMat _histogram; cv::Mat _backProject; cv::Rect _searchWindow; + + freenect_context* _freenectContext; + freenect_device* _freenectDevice; }; Q_DECLARE_METATYPE(cv::Mat)