diff --git a/.project b/.project
new file mode 100644
index 0000000..bfa6825
--- /dev/null
+++ b/.project
@@ -0,0 +1,11 @@
+
+
+ BF_PC_companion
+
+
+
+
+
+
+
+
diff --git a/BF_companion b/BF_companion
index 8266644..bc5b12c 100755
Binary files a/BF_companion and b/BF_companion differ
diff --git a/e502/.e502api_dnssd.c.kate-swp b/e502/.e502api_dnssd.c.kate-swp
new file mode 100644
index 0000000..4a3d6e0
Binary files /dev/null and b/e502/.e502api_dnssd.c.kate-swp differ
diff --git a/e502/CMakeLists.txt b/e502/CMakeLists.txt
new file mode 100644
index 0000000..48bc9d4
--- /dev/null
+++ b/e502/CMakeLists.txt
@@ -0,0 +1,170 @@
+cmake_minimum_required(VERSION 2.8.12)
+
+project(e502api C)
+set(PROJECT_VARNAME_PREFIX E502API)
+
+
+option(E502API_ENABLE_USB "enable usb interface support" ON)
+option(E502API_ENABLE_TCP "enable tcp interface support" ON)
+option(E502API_ENABLE_DNSSD "enable dns-sd service discovery" ON)
+
+set(OSSPEC_USE_MUTEX ON)
+if(E502API_ENABLE_USB)
+ option(E502API_LIBUSB_DEBUG "Print debug messages from libusb" OFF)
+ if(WIN32)
+ option(LIBUSB_INTERNAL "use internal libusb realisation" ON)
+ endif(WIN32)
+ set(OSSPEC_USE_EVENTS ON)
+ set(OSSPEC_USE_THREADS ON)
+else(E502API_ENABLE_USB)
+ set(OSSPEC_USE_EVENTS OFF)
+ set(OSSPEC_USE_THREADS OFF)
+endif(E502API_ENABLE_USB)
+
+if (E502API_ENABLE_DNSSD)
+ if(WIN32)
+ option(ENABLE_BONJOUR "enable bonjour support" ON)
+ else(WIN32)
+ option(ENABLE_AVAHI "enable avahi support" ON)
+ endif(WIN32)
+endif(E502API_ENABLE_DNSSD)
+
+
+include(${LTIMER_DIR}/ltimer.cmake)
+include(${OSSPEC_DIR}/osspec.cmake)
+set(E502API_COMPILE_DEFINITIONS ${E502API_COMPILE_DEFINITIONS} ${LTIMER_DEFINITIONS} ${OSSPEC_DEFINITIONS})
+
+set(SOURCES
+ e502api_usb.c
+ e502api_tcp.c
+ e502api_dnssd.c
+ e502api.c
+ e502api_eth_config.c
+ ${LTIMER_SOURCES}
+ ${OSSPEC_SOURCES}
+ )
+
+set(SETUP_HEADERS e502api.h)
+
+set(HEADERS
+ ${OSSPEC_HEADERS}
+ ${LTIMER_HEADERS}
+ e502_fpga_regs.h
+ e502_cm4_defs.h
+ e502_eth_config.h
+ e502api_private.h
+ e502api_tcp_private.h
+ e502_tcp_protocol.h)
+
+set(LIBS
+ x502api
+ ${LTIMER_LIBS}
+ ${OSSPEC_LIBS})
+
+include_directories(${X502API_LIB_DIR}/osspec)
+
+if (E502API_ENABLE_USB)
+ set(E502API_COMPILE_DEFINITIONS ${E502API_COMPILE_DEFINITIONS} ENABLE_USB)
+
+ if(E502API_LIBUSB_DEBUG)
+ set(E502API_COMPILE_DEFINITIONS ${E502API_COMPILE_DEFINITIONS} LIBUSB_DEBUG)
+ endif(E502API_LIBUSB_DEBUG)
+
+
+ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/")
+
+ if(LIBUSB_INTERNAL)
+ include(CheckStructHasMember)
+ check_struct_has_member("struct timespec" tv_sec time.h HAVE_STRUCT_TIMESPEC LANGUAGE C)
+ if(HAVE_STRUCT_TIMESPEC)
+ add_definitions(-DHAVE_STRUCT_TIMESPEC)
+ endif(HAVE_STRUCT_TIMESPEC)
+ # использование внутренней реализации libusb, а не внешеней библиотеки
+ # используется под windows, так как стандартная версия c WinUSB драйвером
+ # имеет проблемны при отмене трансферов
+ include_directories(libusb-1.0 libusb-1.0/msvc)
+ set(SOURCES ${SOURCES}
+ libusb-1.0/core.c
+ libusb-1.0/descriptor.c
+ libusb-1.0/hotplug.c
+ libusb-1.0/io.c
+ libusb-1.0/strerror.c
+ libusb-1.0/sync.c
+ libusb-1.0/os/poll_windows.c
+ libusb-1.0/os/threads_windows.c
+ libusb-1.0/os/windows_usb.c
+ )
+
+ set(HEADERS ${HEADERS}
+ libusb-1.0/libusb.h
+ libusb-1.0/libusbi.h
+ libusb-1.0/hotplug.h
+ libusb-1.0/version.h
+ libusb-1.0/version_nano.h
+ libusb-1.0/os/poll_windows.h
+ libusb-1.0/os/threads_windows.h
+ libusb-1.0/os/windows_common.h
+ libusb-1.0/msvc/config.h
+ libusb-1.0/msvc/errno.h
+ libusb-1.0/msvc/inttypes.h
+ libusb-1.0/msvc/missing.h
+ libusb-1.0/msvc/stdint.h
+ )
+ else()
+ #ищем libusb-1.0
+ find_package(LibUSB REQUIRED)
+ include_directories(${LIBUSB_INCLUDE_DIR})
+ set(LIBS ${LIBS} ${LIBUSB_LIBRARIES})
+ endif()
+endif(E502API_ENABLE_USB)
+
+
+if(E502API_ENABLE_TCP)
+ set(E502API_COMPILE_DEFINITIONS ${E502API_COMPILE_DEFINITIONS} ENABLE_TCP)
+ if(WIN32)
+ #подключение библиотеки для работы с сокетами
+ set(LIBS ${LIBS} Ws2_32)
+ endif(WIN32)
+endif(E502API_ENABLE_TCP)
+
+
+if(E502API_ENABLE_DNSSD)
+ set(E502API_COMPILE_DEFINITIONS ${E502API_COMPILE_DEFINITIONS} ENABLE_DNSSD)
+
+ if(ENABLE_BONJOUR)
+ find_package(DNSSD REQUIRED)
+ include_directories(${DNSSD_INCLUDE_DIRS})
+ set(LIBS ${LIBS} ${DNSSD_LIBRARIES})
+ set(E502API_COMPILE_DEFINITIONS ${E502API_COMPILE_DEFINITIONS} ENABLE_BONJOUR)
+ endif(ENABLE_BONJOUR)
+
+ if(ENABLE_AVAHI)
+ find_package(Avahi REQUIRED)
+ include_directories(${AVAHI_INCLUDE_DIRS})
+ set(LIBS ${LIBS} ${AVAHI_LIBRARIES})
+ set(E502API_COMPILE_DEFINITIONS ${E502API_COMPILE_DEFINITIONS} ENABLE_AVAHI)
+ endif(ENABLE_AVAHI)
+endif(E502API_ENABLE_DNSSD)
+
+
+message("e502 libs: ${LIBS}")
+
+include(${X502_LIBS_CMAKE_FILE})
+
+
+if(UNIX)
+ if (E502API_ENABLE_USB)
+ if(NOT UDEV_RULES_DIR)
+ set(UDEV_RULES_DIR lib/udev/rules.d)
+ endif(NOT UDEV_RULES_DIR)
+
+ install(FILES e502.rules DESTINATION ${UDEV_RULES_DIR})
+ install(FILES e16.rules DESTINATION ${UDEV_RULES_DIR})
+ endif(E502API_ENABLE_USB)
+endif(UNIX)
+
+
+
+
+
+
diff --git a/e502/cmake/modules/FindAvahi.cmake b/e502/cmake/modules/FindAvahi.cmake
new file mode 100644
index 0000000..58caf29
--- /dev/null
+++ b/e502/cmake/modules/FindAvahi.cmake
@@ -0,0 +1,9 @@
+find_library(AVAHI_LIBRARY-COMMON NAMES avahi-common)
+find_library(AVAHI_LIBRARY-CLIENT NAMES avahi-client)
+find_path(AVAHI_INCLUDE_DIR avahi-client/publish.h)
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Avahi DEFAULT_MSG AVAHI_LIBRARY-COMMON AVAHI_LIBRARY-CLIENT AVAHI_INCLUDE_DIR)
+if(AVAHI_FOUND)
+set(AVAHI_LIBRARIES ${AVAHI_LIBRARY-COMMON} ${AVAHI_LIBRARY-CLIENT})
+set(AVAHI_INCLUDE_DIRS ${AVAHI_INCLUDE_DIR})
+endif()
\ No newline at end of file
diff --git a/e502/cmake/modules/FindDNSSD.cmake b/e502/cmake/modules/FindDNSSD.cmake
new file mode 100644
index 0000000..410e6d5
--- /dev/null
+++ b/e502/cmake/modules/FindDNSSD.cmake
@@ -0,0 +1,64 @@
+# - Try to find dnssd library from Bonjour SDK
+# Once done this will define
+#
+# DNSSD_FOUND - system has dnssd library
+# DNSSD_INCLUDE_DIRS - the dnssd include directory
+# DNSSD_LIBRARIES - Link these to use dnssd library
+
+if(WIN32)
+ cmake_policy(VERSION 3.2)
+ endif(WIN32)
+
+
+if (DNSSD_INCLUDE_DIRS AND DNSSD_LIBRARIES)
+
+ # in cache already
+ set(DNSSD_FOUND TRUE)
+ message(STATUS "Found dnssd: ${DNSSD_LIBRARIES}, ${DNSSD_INCLUDE_DIRS}")
+
+else (DNSSD_INCLUDE_DIRS AND DNSSD_LIBRARIES)
+ string(REGEX REPLACE "\\\\" "/" PROGFILES $ENV{PROGRAMFILES})
+ string(REGEX REPLACE "\\\\" "/" PROGFILESX86 $ENV{PROGRAMFILES\(x86\)})
+
+
+ set(DNSSD_SEARCH_DIRS "${PROGFILES}/Bonjour SDK" "${PROGFILESX86}/Bonjour SDK" ${BONJOUR_ROOT_DIR})
+
+ find_path(DNSSD_INCLUDE_DIRS dns_sd.h
+ PATHS ${DNSSD_SEARCH_DIRS}
+ PATH_SUFFIXES "Include"
+ )
+
+ if( CMAKE_SIZEOF_VOID_P EQUAL 4 )
+ set(DNSSD_LIBRARY_PATH_SUFFIX "Lib/Win32")
+ else()
+ set(DNSSD_LIBRARY_PATH_SUFFIX "Lib/x64")
+ endif()
+
+ find_library(DNSSD_LIBRARIES NAMES dnssd
+ PATHS ${DNSSD_SEARCH_DIRS}
+ PATH_SUFFIXES ${DNSSD_LIBRARY_PATH_SUFFIX}
+
+ )
+
+ set(CMAKE_REQUIRED_INCLUDES ${DNSSD_INCLUDE_DIRS})
+ set(CMAKE_REQUIRED_LIBRARIES ${DNSSD_LIBRARIES})
+
+ if(DNSSD_INCLUDE_DIRS AND DNSSD_LIBRARIES)
+ set(DNSSD_FOUND TRUE)
+ else (DNSSD_INCLUDE_DIRS AND DNSSD_LIBRARIES)
+ set(DNSSD_FOUND FALSE)
+ endif(DNSSD_INCLUDE_DIRS AND DNSSD_LIBRARIES)
+
+ if (DNSSD_FOUND)
+ if (NOT DNSSD_FIND_QUIETLY)
+ message(STATUS "Found dnssd: ${DNSSD_LIBRARIES}, ${DNSSD_INCLUDE_DIRS}")
+ endif (NOT DNSSD_FIND_QUIETLY)
+ else (DNSSD_FOUND)
+ if (DNSSD_FIND_REQUIRED)
+ message(FATAL_ERROR "dnssd not found!")
+ endif (DNSSD_FIND_REQUIRED)
+ endif (DNSSD_FOUND)
+
+ mark_as_advanced(DNSSD_INCLUDE_DIRS DNSSD_LIBRARIES)
+
+endif (DNSSD_INCLUDE_DIRS AND DNSSD_LIBRARIES)
diff --git a/e502/cmake/modules/FindFFTW3.cmake b/e502/cmake/modules/FindFFTW3.cmake
new file mode 100644
index 0000000..81990ff
--- /dev/null
+++ b/e502/cmake/modules/FindFFTW3.cmake
@@ -0,0 +1,65 @@
+if (NOT FFTW3_FOUND)
+ if(WIN32)
+ cmake_policy(VERSION 3.2)
+ endif(WIN32)
+
+ if(WIN32)
+ string(REGEX REPLACE "\\\\" "/" PROGFILES $ENV{PROGRAMFILES})
+ string(REGEX REPLACE "\\\\" "/" PROGFILESX86 $ENV{PROGRAMFILES\(x86\)})
+
+ set(FFTW3_SEARCH_DIRS ${FFTW3_ROOT_DIR} ${PROGFILES} ${PROGFILES}/libs ${PROGFILESX86} ${PROGFILESX86}/libs)
+ set(FFTW3_SEARCH_INCLUDE_DIRS ${FFTW3_SEARCH_DIRS})
+ set(FFTW3_SEARCH_LIBRARY_DIRS ${FFTW3_SEARCH_DIRS})
+ set(FFTW3_SEARCH_INCLUDE_SUFFIX include libfftw3/include fftw3/include)
+ if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(FFTW3_SEARCH_LIBRARY_DEF_SUFFIX "msvc")
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(FFTW3_SEARCH_LIBRARY_DEF_SUFFIX "msvc64")
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ else("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(FFTW3_SEARCH_LIBRARY_DEF_SUFFIX "mingw")
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(FFTW3_SEARCH_LIBRARY_DEF_SUFFIX "mingw64")
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ endif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ set(FFTW3_SEARCH_LIBRARY_SUFFIX "lib/${FFTW3_SEARCH_LIBRARY_DEF_SUFFIX}"
+ "libfftw3/lib/${FFTW3_SEARCH_LIBRARY_DEF_SUFFIX}"
+ "fftw3/lib/${FFTW3_SEARCH_LIBRARY_DEF_SUFFIX}")
+ else(WIN32)
+ find_package(PkgConfig QUIET)
+ pkg_check_modules(FFTW3_PKG QUIET libfftw3)
+
+ set(FFTW3_SEARCH_INCLUDE_DIRS ${FFTW3_PKG_INCLUDE_DIRS} include local/include)
+
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(FFTW3_SEARCH_LIBRARY_DIRS lib local/lib lib/i386-linux-gnu)
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(FFTW3_SEARCH_LIBRARY_DIRS lib64 lib/x86_64-linux-gnu local/lib64)
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ endif(WIN32)
+
+ find_path(FFTW3_INCLUDE_DIR NAMES fftw3.h
+ PATHS
+ ${FFTW3_SEARCH_INCLUDE_DIRS}
+ PATH_SUFFIXES ${FFTW3_SEARCH_INCLUDE_SUFFIX}
+ )
+
+ find_library(FFTW3_LIBRARY NAMES fftw3
+ PATHS
+ ${FFTW3_SEARCH_LIBRARY_DIRS}
+ PATH_SUFFIXES ${FFTW3_SEARCH_LIBRARY_SUFFIX}
+ )
+
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(FFTW3
+ REQUIRED_VARS FFTW3_INCLUDE_DIR FFTW3_LIBRARY
+ )
+
+ if(FFTW3_FOUND)
+ set(FFTW3_LIBRARIES ${FFTW3_LIBRARY})
+ set(FFTW3_INCLUDE_DIRS ${FFTW3_INCLUDE_DIR})
+ endif(FFTW3_FOUND)
+endif (NOT FFTW3_FOUND)
+
diff --git a/e502/cmake/modules/FindGpgError.cmake b/e502/cmake/modules/FindGpgError.cmake
new file mode 100755
index 0000000..2069cb3
--- /dev/null
+++ b/e502/cmake/modules/FindGpgError.cmake
@@ -0,0 +1,16 @@
+# - Try to find libgpg-error
+# Once done this will define
+# GPGERROR_FOUND - System has libgpg-error
+# GPGERROR_INCLUDE_DIRS - The libgpg-error include directories
+# GPGERROR_LIBRARIES - The libraries needed to use libgpg-error
+# GPGERROR_DEFINITIONS - Compiler switches required for using libgpg-error
+
+find_path ( GPGERROR_INCLUDE_DIR gpg-error.h )
+find_library ( GPGERROR_LIBRARY NAMES gpg-error)
+
+set ( GPGERROR_LIBRARIES ${GPGERROR_LIBRARY} )
+set ( GPGERROR_INCLUDE_DIRS ${GPGERROR_INCLUDE_DIR} )
+
+include ( FindPackageHandleStandardArgs )
+
+find_package_handle_standard_args ( libgpg-error DEFAULT_MSG GPGERROR_LIBRARY GPGERROR_INCLUDE_DIR )
\ No newline at end of file
diff --git a/e502/cmake/modules/FindLTRAPI.cmake b/e502/cmake/modules/FindLTRAPI.cmake
new file mode 100644
index 0000000..86b5cb6
--- /dev/null
+++ b/e502/cmake/modules/FindLTRAPI.cmake
@@ -0,0 +1,100 @@
+# - Find LTRAPI library
+# This module defines
+# LTRAPI_INCLUDE_DIRS, where to find LTRAPI headers
+# LTRAPI_LIBRARIES, the libraries needed to LTRAPI
+# LTRAPI_FOUND, If false, do not try to use LTRAPI.
+#
+# Components: ltrapi, ltrXXXapi (xxx - module number)
+#
+# On Windows you can specify LTRAPI_ROOT_DIR for root dir of ltrapi installation (if not default)
+
+if (NOT LTRAPI_FOUND)
+ if(WIN32)
+ cmake_policy(VERSION 3.2)
+ endif(WIN32)
+
+ #по-умолчанию ищем все компоненты
+ if(NOT LTRAPI_FIND_COMPONENTS)
+ set(LTRAPI_FIND_COMPONENTS ltrapi)
+ endif(NOT LTRAPI_FIND_COMPONENTS)
+
+ if(WIN32)
+ string(REGEX REPLACE "\\\\" "/" PROGFILES $ENV{PROGRAMFILES})
+ string(REGEX REPLACE "\\\\" "/" PROGFILESX86 $ENV{PROGRAMFILES\(x86\)})
+
+ set(LTRAPI_SEARCH_DIRS ${PROGFILES} ${PROGFILESX86} ${LTRAPI_ROOT_DIR})
+ set(LTRAPI_SEARCH_INCLUDE_DIRS ${LTRAPI_SEARCH_DIRS})
+ set(LTRAPI_SEARCH_LIBRARY_DIRS ${LTRAPI_SEARCH_DIRS})
+ set(LTRAPI_SEARCH_INCLUDE_SUFFIX L-Card)
+ if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LTRAPI_SEARCH_LIBRARY_DEF_SUFFIX "msvc")
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LTRAPI_SEARCH_LIBRARY_DEF_SUFFIX "msvc64")
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ else("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LTRAPI_SEARCH_LIBRARY_DEF_SUFFIX "mingw")
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LTRAPI_SEARCH_LIBRARY_DEF_SUFFIX "mingw64")
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ endif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+
+ set(LTRAPI_SEARCH_LIBRARY_SUFFIX "lib/${LTRAPI_SEARCH_LIBRARY_DEF_SUFFIX}"
+ "ltr/lib/${LTRAPI_SEARCH_LIBRARY_DEF_SUFFIX}"
+ "L-Card/ltr/lib/${LTRAPI_SEARCH_LIBRARY_DEF_SUFFIX}")
+ else(WIN32)
+ find_package(PkgConfig QUIET)
+ pkg_check_modules(LTRAPI_PKG QUIET libltrapi1)
+
+ set(LTRAPI_SEARCH_INCLUDE_DIRS ${LTRAPI_PKG_INCLUDE_DIRS} include local/include)
+
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LTRAPI_SEARCH_LIBRARY_DIRS lib local/lib lib/i386-linux-gnu)
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LTRAPI_SEARCH_LIBRARY_DIRS lib64 lib/x86_64-linux-gnu local/lib64)
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ endif(WIN32)
+
+
+ foreach(LTRAPI_COMPONENT ${LTRAPI_FIND_COMPONENTS})
+ find_path(LTRAPI_${LTRAPI_COMPONENT}_INCLUDE_DIR NAMES ltr/include/${LTRAPI_COMPONENT}.h
+ PATHS
+ ${LTRAPI_SEARCH_INCLUDE_DIRS}
+ PATH_SUFFIXES ${LTRAPI_SEARCH_INCLUDE_SUFFIX}
+ )
+ if (WIN32)
+ find_library(LTRAPI_${LTRAPI_COMPONENT}_LIBRARY NAMES ${LTRAPI_COMPONENT}
+ PATHS
+ ${LTRAPI_SEARCH_LIBRARY_DIRS}
+ PATH_SUFFIXES ${LTRAPI_SEARCH_LIBRARY_SUFFIX}
+ NO_DEFAULT_PATH
+ )
+ else(WIN32)
+ find_library(LTRAPI_${LTRAPI_COMPONENT}_LIBRARY NAMES ${LTRAPI_COMPONENT}
+ PATHS
+ ${LTRAPI_SEARCH_LIBRARY_DIRS}
+ PATH_SUFFIXES ${LTRAPI_SEARCH_LIBRARY_SUFFIX}
+ )
+ endif(WIN32)
+
+ if (LTRAPI_${LTRAPI_COMPONENT}_INCLUDE_DIR AND LTRAPI_${LTRAPI_COMPONENT}_LIBRARY)
+ set(LTRAPI_${LTRAPI_COMPONENT}_FOUND TRUE)
+ set(LTRAPI_COMPONENT_LIBRARIES ${LTRAPI_COMPONENT_LIBRARIES} ${LTRAPI_${LTRAPI_COMPONENT}_LIBRARY})
+ set(LTRAPI_COMPONENT_INCLUDE_DIRS ${LTRAPI_COMPONENT_INCLUDE_DIRS} ${LTRAPI_${LTRAPI_COMPONENT}_INCLUDE_DIR})
+ endif()
+ endforeach()
+
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(LTRAPI REQUIRED_VARS
+ LTRAPI_COMPONENT_LIBRARIES
+ LTRAPI_COMPONENT_INCLUDE_DIRS
+ HANDLE_COMPONENTS)
+
+ if(LTRAPI_FOUND)
+ set(LTRAPI_LIBRARIES ${LTRAPI_LIBRARY} ${LTRAPI_COMPONENT_LIBRARIES})
+ set(LTRAPI_INCLUDE_DIRS ${LTRAPI_INCLUDE_DIR} ${LTRAPI_COMPONENT_INCLUDE_DIRS})
+ set(LTRAPI_COMPILE_DEFINITIONS LTRAPI_DISABLE_COMPAT_DEFS)
+ endif(LTRAPI_FOUND)
+endif (NOT LTRAPI_FOUND)
+
diff --git a/e502/cmake/modules/FindLibCBOR.cmake b/e502/cmake/modules/FindLibCBOR.cmake
new file mode 100644
index 0000000..4bcc33a
--- /dev/null
+++ b/e502/cmake/modules/FindLibCBOR.cmake
@@ -0,0 +1,66 @@
+
+if (NOT LIBCBOR_FOUND)
+ if(WIN32)
+ cmake_policy(VERSION 3.2)
+ endif(WIN32)
+
+ if(WIN32)
+ string(REGEX REPLACE "\\\\" "/" PROGFILES $ENV{PROGRAMFILES})
+ string(REGEX REPLACE "\\\\" "/" PROGFILESX86 $ENV{PROGRAMFILES\(x86\)})
+
+ set(LIBCBOR_SEARCH_DIRS ${LIBCBOR_ROOT_DIR} ${PROGFILES} ${PROGFILES}/libs ${PROGFILESX86} ${PROGFILESX86}/libs)
+ set(LIBCBOR_SEARCH_INCLUDE_DIRS ${LIBCBOR_SEARCH_DIRS})
+ set(LIBCBOR_SEARCH_LIBRARY_DIRS ${LIBCBOR_SEARCH_DIRS})
+ set(LIBCBOR_SEARCH_INCLUDE_SUFFIX include libcbor/include cbor/include)
+ if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LIBCBOR_SEARCH_LIBRARY_DEF_SUFFIX "msvc")
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LIBCBOR_SEARCH_LIBRARY_DEF_SUFFIX "msvc64")
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ else("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LIBCBOR_SEARCH_LIBRARY_DEF_SUFFIX "mingw")
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LIBCBOR_SEARCH_LIBRARY_DEF_SUFFIX "mingw64")
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ endif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ set(LIBCBOR_SEARCH_LIBRARY_SUFFIX "lib/${LIBCBOR_SEARCH_LIBRARY_DEF_SUFFIX}"
+ "libcbor/lib/${LIBCBOR_SEARCH_LIBRARY_DEF_SUFFIX}"
+ "cbor/lib/${LIBCBOR_SEARCH_LIBRARY_DEF_SUFFIX}")
+ else(WIN32)
+ find_package(PkgConfig QUIET)
+ pkg_check_modules(LIBCBOR_PKG QUIET libcbor)
+
+ set(LIBCBOR_SEARCH_INCLUDE_DIRS ${LIBCBOR_PKG_INCLUDE_DIRS} include local/include)
+
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LIBCBOR_SEARCH_LIBRARY_DIRS lib local/lib lib/i386-linux-gnu)
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LIBCBOR_SEARCH_LIBRARY_DIRS lib64 lib/x86_64-linux-gnu local/lib64)
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ endif(WIN32)
+
+ find_path(LIBCBOR_INCLUDE_DIR NAMES cbor.h
+ PATHS
+ ${LIBCBOR_SEARCH_INCLUDE_DIRS}
+ PATH_SUFFIXES ${LIBCBOR_SEARCH_INCLUDE_SUFFIX}
+ )
+
+ find_library(LIBCBOR_LIBRARY NAMES cbor libcbor liblibcbor
+ PATHS
+ ${LIBCBOR_SEARCH_LIBRARY_DIRS}
+ PATH_SUFFIXES ${LIBCBOR_SEARCH_LIBRARY_SUFFIX}
+ )
+
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(LibCBOR
+ REQUIRED_VARS LIBCBOR_INCLUDE_DIR LIBCBOR_LIBRARY
+ )
+
+ if(LIBCBOR_FOUND)
+ set(LIBCBOR_LIBRARIES ${LIBCBOR_LIBRARY})
+ set(LIBCBOR_INCLUDE_DIRS ${LIBCBOR_INCLUDE_DIR})
+ endif(LIBCBOR_FOUND)
+endif (NOT LIBCBOR_FOUND)
+
diff --git a/e502/cmake/modules/FindLibUSB.cmake b/e502/cmake/modules/FindLibUSB.cmake
new file mode 100644
index 0000000..66ebc23
--- /dev/null
+++ b/e502/cmake/modules/FindLibUSB.cmake
@@ -0,0 +1,48 @@
+# - Find libusb-1.0 library
+# This module defines
+# LIBUSB_INCLUDE_DIR, where to find bluetooth.h
+# LIBUSB_LIBRARIES, the libraries needed to use libusb-1.0.
+# LIBUSB_FOUND, If false, do not try to use libusb-1.0.
+#
+# Copyright (c) 2009, Michal Cihar,
+#
+# vim: expandtab sw=4 ts=4 sts=4:
+
+if (NOT LIBUSB_FOUND)
+ find_package(PkgConfig QUIET)
+ pkg_check_modules(LIBUSB_PKG QUIET libusb-1.0)
+
+ if( CMAKE_SIZEOF_VOID_P EQUAL 4 )
+ set(LIBUSB_SEARCH_PATH lib lib/i386-linux-gnu /usr/local/lib "c:/Program Files (x86)/libusb/MS32/static")
+ else()
+ set(LIBUSB_SEARCH_PATH lib64 lib/x86_64-linux-gnu /usr/local/lib64 "c:/Program Files (x86)/libusb/MS64/static")
+ endif()
+
+
+ find_path(LIBUSB_INCLUDE_DIR NAMES libusb-1.0/libusb.h
+ PATHS
+ ${LIBUSB_PKG_INCLUDE_DIRS}
+ /usr/include
+ /usr/local/include
+ "c:/Program Files (x86)/libusb/include"
+ )
+
+ find_library(LIBUSB_LIBRARIES NAMES usb-1.0 libusb-1.0
+ PATHS
+ ${LIBUSB_PKG_LIBRARY_DIRS}
+ /usr/lib
+ /usr/local/lib
+ ${LIBUSB_SEARCH_PATH}
+ )
+
+ if(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
+ set(LIBUSB_FOUND TRUE CACHE INTERNAL "libusb-1.0 found")
+ message(STATUS "Found libusb-1.0: ${LIBUSB_INCLUDE_DIR}, ${LIBUSB_LIBRARIES}")
+ else(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
+ set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found")
+ message(STATUS "libusb-1.0 not found.")
+ endif(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
+
+ mark_as_advanced(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES)
+endif (NOT LIBUSB_FOUND)
+
diff --git a/e502/cmake/modules/FindLibdaemon.cmake b/e502/cmake/modules/FindLibdaemon.cmake
new file mode 100644
index 0000000..e212118
--- /dev/null
+++ b/e502/cmake/modules/FindLibdaemon.cmake
@@ -0,0 +1,42 @@
+# - Find libdaemon library
+# This module defines
+# LIBDAEMON_INCLUDE_DIRS, where to find libdaemon headers
+# LIBDAEMON_LIBRARIES, the libraries needed to libdaemon
+# LIBDAEMON_FOUND, If false, do not try to use libdaemon.
+#
+
+if(NOT LIBDAEMON_FOUND)
+ find_package(PkgConfig QUIET)
+ pkg_check_modules(LIBDAEMON_PKG QUIET libdaemon)
+
+ set(LIBDAEMON_SEARCH_INCLUDE_DIRS ${LIBDAEMON_PKG_INCLUDE_DIRS} include local/include)
+
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LIBDAEMON_SEARCH_LIBRARY_DIRS lib local/lib lib/i386-linux-gnu)
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LIBDAEMON_SEARCH_LIBRARY_DIRS lib64 lib/x86_64-linux-gnu local/lib64)
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+
+ find_path(LIBDAEMON_INCLUDE_DIR NAMES libdaemon/daemon.h
+ PATHS
+ ${LIBDAEMON_SEARCH_INCLUDE_DIRS}
+ PATH_SUFFIXES
+ )
+
+ find_library(LIBDAEMON_LIBRARY NAMES daemon
+ PATHS
+ ${LIBDAEMON_SEARCH_LIBRARY_DIRS}
+ PATH_SUFFIXES libdaemon
+ )
+
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(LIBDAEMON
+ REQUIRED_VARS LIBDAEMON_INCLUDE_DIR LIBDAEMON_LIBRARY
+ )
+
+ if(LIBDAEMON_FOUND)
+ set(LIBDAEMON_LIBRARIES ${LIBDAEMON_LIBRARY})
+ set(LIBDAEMON_INCLUDE_DIRS ${LIBDAEMON_INCLUDE_DIR})
+ endif(LIBDAEMON_FOUND)
+endif(NOT LIBDAEMON_FOUND)
+
diff --git a/e502/cmake/modules/FindLibgcrypt.cmake b/e502/cmake/modules/FindLibgcrypt.cmake
new file mode 100755
index 0000000..2423147
--- /dev/null
+++ b/e502/cmake/modules/FindLibgcrypt.cmake
@@ -0,0 +1,66 @@
+find_path ( LIBGCRYPT_INCLUDE_DIR gcrypt.h )
+find_library ( LIBGCRYPT_LIBRARY NAMES gcrypt )
+
+
+if(WIN32)
+ cmake_policy(VERSION 3.2)
+
+ string(REGEX REPLACE "\\\\" "/" PROGFILES $ENV{PROGRAMFILES})
+ string(REGEX REPLACE "\\\\" "/" PROGFILESX86 $ENV{PROGRAMFILES\(x86\)})
+
+ set(LIBGCRYPT_SEARCH_DIRS ${LIBGCRYPT_ROOT_DIR} ${PROGFILES} ${PROGFILES}/libs ${PROGFILESX86} ${PROGFILESX86}/libs)
+ set(LIBGCRYPT_SEARCH_INCLUDE_DIRS ${LIBGCRYPT_SEARCH_DIRS})
+ set(LIBGCRYPT_SEARCH_LIBRARY_DIRS ${LIBGCRYPT_SEARCH_DIRS})
+ set(LIBGCRYPT_SEARCH_INCLUDE_SUFFIX include libgcrypt/include gcrypt/include)
+ if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LIBGCRYPT_SEARCH_LIBRARY_DEF_SUFFIX "msvc")
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LIBGCRYPT_SEARCH_LIBRARY_DEF_SUFFIX "msvc64")
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ else("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LIBGCRYPT_SEARCH_LIBRARY_DEF_SUFFIX "mingw")
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LIBGCRYPT_SEARCH_LIBRARY_DEF_SUFFIX "mingw64")
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ endif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ set(LIBGCRYPT_SEARCH_LIBRARY_SUFFIX "lib/${LIBGCRYPT_SEARCH_LIBRARY_DEF_SUFFIX}"
+ "libgcrypt/lib/${LIBGCRYPT_SEARCH_LIBRARY_DEF_SUFFIX}"
+ "gcrypt/lib/${LIBGCRYPT_SEARCH_LIBRARY_DEF_SUFFIX}")
+else(WIN32)
+ find_package(PkgConfig QUIET)
+ pkg_check_modules(LIBGCRYPT_PKG QUIET libgcrypt)
+
+ set(LIBGCRYPT_SEARCH_INCLUDE_DIRS ${LIBGCRYPT_PKG_INCLUDE_DIRS} include local/include)
+
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LIBGCRYPT_SEARCH_LIBRARY_DIRS lib local/lib lib/i386-linux-gnu)
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(LIBGCRYPT_SEARCH_LIBRARY_DIRS lib64 lib/x86_64-linux-gnu local/lib64)
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+endif(WIN32)
+
+
+
+find_path(LIBGCRYPT_INCLUDE_DIR NAMES gcrypt.h
+ PATHS
+ ${LIBGCRYPT_SEARCH_INCLUDE_DIRS}
+ PATH_SUFFIXES ${LIBGCRYPT_SEARCH_INCLUDE_SUFFIX}
+)
+
+find_library(LIBGCRYPT_LIBRARY NAMES gcrypt
+ PATHS
+ ${LIBGCRYPT_SEARCH_LIBRARY_DIRS}
+ PATH_SUFFIXES ${LIBGCRYPT_SEARCH_LIBRARY_SUFFIX}
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Libgcrypt
+ REQUIRED_VARS LIBGCRYPT_LIBRARY LIBGCRYPT_INCLUDE_DIR
+ )
+
+if(LIBGCRYPT_FOUND)
+ set(LIBGCRYPT_LIBRARIES ${LIBGCRYPT_LIBRARY})
+ set(LIBGCRYPT_INCLUDE_DIRS ${LIBGCRYPT_INCLUDE_DIR})
+endif(LIBGCRYPT_FOUND)
diff --git a/e502/cmake/modules/FindMKL.cmake b/e502/cmake/modules/FindMKL.cmake
new file mode 100644
index 0000000..29525a9
--- /dev/null
+++ b/e502/cmake/modules/FindMKL.cmake
@@ -0,0 +1,173 @@
+# базовая директория может быть задана с помощью переменной cmake MKL_ROOT (самый высокий приоритет)
+# или с помощью переменной окружения MKL_ROOT.
+# Если MKL_ROOT не задан, то ищет в стандартных путях:
+# на Windows в Program Files или Program Files (x86) в директории Intel/oneAPI/mkl/latest
+# на Linux в штатных путях (в корне или /usr)
+#
+# использует tbb threading model, соглашение ILP64 на 64 битах, cdecl на 32
+# (при необходимости можно добавить опции...)
+# можно задать пользовательскую версию библитеки
+# (если создана своя версия с подможеством функций с помощью intel compiler):
+# MKL_CUSTOM_LIBRARY_NAME - имя библиотеки
+# MKL_CUSTOM_LIBRARY_DIR - путь к пользовательской библиотеки без lib/<сиффикс> (может быть вне MKL_ROOT)
+# Заголовочные файлы в этом случае сперва ищутся в ${MKL_CUSTOM_LIBRARY_DIR}/include, а затем уже внутри MKL_ROOT
+
+
+# в первую очередь пытаемся найти mkl с помощью pkgconfig, если применимо
+if (NOT MKL_FOUND)
+ if(NOT WIN32 AND NOT MKL_CUSTOM_LIBRARY_NAME AND NOT MKL_ROOT)
+ find_package(PkgConfig QUIET)
+ if(PkgConfig_FOUND)
+ #ищем среди разных реализаций потоков в порядке приоритета
+ pkg_check_modules(MKL QUIET mkl-dynamic-ilp64-tbb)
+ if (NOT MKL_FOUND)
+ pkg_check_modules(MKL QUIET mkl-dynamic-ilp64-iomp)
+ endif(NOT MKL_FOUND)
+
+ if (NOT MKL_FOUND)
+ pkg_check_modules(MKL QUIET mkl-dynamic-ilp64-seq)
+ endif(NOT MKL_FOUND)
+ endif(PkgConfig_FOUND)
+ endif()
+endif(NOT MKL_FOUND)
+# если не нашли через PkgConfig, то ищмем самостоятельно
+if (NOT MKL_FOUND)
+ macro(MKL_FIND_LIBRARY LIB_VAR LIB_FILE_NAME)
+
+ find_library(${LIB_VAR} NAMES ${LIB_FILE_NAME}
+ PATHS
+ ${MKL_SEARCH_LIBRARY_DIRS}
+ PATH_SUFFIXES ${MKL_SEARCH_LIBRARY_DIR_SUFFIX}
+ )
+
+ get_filename_component(LIB_DIR ${${LIB_VAR}} DIRECTORY)
+ get_filename_component(LIB_NAME ${${LIB_VAR}} NAME)
+
+
+ if (${LIB_DIR} IN_LIST MKL_LIBRARY_DIRS)
+ else()
+ set(MKL_LIBRARY_DIRS ${MKL_LIBRARY_DIRS} ${LIB_DIR})
+ endif()
+ set(MKL_REQIRED_LIBS ${MKL_REQIRED_LIBS} ${LIB_VAR})
+ endmacro()
+
+ if(WIN32)
+ cmake_policy(VERSION 3.2)
+
+ # в переменных окружения могут быть пути с разделителем \.
+ # если пыти с разными разделителями, то могут быть проблемы, поэтому переводим
+ # все к /
+ if(NOT MKL_ROOT)
+ set(set MKL_ROOT $ENV{MKLROOT})
+ endif(NOT MKL_ROOT)
+ if(MKL_ROOT)
+ string(REGEX REPLACE "\\\\" "/" MKL_ROOT MKL_ROOT)
+ endif(MKL_ROOT)
+ string(REGEX REPLACE "\\\\" "/" PROGFILES $ENV{PROGRAMFILES})
+ string(REGEX REPLACE "\\\\" "/" PROGFILESX86 $ENV{PROGRAMFILES\(x86\)})
+
+ if(MKL_ROOT)
+ set(MKL_SEARCH_DIRS ${MKL_ROOT})
+ else(MKL_ROOT)
+ set(MKL_WINSTD_DIR "Intel/oneAPI/mkl/latest")
+ set(MKL_SEARCH_DIRS "${PROGFILES}/${MKL_WINSTD_DIR}" "${PROGFILESX86}/${MKL_WINSTD_DIR}")
+ endif(MKL_ROOT)
+ set(MKL_SEARCH_INCLUDE_DIRS ${MKL_SEARCH_DIRS})
+ set(MKL_SEARCH_LIBRARY_DIRS ${MKL_SEARCH_DIRS})
+ set(MKL_SEARCH_INCLUDE_DIR_SUFFIX include)
+ if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(MKL_SEARCH_LIBRARY_DEF_SUFFIX "ia32_win")
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(MKL_SEARCH_LIBRARY_DEF_SUFFIX "intel64_win")
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ endif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ set(MKL_SEARCH_LIBRARY_DIR_SUFFIX "lib/${MKL_SEARCH_LIBRARY_DEF_SUFFIX}")
+
+ #todo возможно сделать опцию, static или dll
+ set(MKL_LIB_FILE_SUFFIX "_dll")
+
+ else(WIN32)
+
+ if(NOT MKL_ROOT)
+ set(MKL_ROOT $ENV{MKLROOT})
+ endif(NOT MKL_ROOT)
+
+
+
+ if (MKL_ROOT)
+ set(MKL_SEARCH_INCLUDE_DIRS ${MKL_ROOT}/include)
+ else(MKL_ROOT)
+ set(MKL_SEARCH_INCLUDE_DIRS include local/include)
+ endif(MKL_ROOT)
+
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ if (MKL_ROOT)
+ set(MKL_SEARCH_LIBRARY_DIRS ${MKL_ROOT}/lib)
+ else (MKL_ROOT)
+ set(MKL_SEARCH_LIBRARY_DIRS lib local/lib lib/i386-linux-gnu)
+ endif(MKL_ROOT)
+ elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ if (MKL_ROOT)
+ set(MKL_SEARCH_LIBRARY_DIRS ${MKL_ROOT}/lib64 ${MKL_ROOT}/lib/intel64)
+ else(MKL_ROOT)
+ set(MKL_SEARCH_LIBRARY_DIRS lib64 lib/x86_64-linux-gnu local/lib64)
+ endif(MKL_ROOT)
+
+ #взято из готового примера - проверить необходимость
+ set(ABI "-m64")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ABI}")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ABI}")
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ endif(WIN32)
+
+
+ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ #todo возможно сделать опцию выбора между ILP64 и LP64 (MKL_LONG 64 или 32)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DMKL_ILP64")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMKL_ILP64")
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 8)
+
+
+ if(MKL_CUSTOM_LIBRARY_DIR)
+ set(MKL_SEARCH_INCLUDE_DIRS ${MKL_CUSTOM_LIBRARY_DIR} ${MKL_SEARCH_INCLUDE_DIRS})
+ endif(MKL_CUSTOM_LIBRARY_DIR)
+
+ find_path(MKL_INCLUDE_DIR NAMES mkl.h
+ PATHS
+ ${MKL_SEARCH_INCLUDE_DIRS}
+ PATH_SUFFIXES ${MKL_SEARCH_INCLUDE_DIR_SUFFIX}
+ )
+
+ if (NOT MKL_CUSTOM_LIBRARY_NAME)
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ MKL_FIND_LIBRARY(MKL_INTERFACE_LIBRARY mkl_intel_c${MKL_LIB_FILE_SUFFIX})
+ elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ MKL_FIND_LIBRARY(MKL_INTERFACE_LIBRARY mkl_intel_ilp64${MKL_LIB_FILE_SUFFIX})
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ MKL_FIND_LIBRARY(MKL_CORE_LIBRARY mkl_core${MKL_LIB_FILE_SUFFIX})
+
+ MKL_FIND_LIBRARY(MKL_THREAD_LIBRARY mkl_tbb_thread${MKL_LIB_FILE_SUFFIX})
+ if(NOT WIN32)
+ MKL_FIND_LIBRARY(MKL_TBB_LIBRARY tbb)
+ endif(NOT WIN32)
+ else(NOT MKL_CUSTOM_LIBRARY_NAME)
+ set(MKL_SEARCH_LIBRARY_DIRS ${MKL_SEARCH_LIBRARY_DIRS} ${MKL_CUSTOM_LIBRARY_DIR})
+ MKL_FIND_LIBRARY(MKL_CUSTOM_LIBRARY ${MKL_CUSTOM_LIBRARY_NAME})
+ endif(NOT MKL_CUSTOM_LIBRARY_NAME)
+
+ include(FindPackageHandleStandardArgs)
+
+
+ find_package_handle_standard_args(MKL
+ REQUIRED_VARS MKL_INCLUDE_DIR ${MKL_REQIRED_LIBS}
+ )
+ if(MKL_FOUND)
+ foreach(MKL_LIB ${MKL_REQIRED_LIBS})
+ set(MKL_LIBRARIES ${MKL_LIBRARIES} ${${MKL_LIB}})
+ endforeach()
+ set(MKL_INCLUDE_DIRS ${MKL_INCLUDE_DIR})
+
+ endif(MKL_FOUND)
+endif()
+
diff --git a/e502/cmake/modules/FindMODBUS.cmake b/e502/cmake/modules/FindMODBUS.cmake
new file mode 100644
index 0000000..108f2ea
--- /dev/null
+++ b/e502/cmake/modules/FindMODBUS.cmake
@@ -0,0 +1,55 @@
+# - Try to find libmodbus
+# Once done this will define
+#
+# MODBUS_FOUND - system has MODBUS
+# MODBUS_INCLUDE_DIRS - the MODBUS include directory
+# MODBUS_LIBRARIES - Link these to use MODBUS
+
+# Copyright (c) 2006, Jasem Mutlaq
+# Based on FindLibfacile by Carsten Niehaus,
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+
+if (MODBUS_INCLUDE_DIRS AND MODBUS_LIBRARIES)
+
+ # in cache already
+ set(MODBUS_FOUND TRUE)
+ message(STATUS "Found libmodbus: ${MODBUS_LIBRARIES}")
+
+else (MODBUS_INCLUDE_DIRS AND MODBUS_LIBRARIES)
+
+ find_path(MODBUS_INCLUDE_DIRS modbus.h
+ PATH_SUFFIXES modbus
+ ${_obIncDir}
+ ${GNUWIN32_DIR}/include
+ )
+
+ find_library(MODBUS_LIBRARIES NAMES modbus
+ PATHS
+ ${_obLinkDir}
+ ${GNUWIN32_DIR}/lib
+ )
+
+ set(CMAKE_REQUIRED_INCLUDES ${MODBUS_INCLUDE_DIRS})
+ set(CMAKE_REQUIRED_LIBRARIES ${MODBUS_LIBRARIES})
+
+ if(MODBUS_INCLUDE_DIRS AND MODBUS_LIBRARIES)
+ set(MODBUS_FOUND TRUE)
+ else (MODBUS_INCLUDE_DIRS AND MODBUS_LIBRARIES)
+ set(MODBUS_FOUND FALSE)
+ endif(MODBUS_INCLUDE_DIRS AND MODBUS_LIBRARIES)
+
+ if (MODBUS_FOUND)
+ if (NOT MODBUS_FIND_QUIETLY)
+ message(STATUS "Found libmodbus: ${MODBUS_LIBRARIES}")
+ endif (NOT MODBUS_FIND_QUIETLY)
+ else (MODBUS_FOUND)
+ if (MODBUS_FIND_REQUIRED)
+ message(FATAL_ERROR "libmodbus not found. Please install libmodbus from http://libmodbus.org/")
+ endif (MODBUS_FIND_REQUIRED)
+ endif (MODBUS_FOUND)
+
+ mark_as_advanced(MODBUS_INCLUDE_DIRS MODBUS_LIBRARIES)
+
+endif (MODBUS_INCLUDE_DIRS AND MODBUS_LIBRARIES)
diff --git a/e502/cmake/modules/FindX502API.cmake b/e502/cmake/modules/FindX502API.cmake
new file mode 100644
index 0000000..f31372a
--- /dev/null
+++ b/e502/cmake/modules/FindX502API.cmake
@@ -0,0 +1,110 @@
+# - Find x502api library
+# This module defines
+# X502API_INCLUDE_DIRS, where to find x502api headers
+# X502API_LIBRARIES, the libraries needed to x502api
+# X502API_FOUND, If false, do not try to use x502api.
+#
+# Components: l502api e502api (all enabled if not specified)
+#
+# On Windows you can specify LPCIE_ROOT_DIR for root dir of lpcie (if not default)
+
+if (NOT X502API_FOUND)
+ if(WIN32)
+ cmake_policy(VERSION 3.2)
+ endif(WIN32)
+
+ set(X502API_SUPPORTED_COMPONENTS x502api l502api e502api)
+
+ #по-умолчанию ищем все компоненты
+ if(NOT X502API_FIND_COMPONENTS)
+ set(X502API_FIND_COMPONENTS ${X502API_SUPPORTED_COMPONENTS})
+ else(NOT X502API_FIND_COMPONENTS)
+ set(X502API_FIND_COMPONENTS x502api ${X502API_FIND_COMPONENTS})
+ endif(NOT X502API_FIND_COMPONENTS)
+
+ if(WIN32)
+ string(REGEX REPLACE "\\\\" "/" PROGFILES $ENV{PROGRAMFILES})
+ string(REGEX REPLACE "\\\\" "/" PROGFILESX86 $ENV{PROGRAMFILES\(x86\)})
+
+ set(X502API_SEARCH_DIRS ${PROGFILES} ${PROGFILESX86} ${LPCIE_ROOT_DIR})
+ set(X502API_SEARCH_INCLUDE_DIRS ${X502API_SEARCH_DIRS})
+ set(X502API_SEARCH_LIBRARY_DIRS ${X502API_SEARCH_DIRS})
+ set(X502API_SEARCH_INCLUDE_SUFFIX L-Card/lpcie/include include)
+ if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(X502API_SEARCH_LIBRARY_DEF_SUFFIX "msvc")
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(X502API_SEARCH_LIBRARY_DEF_SUFFIX "msvc64")
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ else("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(X502API_SEARCH_LIBRARY_DEF_SUFFIX "mingw")
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(X502API_SEARCH_LIBRARY_DEF_SUFFIX "mingw64")
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ endif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+
+ set(X502API_SEARCH_LIBRARY_SUFFIX "lib/${X502API_SEARCH_LIBRARY_DEF_SUFFIX}"
+ "L-Card/lpcie/lib/${X502API_SEARCH_LIBRARY_DEF_SUFFIX}")
+ else(WIN32)
+ find_package(PkgConfig QUIET)
+ pkg_check_modules(X502API_PKG QUIET libx502api1)
+
+ set(X502API_SEARCH_INCLUDE_DIRS ${X502API_PKG_INCLUDE_DIRS} include local/include)
+
+ if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(X502API_SEARCH_LIBRARY_DIRS lib local/lib lib/i386-linux-gnu)
+ else(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(X502API_SEARCH_LIBRARY_DIRS lib64 lib/x86_64-linux-gnu local/lib64)
+ endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ endif(WIN32)
+
+
+ foreach(X502API_COMPONENT ${X502API_FIND_COMPONENTS})
+ list(FIND X502API_SUPPORTED_COMPONENTS ${X502API_COMPONENT} X502API_COMPONENT_IDX)
+ if(${X502API_COMPONENT_IDX}_IDX EQUAL -1)
+ message(WARNING "x502api: unsupported component ${X502API_COMPONENT}")
+ else()
+
+ find_path(X502API_${X502API_COMPONENT}_INCLUDE_DIR NAMES ${X502API_COMPONENT}.h
+ PATHS
+ ${X502API_SEARCH_INCLUDE_DIRS}
+ PATH_SUFFIXES ${X502API_SEARCH_INCLUDE_SUFFIX}
+ )
+
+
+ if (WIN32)
+ find_library(X502API_${X502API_COMPONENT}_LIBRARY NAMES ${X502API_COMPONENT}
+ PATHS
+ ${X502API_SEARCH_LIBRARY_DIRS}
+ PATH_SUFFIXES ${X502API_SEARCH_LIBRARY_SUFFIX}
+ NO_DEFAULT_PATH
+ )
+ else(WIN32)
+ find_library(X502API_${X502API_COMPONENT}_LIBRARY NAMES ${X502API_COMPONENT}
+ PATHS
+ ${X502API_SEARCH_LIBRARY_DIRS}
+ PATH_SUFFIXES ${X502API_SEARCH_LIBRARY_SUFFIX}
+ )
+ endif(WIN32)
+
+ if (X502API_${X502API_COMPONENT}_INCLUDE_DIR AND X502API_${X502API_COMPONENT}_LIBRARY)
+ set(X502API_${X502API_COMPONENT}_FOUND TRUE)
+ set(X502API_COMPONENT_LIBRARIES ${X502API_COMPONENT_LIBRARIES} ${X502API_${X502API_COMPONENT}_LIBRARY})
+ set(X502API_COMPONENT_INCLUDE_DIRS ${X502API_COMPONENT_INCLUDE_DIRS} ${X502API_${X502API_COMPONENT}_INCLUDE_DIR})
+ endif()
+ endif()
+ endforeach()
+
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(X502API REQUIRED_VARS
+ X502API_COMPONENT_LIBRARIES
+ X502API_COMPONENT_INCLUDE_DIRS
+ HANDLE_COMPONENTS)
+
+ if(X502API_FOUND)
+ set(X502API_LIBRARIES ${X502API_LIBRARY} ${X502API_COMPONENT_LIBRARIES})
+ set(X502API_INCLUDE_DIRS ${X502API_INCLUDE_DIR} ${X502API_COMPONENT_INCLUDE_DIRS})
+ endif(X502API_FOUND)
+endif (NOT X502API_FOUND)
+
diff --git a/e502/cmake/modules/FindXLSXWriter.cmake b/e502/cmake/modules/FindXLSXWriter.cmake
new file mode 100644
index 0000000..8040d17
--- /dev/null
+++ b/e502/cmake/modules/FindXLSXWriter.cmake
@@ -0,0 +1,52 @@
+# - Try to find libmodbus
+# Once done this will define
+#
+# XLSXWRITER_FOUND - system has XLSXWRITER
+# XLSXWRITER_INCLUDE_DIRS - the XLSXWRITER include directory
+# XLSXWRITER_LIBRARIES - Link these to use XLSXWRITER
+
+# Copyright (c) 2006, Jasem Mutlaq
+# Based on FindLibfacile by Carsten Niehaus,
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+
+if (XLSXWRITER_INCLUDE_DIRS AND XLSXWRITER_LIBRARIES)
+ # in cache already
+ set(XLSXWRITER_FOUND TRUE)
+ message(STATUS "Found libxlsxwriter: ${XLSXWRITER_LIBRARIES}")
+else (XLSXWRITER_INCLUDE_DIRS AND XLSXWRITER_LIBRARIES)
+ find_path(XLSXWRITER_INCLUDE_DIRS xlsxwriter.h
+ PATH_SUFFIXES xlsxwriter
+ ${_obIncDir}
+ ${GNUWIN32_DIR}/include
+ )
+
+ find_library(XLSXWRITER_LIBRARIES NAMES xlsxwriter
+ PATHS
+ ${_obLinkDir}
+ ${GNUWIN32_DIR}/lib
+ )
+
+ set(CMAKE_REQUIRED_INCLUDES ${XLSXWRITER_INCLUDE_DIRS})
+ set(CMAKE_REQUIRED_LIBRARIES ${XLSXWRITER_LIBRARIES})
+
+ if(XLSXWRITER_INCLUDE_DIRS AND XLSXWRITER_LIBRARIES)
+ set(XLSXWRITER_FOUND TRUE)
+ else (XLSXWRITER_INCLUDE_DIRS AND XLSXWRITER_LIBRARIES)
+ set(XLSXWRITER_FOUND FALSE)
+ endif(XLSXWRITER_INCLUDE_DIRS AND XLSXWRITER_LIBRARIES)
+
+ if (XLSXWRITER_FOUND)
+ if (NOT XLSXWRITER_FIND_QUIETLY)
+ message(STATUS "Foud llibxlsxwriter: ${XLSXWRITER_LIBRARIES}")
+ endif (NOT XLSXWRITER_FIND_QUIETLY)
+ else (XLSXWRITER_FOUND)
+ if (XLSXWRITER_FIND_REQUIRED)
+ message(FATAL_ERROR "llibxlsxwriter not found.")
+ endif (XLSXWRITER_FIND_REQUIRED)
+ endif (XLSXWRITER_FOUND)
+
+ mark_as_advanced(XLSXWRITER_INCLUDE_DIRS XLSXWRITER_LIBRARIES)
+
+endif (XLSXWRITER_INCLUDE_DIRS AND XLSXWRITER_LIBRARIES)
diff --git a/e502/cmake/modules/FindZMQ.cmake b/e502/cmake/modules/FindZMQ.cmake
new file mode 100644
index 0000000..1131909
--- /dev/null
+++ b/e502/cmake/modules/FindZMQ.cmake
@@ -0,0 +1,17 @@
+# - Try to find ZMQ
+# Once done this will define
+# ZMQ_FOUND - System has ZMQ
+# ZMQ_INCLUDE_DIRS - The ZMQ include directories
+# ZMQ_LIBRARIES - The libraries needed to use ZMQ
+# ZMQ_DEFINITIONS - Compiler switches required for using ZMQ
+
+find_path ( ZMQ_INCLUDE_DIR zmq.h )
+find_library ( ZMQ_LIBRARY NAMES zmq )
+
+set ( ZMQ_LIBRARIES ${ZMQ_LIBRARY} )
+set ( ZMQ_INCLUDE_DIRS ${ZMQ_INCLUDE_DIR} )
+
+include ( FindPackageHandleStandardArgs )
+# handle the QUIETLY and REQUIRED arguments and set ZMQ_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args ( ZMQ DEFAULT_MSG ZMQ_LIBRARY ZMQ_INCLUDE_DIR )
\ No newline at end of file
diff --git a/e502/e16.rules b/e502/e16.rules
new file mode 100644
index 0000000..e842599
--- /dev/null
+++ b/e502/e16.rules
@@ -0,0 +1 @@
+SUBSYSTEM=="usb", ATTRS{idVendor}=="2a52", ATTRS{idProduct}=="0e16", MODE="0666"
diff --git a/e502/e502.rules b/e502/e502.rules
new file mode 100644
index 0000000..9552016
--- /dev/null
+++ b/e502/e502.rules
@@ -0,0 +1 @@
+SUBSYSTEM=="usb", ATTRS{idVendor}=="2a52", ATTRS{idProduct}=="e502", MODE="0666"
diff --git a/e502/e502_cm4_defs.h b/e502/e502_cm4_defs.h
new file mode 100644
index 0000000..f4bdd20
--- /dev/null
+++ b/e502/e502_cm4_defs.h
@@ -0,0 +1,164 @@
+#ifndef E502_LPC_CMD_DEFS_H
+#define E502_LPC_CMD_DEFS_H
+
+
+#define E502_CM4_SUPPORT_OUT_CYCLE_SETUP_CHECK(ver) (ver >= 0x01000200)
+
+
+/***************************************************************************//**
+ @addtogroup e502proto_defs Определения протокола обмена с модулем E502
+ @{
+*******************************************************************************/
+
+/** Коды ошибок, возвращаемые модулем при завершении управляющей команды */
+typedef enum {
+ E502_CM4_ERR_OK = 0, /**< Команда выполнена успешно */
+ E502_CM4_ERR_FPGA_NSTATUS_TOUT = -1001, /**< При загрузке ПЛИС не удалось дождаться сигнала перехода в режим загрузки */
+ E502_CM4_ERR_FPGA_CONF_DONE_TOUT = -1002, /**< При загрузке ПЛИС не удалось дождаться сигнала завершения загрузки */
+ E502_CM4_ERR_FPGA_FW_NOT_PRESENT = -1003, /**< Не обнаружена прошивка ПЛИС во flash-памяти модуля */
+ E502_CM4_ERR_FPGA_REG_NACK = -1004, /**< Обращение к регистру ПЛИС вернуло ответ NACK */
+ E502_CM4_ERR_FPGA_REG_ERROR = -1005, /**< Обращение к регистру ПЛИС вернуло ответ ERROR */
+ E502_CM4_ERR_FPGA_REG_WT_TOUT = -1006, /**< Не удалось дожлаться ответ на обращение к регистру ПЛИС */
+ E502_CM4_ERR_TEST_INVALID_NUM = -1007, /**< Неподдерживаемый номер теста */
+ E502_CM4_ERR_HARD_FAULT = -1007, /**< Восстановление после hard fault */
+ E502_CM4_ERR_TEST_VALUE_MISMATH = -1008, /**< Несовпадение ожидаемых значений при проходе теста */
+ E502_CM4_ERR_TEST_NOT_RUNNING = -1009, /**< Тест не запущен */
+ E502_CM4_ERR_TEST_ALREADY_RUNNING = -1010, /**< Tест уже запщен */
+ E502_CM4_ERR_BF_LDR_FILE_SIZE = -1011, /**< Не удалось найти конец файла прошивки BlackFin */
+ E502_CM4_ERR_LDR_FILE_FORMAT = -1012, /**< Неверный формат файла прошивки BlackFin */
+ /** Используются возможность LDR-файла, недоступные при записи прошивки
+ BlackFin по HDMA */
+ E502_CM4_ERR_LDR_FILE_UNSUP_FEATURE = -1013,
+ /** Неверный стартовый адрес программы в прошивке BlackFin */
+ E502_CM4_ERR_LDR_FILE_UNSUP_STARTUP_ADDR = -1014,
+ /** Истек таймаут выполнения запроса на чтения/запись памяти BlackFin */
+ E502_CM4_ERR_BF_REQ_TIMEOUT = -1015,
+ /** Команда для BlackFin все еще находится в процессе обработки */
+ E502_CM4_ERR_BF_CMD_IN_PROGRESS = -1016,
+ /** Истекло время выполнения управляющей команды процессором BlackFin */
+ E502_CM4_ERR_BF_CMD_TIMEOUT = -1017,
+ /** Возвращено недостаточно данных в ответ на команду к BlackFin */
+ E502_CM4_ERR_BF_CMD_RETURN_INSUF_DATA = -1018,
+ /** Истек таймаут ожидания готовности процессора BlackFin к записи прошивки */
+ E502_CM4_ERR_BF_LOAD_RDY_TOUT = -1019,
+ /** Попытка выполнить операцию для которой нужен сигнальный процессор при
+ отсутствии сигнального процессора в модуле */
+ E502_CM4_ERR_BF_NOT_PRESENT = -1020,
+ /** Неверный адрес памяти BlackFin при записи или чтении по HDMA */
+ E502_CM4_ERR_BF_INVALID_ADDR = -1021,
+ /** Неверный размер данных, передаваемых с управляющей командой в BlackFin */
+ E502_CM4_ERR_BF_INVALID_CMD_DATA_SIZE = -1022,
+ E502_CM4_ERR_UNKNOWN_CMD = -1023, /**< Неподдерживаемый код команды */
+ E502_CM4_ERR_INVALID_CMD_PARAMS = -1024, /**< Неверные параметры переданной команды */
+ E502_CM4_ERR_FIRM_BUF_OVERFLOW = -1025, /**< Переполнение буфера для приема прошивки */
+ E502_CM4_ERR_CMD_SIGNATURE = -1026, /**< Неверный признак начала команды */
+ E502_CM4_ERR_INVALID_CMD_DATA_SIZE = -1027, /**< Неверное количество данных в команде */
+ E502_CM4_ERR_FLASH_PROT_CODE = -1028, /**< Неверный код настройки защиты Flash-памяти */
+ E502_CM4_ERR_FLASH_OP = -1029, /**< Ошибка выполнения операции с Flash-памятью */
+ E502_CM4_ERR_FLASH_DATA_COMPARE = -1030, /**< Ошибка сравнения записанных данных во Flash-память */
+ E502_CM4_ERR_INVALID_PASSWORD = -1031, /**< Неверный пароль для изменения сетевых настроек */
+ E502_CM4_ERR_FPGA_NOT_LOADED = -1032, /**< ПЛИС не был загружен */
+ E502_CM4_ERR_FLASH_SET_PROT_BITS = -1033, /**< Не удалось изменить занчения битов защиты Flash-памяти */
+ E502_CM4_ERR_FPGA_FW_INVALID_TEMP_RANGE = -1034, /**< Загруженная прошивка ПЛИС предназначена для другого темп. исполнения */
+ E502_CM4_ERR_M0_STREAM_START_REQ = -1035, /**< Нет ответа на запрос запуска потока от ядра Cortex-M0 */
+ E502_CM4_ERR_M0_STREAM_STOP_REQ = -1036, /**< Нет ответа на запрос останова потока от ядра Cortex-M0 */
+ E502_CM4_ERR_OUT_STREAM_RUNNING = -1037, /**< Уже запущен вывод в потоковом режиме */
+ E502_CM4_ERR_OUT_NO_CYCLE_BUF = -1038, /**< Нет свободного буфера для циклического режима. Не произошла смена страниц */
+ E502_CM4_ERR_OUT_CYCLE_BUF_SIZE = -1039, /**< Задан слишком большой размер циклического буфера */
+ E502_CM4_ERR_OUT_CYCLE_NOT_LOADED = -1040, /**< Не был полностью загружен циклический буфер перед сменой */
+} t_e502_cm4_errs;
+
+/** @} */
+
+
+typedef enum {
+ E502_STREAM_CH_IN = 0,
+ E502_STREAM_CH_OUT = 1
+} t_e502_stream_ch;
+
+
+typedef enum {
+ E502_IFACE_USB = 0,
+ E502_IFACE_TCP = 1
+} t_e502_ifaces;
+
+/** Коды команды протокола обмена с модулем E502 */
+typedef enum {
+ E502_CM4_CMD_GET_USB_SPEED = 6,
+ E502_CM4_CMD_GET_MODULE_NAME = 11,
+
+ E502_CM4_CMD_BOOT = 0x0F,
+ E502_CM4_CMD_FPGA_REG_READ = 0x10,
+ E502_CM4_CMD_FPGA_REG_WRITE = 0x11,
+ E502_CM4_CMD_STREAM_START = 0x12,
+ E502_CM4_CMD_STREAM_STOP = 0x13,
+ E502_CM4_CMD_STREAM_SET_STEP = 0x14,
+ E502_CM4_CMD_STREAM_IS_RUNNING = 0x15,
+ E502_CM4_CMD_FIRM_BUF_WRITE = 0x16,
+ E502_CM4_CMD_FLASH_RD = 0x17,
+ E502_CM4_CMD_FLASH_WR = 0x18,
+ E502_CM4_CMD_FLASH_ERASE = 0x19,
+ E502_CM4_CMD_FLASH_SET_PROTECT = 0x1A,
+ E502_CM4_CMD_FLASH_RELOAD_INFO = 0x1B,
+ E502_CM4_CMD_ETH_CFG_SET = 0x1C,
+ E502_CM4_CMD_ETH_CFG_GET = 0x1D,
+ E502_CM4_CMD_BF_MEM_WRITE = 0x20,
+ E502_CM4_CMD_BF_MEM_READ = 0x21,
+ E502_CM4_CMD_BF_FIRM_LOAD = 0x22,
+
+ E502_CM4_CMD_DROP_DATA_CON = 0x23,
+ E502_CM4_CMD_RELOAD_FPGA = 0x24,
+ E502_CM4_CMD_GET_DEVFLAGS = 0x25,
+
+ E502_CM4_CMD_OUT_CYCLE_LOAD = 0x26,
+ E502_CM4_CMD_OUT_CYCLE_SETUP = 0x27,
+ E502_CM4_CMD_OUT_CYCLE_STOP = 0x28,
+ E502_CM4_CMD_OUT_CYCLE_SETUP_CHECK = 0x29,
+
+
+
+ E502_CM4_CMD_TEST_START = 0x40,
+ E502_CM4_CMD_TEST_STOP = 0x41,
+ E502_CM4_CMD_TEST_GET_STATE = 0x42,
+
+ E502_CM4_CMD_GET_MODULE_INFO = 0x80,
+ E502_CM4_CMD_GET_MODULE_MODE = 0x81,
+
+ E502_CM4_CMD_GET_LAST_ERROR = 0x82
+} t_e502_cm4_cmd_codes;
+
+
+
+
+/** @internal */
+typedef enum {
+ E502_CM4_TEST_NONE=0,
+ E502_CM4_TEST_SRAM_BUF_RING,
+ E502_CM4_TEST_SRAM_SDRAM_RING_DMA,
+ E502_CM4_TEST_USB_TX_CNTR,
+ E502_CM4_TEST_USB_RING,
+ E502_CM4_TEST_SPI_SLAVE,
+ E502_CM4_TEST_SDRAM,
+ E502_CM4_TEST_ETH_PHY_LOOPBACK,
+ E502_CM4_TEST_DAC_CNTR,
+ E502_CM4_TEST_GD_SDIO,
+ E502_CM4_TEST_DCI_CNTR,
+ E502_CM4_TEST_FPGA_RING
+} t_test_number;
+
+
+/** @internal */
+/** Параметры теста */
+typedef struct {
+ uint32_t test; /**< Номер выполняемого теста */
+ uint32_t run; /**< Признак, запущен ли сейчас тест */
+ uint32_t stage; /**< Этап выполнения теста */
+ uint32_t cntr; /**< Счетчик - сколько раз прошел тест */
+ int32_t err; /**< Код ошибки выполнения теста */
+ uint32_t last_addr; /**< Последний используемый адрес */
+ uint32_t last_wr; /**< Последнее записанное значение */
+ uint32_t last_rd; /**< Последнее считанное значение */
+} t_e502_cm4_test_state;
+
+
+#endif // E502_LPC_CMD_DEFS_H
diff --git a/e502/e502_eth_config.h b/e502/e502_eth_config.h
new file mode 100644
index 0000000..20659fe
--- /dev/null
+++ b/e502/e502_eth_config.h
@@ -0,0 +1,51 @@
+#ifndef E502_ETH_CONFIG_H
+#define E502_ETH_CONFIG_H
+
+#define E502_ETHCONFIG_MAC_ADDR_SIZE 6
+#define E502_ETHCONFIG_INSTANCE_NAME_SIZE 64
+#define E502_ETHCONFIG_PASSWD_SIZE 32
+
+
+
+#define E502_IPV4_ADDR_SIZE 4
+
+typedef enum {
+ E502_ETH_FLAGS_IFACE_ENABLED = 0x0001,
+ E502_ETH_FLAGS_AUTO_IP = 0x0002,
+ E502_ETH_FLAGS_USER_MAC = 0x0004,
+ E502_ETH_FLAGS_ADDR_ESTABLISHED = 0x0008, /* адрес, выделенный DHCP-сервером, проверен */
+ E502_ETH_FLAGS_AUTO_IP_STATE_MASK = E502_ETH_FLAGS_ADDR_ESTABLISHED,
+} t_e502_eth_flags;
+
+
+typedef struct {
+ uint8_t addr[E502_IPV4_ADDR_SIZE];
+ uint8_t mask[E502_IPV4_ADDR_SIZE];
+ uint8_t gate[E502_IPV4_ADDR_SIZE];
+} t_e502_ipv4_config;
+
+typedef struct {
+ uint32_t format;
+ uint32_t flags;
+ char inst_name[E502_ETHCONFIG_INSTANCE_NAME_SIZE];
+ uint8_t mac[E502_ETHCONFIG_MAC_ADDR_SIZE];
+ t_e502_ipv4_config ipv4;
+ uint16_t tcp_cmd_port;
+ uint16_t tcp_data_port;
+} t_e502_eth_config;
+
+typedef enum {
+ E502_ETH_CONFIG_FLAGS_SET_NEW_PASSWD = 0x0001
+} t_e502_eth_config_flags;
+
+typedef struct {
+ char passwd[E502_ETHCONFIG_PASSWD_SIZE];
+ char new_passwd[E502_ETHCONFIG_PASSWD_SIZE];
+ t_e502_eth_config cfg;
+} t_e502_eth_set_config_params;
+
+#define E502_ETHCONFIG_SET_HDR_SIZE offsetof(t_e502_eth_set_config_params, cfg)
+
+
+#endif // E502_ETH_CONFIG_H
+
diff --git a/e502/e502_fpga_regs.h b/e502/e502_fpga_regs.h
new file mode 100644
index 0000000..a5abc0e
--- /dev/null
+++ b/e502/e502_fpga_regs.h
@@ -0,0 +1,208 @@
+#ifndef E502_FPGA_REGS_H
+#define E502_FPGA_REGS_H
+
+//#ifndef L5XX_REGS_H
+//#define L5XX_REGS_H
+
+#define E502_MAX_PAGES_CNT 252
+
+#define E502_BF_SDRAM_SIZE (32UL*1024*1024)
+
+#define E502_BF_MEMADDR_CMD 0xFF800800
+
+
+#define E502_BF_CMD_READ 0x0001
+#define E502_BF_CMD_WRITE 0x0002
+#define E502_BF_CMD_HIRQ 0x0004
+#define E502_BF_CMD_HDMA_RST 0x0008
+
+#define RING_MODE(a) (a << 2)
+
+// Разрешение синхронного потока цифрового вывода
+#define SYN_DIGOUT_EN (1 << 0)
+//Разрешение синхронного потока ЦАП1
+#define SYN_DAC1_EN (1 << 1)
+//Разрешение синхронного потока ЦАП2
+#define SYN_DAC2_EN (1 << 2)
+#define DCI_TEST_MODE (1)
+
+
+#define clk125_fail (1 << 0)
+#define clk125_lock (1 << 1)
+#define slv_clk_pll_fail (1 << 2)
+#define slv_clk_pll_lock (1 << 3)
+#define adcbuf_empty_err (1 << 4)
+#define adcbuf_full_err (1 << 5)
+#define dacbuf_empty_err (1 << 6)
+#define dacbuf_full_err (1 << 7)
+#define gd32_sdio_crc_err (1 << 8)
+#define dac_buf_chan_extra_err (1 << 9)
+#define dac_buf_chan_extra_err1 (1 << 10)
+#define dacbuf_rst_done_err (1 << 11)
+#define adcbuf_rst_done_err (1 << 12)
+#define ch_sdio_size_req_err (1 << 14)
+#define gd_sdio_size_req_err (1 << 15)
+#define ch32_sdio_crc_err (1 << 16)
+
+
+
+/********************* Адреса регистров блока ARM_INTERFACE *******************/
+
+#define E502_REGS_ARM_BLOCK 0x0100
+#define E502_REGS_ARM_DMA (E502_REGS_ARM_BLOCK+0)
+#define E502_REGS_ARM_FPGA_ERR (E502_REGS_ARM_BLOCK+1)
+
+#define E502_REGS_ARM_DAC_ERR (E502_REGS_ARM_BLOCK+3)
+#define DIGOUT_ERROR (1 << 0)
+#define DAC1_ERROR (1 << 2)
+#define DAC2_ERROR (1 << 4)
+
+#define E502_REGS_ARM_VERSION (E502_REGS_ARM_BLOCK + 2)
+#define E502_REGS_ARM_HARD_ID (E502_REGS_ARM_BLOCK + 0xA)
+#define E502_REGS_ARM_DEBUG_REG (E502_REGS_ARM_BLOCK + 0xB)
+#define E502_REGS_ARM_DAC_CH_EN (E502_REGS_ARM_BLOCK + 0xD)
+#define E502_REGS_ARM_TIME_CTRL (E502_REGS_ARM_BLOCK + 0x10)
+#define E502_REGS_ARM_TIME_SEC (E502_REGS_ARM_BLOCK + 0x11)
+#define E502_REGS_ARM_TIME_SSEC (E502_REGS_ARM_BLOCK + 0x12)
+#define E502_REGS_ARM_TIME_ADJ (E502_REGS_ARM_BLOCK + 0x13)
+#define E502_REGS_ARM_FLASHSIZE (E502_REGS_ARM_BLOCK + 0x14)
+
+/********************* Адреса регистров блока IOHARD **************************/
+#define E502_REGS_IOHARD_BLOCK 0x0200
+//Адрес Control Table
+#define E502_REGS_IOHARD_LTABLE (E502_REGS_IOHARD_BLOCK+0)
+#define E502_REGS_IOHARD_LTABLE_MAX_SIZE 0x100 // Максимальный размер Control Table
+
+#define E502_REGS_IOHARD_LCH_CNT (E502_REGS_IOHARD_BLOCK+0x100)
+#define E502_REGS_IOHARD_ADC_FREQ_DIV (E502_REGS_IOHARD_BLOCK+0x102)
+#define E502_REGS_IOHARD_ADC_FRAME_DELAY (E502_REGS_IOHARD_BLOCK+0x104)
+#define E502_REGS_IOHARD_DIGIN_FREQ_DIV (E502_REGS_IOHARD_BLOCK+0x106)
+#define E502_REGS_IOHARD_IO_MODE (E502_REGS_IOHARD_BLOCK+0x108)
+#define E502_REGS_IOHARD_GO_SYNC_IO (E502_REGS_IOHARD_BLOCK+0x10A)
+#define E502_REGS_IOHARD_PRELOAD_ADC (E502_REGS_IOHARD_BLOCK+0x10C)
+#define E502_REGS_IOHARD_DAC_FLUSH (E502_REGS_IOHARD_BLOCK+0x110)
+#define E502_REGS_IOHARD_ASYNC_OUT (E502_REGS_IOHARD_BLOCK+0x112)
+#define E502_REGS_IOHARD_LED (E502_REGS_IOHARD_BLOCK+0x114)
+#define E502_REGS_IOHARD_DIGIN_PULLUP (E502_REGS_IOHARD_BLOCK+0x116)
+#define E502_REGS_IOHARD_OUTSWAP_BFCTL (E502_REGS_IOHARD_BLOCK+0x118)
+#define E502_REGS_IOHARD_OUTSWAP_ERROR (E502_REGS_IOHARD_BLOCK+0x120)
+
+
+
+/********************* Адреса регистров блока IOARITH **************************/
+#define E502_REGS_IOARITH_BLOCK 0x0400
+#define E502_REGS_IOARITH_B10 E502_REGS_IOARITH_BLOCK
+#define E502_REGS_IOARITH_B5 (E502_REGS_IOARITH_BLOCK+0x01)
+#define E502_REGS_IOARITH_B2 (E502_REGS_IOARITH_BLOCK+0x02)
+#define E502_REGS_IOARITH_B1 (E502_REGS_IOARITH_BLOCK+0x03)
+#define E502_REGS_IOARITH_B05 (E502_REGS_IOARITH_BLOCK+0x04)
+#define E502_REGS_IOARITH_B02 (E502_REGS_IOARITH_BLOCK+0x05)
+#define E502_REGS_IOARITH_K10 (E502_REGS_IOARITH_BLOCK+0x08)
+#define E502_REGS_IOARITH_K5 (E502_REGS_IOARITH_BLOCK+0x09)
+#define E502_REGS_IOARITH_K2 (E502_REGS_IOARITH_BLOCK+0x0A)
+#define E502_REGS_IOARITH_K1 (E502_REGS_IOARITH_BLOCK+0x0B)
+#define E502_REGS_IOARITH_K05 (E502_REGS_IOARITH_BLOCK+0x0C)
+#define E502_REGS_IOARITH_K02 (E502_REGS_IOARITH_BLOCK+0x0D)
+#define E502_REGS_IOARITH_ADC_FREQ_DIV (E502_REGS_IOARITH_BLOCK+0x12)
+#define E502_REGS_IOARITH_THRESHOLD (E502_REGS_IOARITH_BLOCK+0x15)
+#define E502_REGS_IOARITH_N_CHAN_SYN (E502_REGS_IOARITH_BLOCK+0x16)
+#define E502_REGS_IOARITH_IN_STREAM_ENABLE (E502_REGS_IOARITH_BLOCK+0x19)
+#define E502_REGS_IOARITH_DIN_ASYNC (E502_REGS_IOARITH_BLOCK+0x1A)
+
+
+/********************* Адреса регистров блока CMD **************************/
+#define E502_REGS_CMD_BLOCK 0x0600
+
+/********************* Адреса регистров блока управления BlackFin'ом **********/
+#define E502_REGS_BF_CTL_BLOCK 0
+#define E502_REGS_BF_CTL (E502_REGS_BF_CTL_BLOCK+0)
+#define E502_REGS_BF_CMD (E502_REGS_BF_CTL_BLOCK+1)
+#define E502_REGS_BF_STATUS (E502_REGS_BF_CTL_BLOCK+2)
+#define E502_REGS_BF_IRQ (E502_REGS_BF_CTL_BLOCK+3)
+#define E502_REGS_BF_IRQ_EN (E502_REGS_BF_CTL_BLOCK+4)
+#define E502_REGS_BF_REQ_ADDR (E502_REGS_BF_CTL_BLOCK+5)
+#define E502_REGS_BF_REQ_SIZE (E502_REGS_BF_CTL_BLOCK+6)
+#define E502_REGS_BF_REQ_DATA (E502_REGS_BF_CTL_BLOCK+128)
+
+#define E502_BF_REQ_DATA_SIZE_MAX 128
+#define E502_BF_REQ_DATA_SIZE_MIN 8
+
+
+/********************* Адреса служебных регистров контроллера **************************/
+#define E502_REGS_ARM_SRV_BLOCK 0x0700
+#define E502_REGS_ARM_CH_UID (E502_REGS_ARM_SRV_BLOCK + 0)
+#define E502_REGS_ARM_GD_UID (E502_REGS_ARM_CH_UID + 3)
+#define E502_REGS_PTP_LOCK_LIMIT (E502_REGS_ARM_GD_UID + 3)
+#define E502_REGS_PINS_DEVID (E502_REGS_PTP_LOCK_LIMIT + 1)
+
+
+/* описание отдельных битов регистров */
+
+#define E502_REGBIT_ARM_DMA_ADC_BUF_CLR_Pos 0
+#define E502_REGBIT_ARM_DMA_ADC_BUF_CLR_Msk (1UL << E502_REGBIT_ARM_DMA_ADC_BUF_CLR_Pos)
+
+#define E502_REGBIT_ARM_DMA_DAC_BUF_CLR_Pos 1
+#define E502_REGBIT_ARM_DMA_DAC_BUF_CLR_Msk (1UL << E502_REGBIT_ARM_DMA_DAC_BUF_CLR_Pos)
+
+#define E502_REGBIT_ARM_DMA_RING_MODE_Pos 2
+#define E502_REGBIT_ARM_DMA_RING_MODE_Msk (1UL << E502_REGBIT_ARM_DMA_RING_MODE_Pos)
+
+
+#define E502_REGBIT_BF_STATUS_HWAIT_Pos 0
+#define E502_REGBIT_BF_STATUS_HWAIT_Msk (1UL << E502_REGBIT_BF_STATUS_HWAIT_Pos)
+
+#define E502_REGBIT_BF_STATUS_BUSY_Pos 1
+#define E502_REGBIT_BF_STATUS_BUSY_Msk (1UL << E502_REGBIT_BF_STATUS_BUSY_Pos)
+
+
+
+#define E502_REGBIT_BF_CTL_BF_RESET_Pos 1
+#define E502_REGBIT_BF_CTL_BF_RESET_Msk (0x1UL << E502_REGBIT_BF_CTL_BF_RESET_Pos)
+
+
+#define E502_REGBIT_BF_CTL_HOST_WAIT_Pos 3
+#define E502_REGBIT_BF_CTL_HOST_WAIT_Msk (0x1UL << E502_REGBIT_BF_CTL_HOST_WAIT_Pos)
+
+#define E502_REGBIT_BF_CTL_DSP_MODE_Pos 4
+#define E502_REGBIT_BF_CTL_DSP_MODE_Msk (0x1UL << E502_REGBIT_BF_CTL_DSP_MODE_Pos)
+
+#define E502_REGBIT_BF_CTL_DBG_MODE_Pos 5
+#define E502_REGBIT_BF_CTL_DBG_MODE_Msk (0x1UL << E502_REGBIT_BF_CTL_DBG_MODE_Pos)
+
+#define E502_REGBIT_BF_CTL_CLK_DIV_Pos 8
+#define E502_REGBIT_BF_CTL_CLK_DIV_Msk (0xFUL << E502_REGBIT_BF_CTL_CLK_DIV_Pos)
+
+#define E502_REGBIT_ADC_SLV_CLK_LOCK_Pos 31
+#define E502_REGBIT_ADC_SLV_CLK_LOCK_Msk (0x1UL << E502_REGBIT_ADC_SLV_CLK_LOCK_Pos)
+
+#define E502_REGBIT_IOHARD_OUT_SWAP_Pos 0
+#define E502_REGBIT_IOHARD_OUT_SWAP_Msk (0x1UL << E502_REGBIT_IOHARD_OUT_SWAP_Pos)
+
+#define E502_REGBIT_IOHARD_OUT_TFS_EN_Pos 1
+#define E502_REGBIT_IOHARD_OUT_TFS_EN_Msk (0x1UL << E502_REGBIT_IOHARD_OUT_TFS_EN_Pos)
+
+#define E502_REGBIT_IOHARD_OUT_RING_Pos 2
+#define E502_REGBIT_IOHARD_OUT_RING_Msk (0x1UL << E502_REGBIT_IOHARD_OUT_RING_Pos)
+
+#define E502_REGBIT_IOHARD_OUT_RFS_EN_Pos 3
+#define E502_REGBIT_IOHARD_OUT_RFS_EN_Msk (0x1UL << E502_REGBIT_IOHARD_OUT_RFS_EN_Pos)
+
+
+
+
+
+
+
+
+
+#define E502_REGBIT_DMA_IRQ_STEP_Msk(ch) (1UL << ch)
+#define E502_REGBIT_DMA_IRQ_PAGE_Msk(ch) (1UL << (ch+8))
+#define E502_REGBIT_DMA_IRQ_FLUSH_Msk(ch) (1UL << (ch+16))
+
+
+
+//#endif // L5XX_REGS_H
+
+
+
+#endif // E502_FPGA_REGS_H
diff --git a/e502/e502_tcp_protocol.h b/e502/e502_tcp_protocol.h
new file mode 100644
index 0000000..8cc1433
--- /dev/null
+++ b/e502/e502_tcp_protocol.h
@@ -0,0 +1,37 @@
+#ifndef E502_TCP_PROTOCOL_H
+#define E502_TCP_PROTOCOL_H
+
+//значения сигнатуры, которое является признаком действительной команды или ответа
+#define E502_TCP_CMD_SIGNATURE 0x314C5443
+//заголово команды
+typedef struct {
+ uint32_t sign;
+ uint32_t cmd; //код команды (реально используются 0-255)
+ uint32_t par; //параметр (соответствует value и index в usb)
+ uint32_t data_len; //кол-во данных на передачу в байтах
+ uint32_t resp_len; //кол-во ожидаем данных на прием в байтах
+} t_e502_tcp_cmd_hdr;
+
+//заголовок ответа
+typedef struct {
+ uint32_t sign;
+ int32_t res; //код возврата (0 - ОК, <0 - ошибка)
+ uint32_t len; //кол-во данных в ответе
+} t_e502_tcp_resp_hdr;
+
+#define E502_TCP_CMD_HDR_SIZE sizeof(t_e502_tcp_cmd_hdr)
+#define E502_TCP_CMD_RESP_SIZE sizeof(t_e502_tcp_resp_hdr)
+
+
+#define E502_TCP_CMD_RX_DATA_SIZE_MAX 512
+#define E502_TCP_CMD_TX_DATA_SIZE_MAX 512
+
+
+#define E502_TCP_DEFAULT_CMD_PORT 11114
+#define E502_TCP_DEFAULT_DATA_PORT 11115
+
+
+#define E502_TCP_SERVICE_NAME "_lcard_acqdev._tcp"
+
+
+#endif // E502_TCP_PROTOCOL_H
diff --git a/e502/e502api.c b/e502/e502api.c
new file mode 100644
index 0000000..6a837ae
--- /dev/null
+++ b/e502/e502api.c
@@ -0,0 +1,281 @@
+#include "e502api_private.h"
+#include
+#include
+
+#define BF_LOAD_TOUT 20000
+
+int32_t e502_iface_fpga_read(t_x502_hnd hnd, uint32_t addr, uint32_t *val) {
+ return hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_FPGA_REG_READ, addr, NULL, 0,
+ val, sizeof(*val), NULL, 0);
+}
+
+int32_t e502_iface_fpga_write(t_x502_hnd hnd, uint32_t addr, uint32_t val) {
+ return hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_FPGA_REG_WRITE, addr, &val, sizeof(val),
+ NULL, 0, NULL,0);
+}
+
+
+int32_t e502_iface_fpga_mode_init(t_x502_hnd hnd) {
+ int32_t err;
+ t_e502_cm4_test_state res;
+ err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_TEST_GET_STATE, 0, NULL, 0,
+ &res, sizeof(res), NULL, 0);
+ if ((err == X502_ERR_OK) && res.run) {
+ err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_TEST_STOP, 0, NULL, 0,
+ NULL, 0, NULL,0);
+ }
+ return err;
+}
+
+
+int32_t e502_iface_stream_running(t_x502_hnd hnd, uint32_t ch, int32_t* running) {
+ int32_t err;
+ uint8_t l_run;
+ err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_STREAM_IS_RUNNING, (ch << 16),
+ NULL, 0, &l_run, 1, NULL, 0);
+ if (!err && (running!=NULL)) {
+ *running = l_run;
+ }
+ return err;
+}
+
+
+int32_t e502_iface_bf_mem_block_rd(t_x502_hnd hnd, uint32_t addr, uint32_t *block, uint32_t size) {
+ return hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_BF_MEM_READ, addr, NULL, 0,
+ block, size*4, NULL, 0);
+}
+
+int32_t e502_iface_bf_mem_block_wr(t_x502_hnd hnd, uint32_t addr, const uint32_t *block, uint32_t size) {
+ return hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_BF_MEM_WRITE, addr, block, size*4,
+ NULL, 0, NULL, 0);
+}
+
+int32_t e502_iface_bf_firm_load(t_x502_hnd hnd, const char *filename) {
+ int32_t err = 0;
+ FILE* f=fopen(filename, "rb");
+ if (f==NULL) {
+ err = X502_ERR_LDR_FILE_OPEN;
+ } else {
+ uint8_t *buf = malloc(hnd->iface_hnd->ioctl_max_data_size);
+ long size, done=0;
+
+ if (buf == NULL)
+ err = X502_ERR_MEMORY_ALLOC;
+
+ //определяем размер файла
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+
+ /* данные записываем блоками по L502_BF_REQ_DATA_SIZE */
+ while (!err && (size!=done)) {
+ unsigned block_size = size-done;
+ if (block_size > hnd->iface_hnd->ioctl_max_data_size)
+ block_size = hnd->iface_hnd->ioctl_max_data_size;
+
+ if (fread(buf, 1, block_size, f) != block_size) {
+ err = X502_ERR_LDR_FILE_READ;
+ }
+
+ if (!err) {
+ err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_FIRM_BUF_WRITE,
+ done, buf, block_size, NULL, 0, NULL, 0);
+ }
+
+ if (!err) {
+ done += block_size;
+ }
+ }
+
+ if (!err) {
+ err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_BF_FIRM_LOAD, 0,
+ NULL, 0, NULL, 0, NULL, BF_LOAD_TOUT);
+ }
+
+ free(buf);
+
+ fclose(f);
+ }
+
+ return err;
+}
+
+
+int32_t e502_iface_flash_rd(t_x502_hnd hnd, uint32_t addr, uint8_t* data, uint32_t size) {
+ return hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_FLASH_RD, addr, NULL, 0,
+ data, size, NULL, 0);
+}
+
+int32_t e502_iface_flash_wr(t_x502_hnd hnd, uint32_t addr, const uint8_t* data, uint32_t size) {
+ return hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_FLASH_WR, addr, data, size,
+ NULL, 0, NULL, 0);
+}
+
+int32_t e502_iface_flash_erase(t_x502_hnd hnd, uint32_t addr, uint32_t size) {
+ return hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_FLASH_ERASE, addr, &size, sizeof(size), NULL, 0, NULL, 0);
+}
+
+int32_t e502_iface_flash_set_prot(t_x502_hnd hnd, uint32_t prot, const uint8_t* prot_data, uint32_t size) {
+ return hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_FLASH_SET_PROTECT, prot, prot_data, size, NULL, 0, NULL, 0);
+}
+
+int32_t e502_iface_reload_dev_info(t_x502_hnd hnd) {
+ int32_t err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_FLASH_RELOAD_INFO, 0, NULL, 0, NULL, 0, NULL, 0);
+ if (err == X502_ERR_OK) {
+ err = e502_fill_devflags(hnd);
+ }
+ return err;
+}
+
+
+
+int32_t e502_iface_cycle_load_start(t_x502_hnd hnd, uint32_t size) {
+ int32_t err = X502_ERR_OK;
+ if (!(hnd->flags & PRIV_FLAGS_CYCLE_MODE)) {
+ STREAM_OUT_CFG(hnd, err);
+ if (err == X502_ERR_OK)
+ err = hnd->iface_hnd->stream_start(hnd, X502_STREAM_CH_OUT, X502_STREAM_FLAG_NO_REQUEST);
+ }
+
+ if (err == X502_ERR_OK) {
+ err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_OUT_CYCLE_LOAD, size, NULL,0, NULL, 0, NULL, 0);
+ }
+
+ return err;
+}
+
+int32_t e502_iface_cycle_setup(t_x502_hnd hnd, uint32_t flags) {
+ return hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_OUT_CYCLE_SETUP, flags, NULL,0, NULL, 0, NULL, 0);
+}
+
+int32_t e502_iface_cycle_stop(t_x502_hnd hnd, uint32_t flags) {
+ int32_t err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_OUT_CYCLE_STOP, flags, NULL,0, NULL, 0, NULL, 0);
+ if (err == X502_ERR_OK)
+ err = hnd->iface_hnd->stream_free(hnd, X502_STREAM_CH_OUT, X502_STREAM_FLAG_NO_REQUEST);
+ return err;
+}
+
+bool E502_is_E16(t_x502_hnd hnd) {
+ if (strcmp(hnd->info.name, E16_DEVICE_NAME) == 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+int32_t e502_iface_cycle_check_setup(t_x502_hnd hnd, uint32_t *done) {
+ int32_t err = E502_CM4_SUPPORT_OUT_CYCLE_SETUP_CHECK(hnd->info.mcu_firmware_ver)
+ ? X502_ERR_OK : X502_ERR_NOT_SUP_BY_FIRMWARE;
+ if (E502_is_E16(hnd) || err == X502_ERR_OK) {
+ uint8_t ret_done;
+ err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_OUT_CYCLE_SETUP_CHECK, 0, NULL,0, &ret_done, sizeof(ret_done), NULL, 0);
+ if (err == X502_ERR_OK) {
+ *done = ret_done ? 1 : 0;
+ }
+ }
+ return err;
+}
+
+int32_t e502_iface_check_feature(t_x502_hnd hnd, uint32_t feature) {
+ int32_t err = X502_ERR_NOT_SUP_BY_FIRMWARE;
+ switch (feature) {
+ case X502_FEATURE_OUT_FREQ_DIV:
+ case X502_FEATURE_OUT_STATUS_FLAGS:
+ err = X502_ERR_OK;
+ break;
+ default:
+ err = X502_ERR_UNKNOWN_FEATURE_CODE;
+ break;
+ }
+ return err;
+}
+
+void e502_devinfo_init(t_x502_info *info, const t_lboot_devinfo *lboot_info) {
+ int ver[4];
+ int ver_comp_valid;
+
+ strcpy(info->serial, lboot_info->serial);
+ info->mcu_firmware_ver = 0;
+ ver_comp_valid = sscanf(lboot_info->soft_ver, "%d.%d.%d.%d", &ver[0], &ver[1], &ver[2], &ver[3]);
+ if (ver_comp_valid >= 1)
+ info->mcu_firmware_ver |= (ver[0]&0xFF) << 24;
+ if (ver_comp_valid >= 2)
+ info->mcu_firmware_ver |= (ver[1]&0xFF) << 16;
+ if (ver_comp_valid >= 3)
+ info->mcu_firmware_ver |= (ver[2]&0xFF) << 8;
+ if (ver_comp_valid >= 4)
+ info->mcu_firmware_ver |= ver[3]&0xFF;
+}
+
+int32_t e502_fill_devflags(t_x502_hnd hnd) {
+ int32_t err;
+ uint32_t devflags;
+
+ err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_GET_DEVFLAGS, 0, NULL, 0, &devflags,
+ sizeof(devflags), NULL, 0);
+ if (err == X502_ERR_OK) {
+ hnd->info.devflags &= ~E502_CM4_DEVFLAGS;
+ hnd->info.devflags |= devflags;
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_SwitchToBootloader(t_x502_hnd hnd) {
+ int32_t err = X502_CHECK_HND_OPENED(hnd);
+ if (err == X502_ERR_OK) {
+ err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_BOOT, 0, NULL, 0, NULL, 0, NULL, 0);
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_ReloadFPGA(t_x502_hnd hnd) {
+ int32_t err = X502_CHECK_HND_OPENED(hnd);
+ if (err == X502_ERR_OK) {
+ err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_RELOAD_FPGA, 0, NULL, 0, NULL, 0, NULL, 0);
+ }
+ return err;
+}
+
+
+X502_EXPORT(int32_t) E502_CortexExecCmd(t_x502_hnd hnd, uint32_t cmd_code, uint32_t par,
+ const uint8_t* snd_data, uint32_t snd_size,
+ uint8_t* rcv_data, uint32_t rcv_size,
+ uint32_t tout, uint32_t* recvd_size) {
+ int32_t err = X502_CHECK_HND_OPENED(hnd);
+ if (err == X502_ERR_OK) {
+ err = hnd->iface_hnd->gen_ioctl(hnd, cmd_code, par,
+ snd_data, snd_size, rcv_data, rcv_size,
+ recvd_size, tout);
+ }
+ return err;
+}
+
+
+
+
+
+
+#ifdef WIN32
+#include
+BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
+ WSADATA wsaData;
+ WORD wVersionRequested;
+
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ wVersionRequested = MAKEWORD(2, 2);
+ if (WSAStartup(wVersionRequested, &wsaData))
+ return FALSE;
+ if (wsaData.wVersion != wVersionRequested) {
+ WSACleanup();
+ return FALSE;
+ }
+ break;
+ case DLL_PROCESS_DETACH:
+ WSACleanup();
+ break;
+ }
+ return TRUE;
+}
+#endif
diff --git a/e502/e502api.def b/e502/e502api.def
new file mode 100644
index 0000000..ae8c27f
--- /dev/null
+++ b/e502/e502api.def
@@ -0,0 +1,60 @@
+LIBRARY e502api.dll
+
+EXPORTS
+ E502_UsbGetDevRecordsList
+ E502_UsbGetDevRecordsList2
+ E16_UsbGetDevRecordsList
+ E440_UsbGetDevRecordsList
+ E502_UsbGetSerialList
+ E16_UsbGetSerialList
+ E502_OpenUsb
+ E16_OpenUsb
+ E502_OpenByIpAddr
+ E16_OpenByIpAddr
+ E502_MakeDevRecordByIpAddr
+ E502_MakeDevRecordByIpAddr2
+ E502_MakeDevRecordByEthSvc
+ E502_EthConfigCreate
+ E502_EthConfigFree
+ E502_GetIpAddr
+ E502_EthConfigRead
+ E502_EthConfigWrite
+ E502_EthConfigGetEnabled
+ E502_EthConfigSetEnabled
+ E502_EthConfigGetAutoIPEnabled
+ E502_EthConfigSetAutoIPEnabled
+ E502_EthConfigGetAutoIPState
+ E502_EthConfigGetUserMACEnabled
+ E502_EthConfigSetUserMACEnabled
+ E502_EthConfigGetIPv4Addr
+ E502_EthConfigSetIPv4Addr
+ E502_EthConfigGetIPv4Mask
+ E502_EthConfigSetIPv4Mask
+ E502_EthConfigGetIPv4Gate
+ E502_EthConfigSetIPv4Gate
+ E502_EthConfigGetUserMac
+ E502_EthConfigSetUserMac
+ E502_EthConfigGetFactoryMac
+ E502_EthConfigGetInstanceName
+ E502_EthConfigSetInstanceName
+ E502_EthConfigSetNewPassword
+ E502_EthConfigCopy
+ E502_SwitchToBootloader
+ E502_ReloadFPGA
+ E502_CortexExecCmd
+ E502_EthSvcRecordFree
+ E502_EthSvcRecordGetInstanceName
+ E502_EthSvcRecordGetDevName
+ E502_EthSvcRecordGetDevSerial
+ E502_EthSvcRecordResolveIPv4Addr
+ E502_EthSvcRecordIsSameInstance
+ E502_EthSvcBrowseStart
+ E502_EthSvcBrowseGetEvent
+ E502_EthSvcBrowseStop
+ E502_EthDevRecordSetCmdPort
+ E502_EthDevRecordSetDataPort
+ E502_SearchEthForDevicesIPv4Addr
+ E502_EthConfigSetTcpCmdPort
+ E502_EthConfigSetTcpDataPort
+ E502_EthConfigGetTcpCmdPort
+ E502_EthConfigGetTcpDataPort
diff --git a/e502/e502api.h b/e502/e502api.h
new file mode 100644
index 0000000..6c1bdcd
--- /dev/null
+++ b/e502/e502api.h
@@ -0,0 +1,1057 @@
+#ifndef E502API_H
+#define E502API_H
+
+#include "x502api.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/***************************************************************************//**
+ @addtogroup const_list
+ @{
+ *****************************************************************************/
+/** @brief События поиска сетевых сервисов
+
+ Коды событий, возникающих при поиске сетевых сервисов, возвращаемые
+ функцией E502_EthSvcBrowseGetEvent() */
+typedef enum {
+ E502_ETH_SVC_EVENT_NONE = 0, /**< Ни одного события не произошло */
+ E502_ETH_SVC_EVENT_ADD = 1, /**< Обнаружено появление нового сетевого сервиса */
+ E502_ETH_SVC_EVENT_REMOVE = 2, /**< Обнаружено исчезновение ранее доступного
+ сетевого сервиса */
+ E502_ETH_SVC_EVENT_CHANGED = 3 /**< Изменение параметров ранее обнаруженного
+ сетевого сервиса */
+} t_e502_eth_svc_event;
+
+/** @} */
+
+/***************************************************************************//**
+ @addtogroup type_list
+ @{
+*******************************************************************************/
+
+/***************************************************************************//**
+ @brief Описатель конфигурации сетевого интерфейса
+
+ Непрозрачный указатель на структуру, содержащую параметры конфигурации
+ сетевого интерфейса модуля E-502.
+ Пользовательской программе не доступны поля структуры напрямую, а только
+ через функции библиотеки.
+ Описатель конфигурации создается с помощью E502_EthConfigCreate() и в конце работы
+ освобождается с помощью E502_EthConfigFree().
+ Как правило все настройки не должны заполняться пользователем вручную,
+ обычно сперва они считываются из устройства с помощью E502_EthConfigRead(),
+ после чего часть настроек можно изменить и сохранить в модуль через
+ E502_EthConfigWrite()
+ *****************************************************************************/
+typedef struct st_e502_eth_config_state* t_e502_eth_config_hnd;
+
+/**************************************************************************//**
+ @brief Описатель контекста поиска устройств в сети
+
+ Указатель на непрозрачную структуру с информацией о состоянии текущего
+ сеанса поиска устройств в сети. Создается при начале поиска вызовом
+ E502_EthSvcBrowseStart() и уничтожается с помощью E502_EthSvcBrowseStop()
+ *****************************************************************************/
+typedef struct st_e502_eth_svc_browse_context *t_e502_eth_svc_browse_hnd;
+/**************************************************************************//**
+ @brief Описатель сетевого сервиса
+
+ Указатель на непрозрачную структуру с информацией о сервисе в сети,
+ соответствующем одному модулю E-502. Используется при автоматическом обнаружении
+ устройств в локальной сети. Создается при вызове E502_EthSvcBrowseGetEvent()
+ и уничтожается с помощью E502_EthSvcRecordFree()
+ *****************************************************************************/
+typedef struct st_e502_eth_svc_record *t_e502_eth_svc_record_hnd;
+
+/** @} */
+
+/***************************************************************************//**
+ @addtogroup func_open
+ @{
+*******************************************************************************/
+
+/***************************************************************************//**
+ @brief Получение списка серийных номеров модулей E-502, подключенных по USB
+
+ Функция возвращает список номеров всех найденных модулей E-502, независимо от
+ того, открыты они сейчас или нет.
+
+ Функция на данный момент не поддерживает флаг #X502_GETDEVS_FLAGS_ONLY_NOT_OPENED.
+
+ @param[in] serials Массив размером size*#X502_SERIAL_SIZE байт, в который
+ будут сохранены серийные номера найденных модулей.
+ Может быть NULL, если size=0, а devcnt!=NULL, в случае,
+ если нужно только получить количество модулей в системе.
+ @param[in] size Определяет, сколько максимально серийных номеров может
+ быть сохранено в массив serial. Будут сохранены только
+ первые size серийных номеров.
+ Может быть 0, если serials=NULL
+ @param[in] flags Флаги из #t_x502_getdevs_flags, определяющие поведение
+ функции.
+ @param[out] devcnt Если devcnt!=NULL, то в данную переменную сохраняется
+ общее число найденных модулей E502
+ (может быть больше size).
+ @return Если <0 - код ошибки, иначе количество сохраненных
+ серийных номеров в массиве serials (всегда <= size)
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_UsbGetSerialList(char serials[][X502_SERIAL_SIZE], uint32_t size,
+ uint32_t flags, uint32_t *devcnt);
+
+/***************************************************************************//**
+ @brief Работает аналогично E502_UsbGetSerialList, только для модулей E16
+ ******************************************************************************/
+X502_EXPORT(int32_t) E16_UsbGetSerialList(char serials[][X502_SERIAL_SIZE], uint32_t size,
+ uint32_t flags, uint32_t *devcnt);
+
+/***************************************************************************//**
+ @brief Открытие модуля E-502, подключенного по USB, по его серийному номеру
+
+ Функция устанавливает связь с модулем E-502, подключенным по интерфейсу USB,
+ по его серийному номеру.
+
+ После успешного выполнения этой функции, пользователь получает эксклюзивный
+ доступ к модулю через описатель модуля. До закрытия связи с помощью
+ X502_Close() никто другой установить связь с модулем не сможет
+ (будет возвращена ошибка #X502_ERR_DEVICE_ACCESS_DENIED).
+
+ Если в качестве серийного номера передан NULL или пустая строка, то будет
+ установлена связь с первым найденным модулем, с которым получится успешно
+ ее установить.
+ Если в системе нет ни одного модуля, то будет возвращена ошибка
+ #X502_ERR_DEVICE_NOT_FOUND. Если в системе присутствуют модули E-502, но
+ соединение ни с одним из них установить не удалось, то будет возвращена
+ ошибка, полученная при попытке установить соединение с последним
+ найденным модулем.
+
+ После завершения работы с устройством соединение должно быть закрыто с
+ помощью X502_Close().
+
+ @param[in] hnd Описатель устройства.
+ @param[in] serial Указатель на строку с серийным номером открываемого
+ модуля или NULL.
+ @return Код ошибки.
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_OpenUsb(t_x502_hnd hnd, const char *serial);
+
+/***************************************************************************//**
+ @brief Работает аналогично E502_OpenUsb, только для модулей E16
+ ******************************************************************************/
+X502_EXPORT(int32_t) E16_OpenUsb(t_x502_hnd hnd, const char *serial);
+
+/***************************************************************************//**
+ @brief Открытие модуля E-502 по IP-адресу
+
+ Функция устанавливает связь с модулем E-502, подключенным по интерфейсу Ethernet,
+ для которого установлен указанный адрес IPv4.
+
+ После завершения работы с устройством соединение должно быть закрыто с
+ помощью X502_Close().
+
+ @param[in] hnd Описатель устройства.
+ @param[in] ip_addr IPv4 адрес модуля в виде 32-битного слова.
+ Для адреса "a.b.c.d" ip_addr = (a<<24)|(b<<16)|(c<<8)|d.
+ @param[in] flags Флаги, управляющие работой функции. Резерв, должны быть всегда 0.
+ @param[in] tout Время на установления подключения в мс. Если подключение
+ не удастся завершить за заданное время, то функция вернет ошибку.
+ @return Код ошибки.
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_OpenByIpAddr(t_x502_hnd hnd, uint32_t ip_addr,
+ uint32_t flags, uint32_t tout);
+
+/***************************************************************************//**
+ @brief Открытие модуля E-16 по IP-адресу
+
+ Функция устанавливает связь с модулем E-16, подключенным по интерфейсу Ethernet,
+ для которого установлен указанный адрес IPv4.
+
+ После завершения работы с устройством соединение должно быть закрыто с
+ помощью X502_Close().
+
+ @param[in] hnd Описатель устройства.
+ @param[in] ip_addr IPv4 адрес модуля в виде 32-битного слова.
+ Для адреса "a.b.c.d" ip_addr = (a<<24)|(b<<16)|(c<<8)|d.
+ @param[in] flags Флаги, управляющие работой функции. Резерв, должны быть всегда 0.
+ @param[in] tout Время на установления подключения в мс. Если подключение
+ не удастся завершить за заданное время, то функция вернет ошибку.
+ @return Код ошибки.
+*******************************************************************************/
+X502_EXPORT(int32_t) E16_OpenByIpAddr(t_x502_hnd hnd, uint32_t ip_addr,
+ uint32_t flags, uint32_t tout);
+
+
+/** @} */
+
+/***************************************************************************//**
+ @addtogroup func_devrec
+ @{
+*******************************************************************************/
+
+/***************************************************************************//**
+ @brief Получить список записей, соответствующих подключенным модулям E502
+
+ Функция находит все подключенные по интерфейсу USB модули E-502 и инициализирует
+ записи о каждом найденном устройстве и сохраняет их в переданный список
+ (если не нулевой).
+ Возвращенные в списке записи должны быть очищены после использования
+ с помощью X502_FreeDevRecordList() (также в случае повторного
+ вызов E502_UsbGetDevRecordsList() с тем же массивом записей, записи, полученные
+ при предыдущем вызове, должны быть сперва очищены).
+
+ @param[out] list Массив для сохранения записей о найденных устройствах.
+ Должен содержать место для сохранения не менее size записей.
+ Может быть NULL, если size=0, а devcnt!=NULL, в случае,
+ если нужно только получить количество модулей в системе.
+ @param[in] size Определяет, сколько максимально записей может
+ быть сохранено в массив list. Будут сохранены только
+ первые size записей, если устройств найдено больше.
+ @param[in] flags Флаги из #t_x502_getdevs_flags, определяющие поведение
+ функции.
+ @param[out] devcnt Если не нулевой указатель, то в данную переменную сохраняется
+ общее число найденных модулей E-502, подключенных по
+ интерфейсу USB (может быть больше size).
+ @return Если <0 --- код ошибки, иначе количество сохраненных
+ записей о найденных устройствах (всегда <= size).
+ Именно на этот размер нужно сделать в дальнейшем
+ X502_FreeDevRecordList() для освобождения памяти,
+ выделенной под информацию, на которую ссылается запись.
+ ******************************************************************************/
+X502_EXPORT(int32_t) E502_UsbGetDevRecordsList(t_x502_devrec* list, uint32_t size,
+ uint32_t flags, uint32_t* devcnt) ;
+
+
+/***************************************************************************//**
+ @brief Аналогично E502_UsbGetDevRecordsList, получить список записей, соответствующих подключенным модулям E16
+ ******************************************************************************/
+X502_EXPORT(int32_t) E16_UsbGetDevRecordsList(t_x502_devrec* list, uint32_t size,
+ uint32_t flags, uint32_t* devcnt) ;
+
+/***************************************************************************//**
+ @brief Аналогично E502_UsbGetDevRecordsList, получить список записей, соответствующих подключенным модулям E14-440
+ ******************************************************************************/
+X502_EXPORT(int32_t) E440_UsbGetDevRecordsList(t_x502_devrec* list, uint32_t size,
+ uint32_t flags, uint32_t* devcnt) ;
+
+
+/***************************************************************************//**
+ @brief Получить список записей, соответствующих подключенным модулям E502
+
+ Делает тоже самое что и E502_UsbGetDevRecordsList, отличие в том что можно указать idVendor и idProduct USB устройства
+
+@param[in] idVendor idVendor
+@param[in] idProduct idProduct
+ @return Если <0 --- код ошибки, иначе количество сохраненных
+ записей о найденных устройствах (всегда <= size).
+ Именно на этот размер нужно сделать в дальнейшем
+ X502_FreeDevRecordList() для освобождения памяти,
+ выделенной под информацию, на которую ссылается запись.
+ ******************************************************************************/
+X502_EXPORT(int32_t) E502_UsbGetDevRecordsList2(t_x502_devrec *list, uint32_t size,
+ uint32_t flags, uint32_t *devcnt, uint16_t idVendor, uint16_t idProduct);
+
+/***************************************************************************//**
+ @brief Создание записи о устройстве с указанным IP-адресом
+
+ Данная функция инициализирует запись о устройстве, подключенном по интерфейсу
+ Ethernet, с указанным IPv4 адресом. Данная функция только создает запись, но
+ не проверяет наличие соответствующего устройства. Подключение к модулю
+ выполняется аналогично другим записям через X502_OpenByDevRecord().
+
+ @param[out] devrec Указатель на запись устройства, которая должна быть
+ создана и заполнена нужными параметрами.
+ @param[in] ip_addr IPv4 адрес модуля в виде 32-битного слова (аналогично
+ параметру ip_addr функции E502_OpenByIpAddr()).
+ @param[in] flags Флаги. Резерв, должны быть всегда 0.
+ @param[in] tout Время для установления подключения в мс.
+ Данное время сохраняется в записи и используется при
+ последующем вызове X502_OpenByDevRecord(). Если подключение
+ не удастся завершить за это время, то функция
+ X502_OpenByDevRecord() вернет ошибку.
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_MakeDevRecordByIpAddr(t_x502_devrec *devrec, uint32_t ip_addr,
+ uint32_t flags, uint32_t tout);
+
+/***************************************************************************//**
+ @brief Создание записи о устройстве с указанным IP-адресом
+
+ Работает аналогично функции E502_MakeDevRecordByIpAddr.
+ Для поддержки E16 добавлен параметр *devname.
+
+ @param[out] devrec Указатель на запись устройства, которая должна быть
+ создана и заполнена нужными параметрами.
+ @param[in] ip_addr IPv4 адрес модуля в виде 32-битного слова (аналогично
+ параметру ip_addr функции E502_OpenByIpAddr()).
+ @param[in] flags Флаги. Резерв, должны быть всегда 0.
+ @param[in] tout Время для установления подключения в мс.
+ Данное время сохраняется в записи и используется при
+ последующем вызове X502_OpenByDevRecord(). Если подключение
+ не удастся завершить за это время, то функция
+ X502_OpenByDevRecord() вернет ошибку.
+ @param[in] devname Имя устройства "E16" или "E502".
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_MakeDevRecordByIpAddr2(t_x502_devrec *devrec, uint32_t ip_addr,
+ uint32_t flags, uint32_t tout, char const *devname);
+
+
+/***************************************************************************//**
+ @brief Установка TCP-порта управляющего соединения для записи о устройстве
+
+ Данная функция позволяет изменить TCP-порт управляющего соединения модуля
+ E-502. Это может быть необходимо, если модуль E-502 и хост, с которого
+ необходимо установить соединение, находятся в разных сетях и адрес модуля
+ E-502 не доступен из сети хоста. В этом случае требуется настройка проброса
+ портов на маршрутизаторе и при наличие более одного такого модуля E-502, т.к
+ все соединения идут с маршрутизитором, то различить эти модули можно только
+ по TCP-порту, если настроить разные порты при пробросе.
+ В этом случае помимо порта управляющего соединения, необходимо изменить и
+ порт соединения для передачи данных, вызвав E502_EthDevRecordSetDataPort().
+
+ Данная функция должна быть вызвана для записи, созданной до этого с помощью
+ E502_MakeDevRecordByIpAddr() и до открытия соединения с помощью
+ X502_OpenByDevRecord().
+
+ @param[in] devrec Указатель на запись устройства, в которой нужно изменить
+ управляющий TCP-порт.
+ @param[in] cmd_port Новое значение TCP-порта для управляющего соединения
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthDevRecordSetCmdPort(t_x502_devrec *devrec, uint16_t cmd_port);
+
+/***************************************************************************//**
+ @brief Установка TCP-порта соединения передачи данных для записи о устройстве
+
+ Функция аналогична E502_EthDevRecordSetCmdPort(), но изменяет TCP-порт для
+ соединения, по которому идет обмен данных потоков ввода-вывода.
+
+ @param[in] devrec Указатель на запись устройства, в которой нужно изменить
+ управляющий TCP-порт.
+ @param[in] data_port Новое значение TCP-порта для соединения передачи данных
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthDevRecordSetDataPort(t_x502_devrec *devrec, uint16_t data_port);
+
+
+
+/***************************************************************************//**
+ @brief Создание записи о устройстве по описателю сетевого сервиса
+
+ Данная функция инициализирует запись о устройстве, подключенном по интерфейсу
+ Ethernet, соответствующему сетевому сервису, на который указывает переданный
+ описатель сетевого сервиса. Этот описатель может быть получен с помощью
+ функций поиска сетевых сервисов, соответствующих модулям E-502, в локальной сети.
+ Данная функция только создает запись, но не проверяет наличие соответствующего
+ устройства. Подключение к модулю выполняется аналогично другим
+ записям через X502_OpenByDevRecord().
+ Вся необходимая информация из описателя сетевого сервиса сохраняется в записи
+ о устройстве, т.е. после вызова данной фунции при желании описатель
+ сетевого сервиса можно сразу освобождать с помощью E502_EthSvcRecordFree().
+
+ @param[out] devrec Указатель на запись устройства, которая должна быть
+ создана и заполнена нужными параметрами.
+ @param[in] svc Описатель сетевого сервиса, полученный с помощью
+ E502_EthSvcBrowseGetEvent().
+ @param[in] flags Флаги. Резерв, должны быть всегда 0.
+ @param[in] tout Время для установления подключения в мс.
+ Данное время сохраняется в записи и используется при
+ последующем вызове X502_OpenByDevRecord(). Если подключение
+ не удастся завершить за это время, то функция
+ X502_OpenByDevRecord() вернет ошибку.
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_MakeDevRecordByEthSvc(t_x502_devrec *devrec, t_e502_eth_svc_record_hnd svc,
+ uint32_t flags, uint32_t tout);
+
+/** @} */
+
+
+/***************************************************************************//**
+ @addtogroup func_eth_config Функции для настройки сетевых параметров модуля E502
+ @{
+*******************************************************************************/
+
+
+
+
+/***************************************************************************//**
+ @brief Получение текущего IP-адреса устройства
+
+ Функция возвращает IP-адрес устройства по которому было установлено соединение.
+ То есть связь с устройством должна быть уже установлена и кроме того,
+ установлена именно по интерфейсу Ethernet.
+ @param[in] hnd Описатель устройства
+ @param[out] ip_addr Текущий IPv4 адрес модуля в виде 32-битного слова (аналогично
+ параметру ip_addr функции E502_OpenByIpAddr()).
+
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_GetIpAddr(t_x502_hnd hnd, uint32_t *ip_addr);
+
+
+
+/***************************************************************************//**
+ @brief Создание описателя конфигурации сетевого интерфейса
+
+ Создание описателя конфигурации сетевого интерфейса.
+ В случае успешного выделения памяти инициализирует поля описателя
+ значениями по-умолчанию.
+ @return NULL в случае ошибки, иначе - описатель модуля
+*******************************************************************************/
+X502_EXPORT(t_e502_eth_config_hnd) E502_EthConfigCreate(void);
+
+/***************************************************************************//**
+ @brief Освобождение описателя конфигурации сетевого интерфейса.
+
+ Освобождение памяти, выделенной под описатель конфигурации сетевого интерфейса
+ с помощью E502_EthConfigCreate().
+ После этого описатель уже использовать нельзя, независимо от возвращенного
+ значения!
+ @param[in] cfg Описатель конфигурации сетевого интерфейса
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigFree(t_e502_eth_config_hnd cfg);
+
+
+
+/***************************************************************************//**
+ @brief Чтение текущей сетевой конфигурации интерфейса
+
+ Функция читает текущие параметры интерфейса и сохраняет их в структуру,
+ на которую указывает созданный с помощью E502_EthConfigCreate() описатель
+ конфигурации сетевого интерфейса.
+
+ Соединение с устройством при этом должно быть установлено, но может быть
+ установлено по любому поддерживаемому интерфейсу.
+
+ @param[in] hnd Описатель устройства из которого нужно считать конфигурацию
+ @param[in,out] cfg Описатель конфигурации сетевого интерфейса
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigRead(t_x502_hnd hnd, t_e502_eth_config_hnd cfg);
+
+
+/***************************************************************************//**
+ @brief Запись сетевой конфигурации интерфейса
+
+ Функция передает модулю конфигурацию сетевого интерфейса, которую модуль
+ должен сохранить в своей энергонезависимой памяти.
+
+ При успешном выполнении данной функции модуль отключает Ethernet-интерфейс,
+ настраивает его на новые параметры и снова его инициализирует, поэтому
+ если соединение с устройством установлено по сети, то дальнейшая работа
+ с устройством будет уже не возможна --- необходимо закрыть связь с устройством
+ и установить ее заново.
+
+ Для изменения конфигурации необходимо передать пароль для конфигурации
+ (пустая строка, если пароль не был установлен). При работе по USB интерфейсу
+ в качестве пароля можно передать текущий серийный номер устройства
+ (для случая, если забыт установленный пароль).
+
+ @param[in] hnd Описатель устройства из которого нужно считать конфигурацию
+ @param[in] cfg Описатель конфигурации сетевого интерфейса
+ @param[in] passwd Строка с паролем для изменения конфигурации
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigWrite(t_x502_hnd hnd, t_e502_eth_config_hnd cfg,
+ const char *passwd);
+
+
+/***************************************************************************//**
+ @brief Копирование содержимого сетевой конфигурации интерфейса
+
+ Функция выполняет копирование всех параметров одной созданной конфигурации
+ в другую конфирурацию, создавая полную копию.
+
+ @param[in] src_cfg Описатель исходной сетевой конфигурации интерфейса,
+ содержимое которой нужно скопировать.
+ @param[out] dst_cfg Описатель сетевой конфигурации интерфейса, в которую
+ нужно скопировать содержимое исходной конфигурации
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigCopy(t_e502_eth_config_hnd src_cfg,
+ t_e502_eth_config_hnd dst_cfg);
+
+/***************************************************************************//**
+ @brief Определение, разрешен ли интерфейс Ethernet
+
+ Функция возвращает, разрешен ли интерфейс Ethernet в указанной конфигурации.
+ Если интерфейс не разрешен, то Ethernet контроллер полностью отключен.
+ @param[in] cfg Описатель конфигурации сетевого интерфейса
+ @param[out] en Если интерфейс разрешен, то в данной переменной возвращается 1,
+ иначе --- 0
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigGetEnabled(t_e502_eth_config_hnd cfg, uint32_t *en);
+
+/***************************************************************************//**
+ @brief Разрешение интерфейса Ethernet
+
+ Функция устанавливает, разрешена ли работа по интерфейсу Ethernet.
+ Если интерфейс не разрешен, то Ethernet контроллер полностью отключен.
+ @param[in] cfg Описатель конфигурации сетевого интерфейса
+ @param[in] en 0 означает запрет интерфейса Ethernet, 1 --- разрешение
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigSetEnabled(t_e502_eth_config_hnd cfg, uint32_t en);
+/***************************************************************************//**
+ @brief Определение, разрешено ли автоматическое получение параметров IP
+
+ Функция возвращает, разрешено ли автоматическое получение параметров IP
+ (IP-адрес, маска подсети, адрес шлюза) с использованием DHCP/linklocal или
+ используются статически заданные параметры.
+ @param[in] cfg Описатель конфигурации сетевого интерфейса
+ @param[out] en Если разрешено автоматическое получение параметров, то
+ возвращается 1, иначе --- 0
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigGetAutoIPEnabled(t_e502_eth_config_hnd cfg, uint32_t *en);
+/***************************************************************************//**
+ @brief Разрешение автоматического получения параметров IP
+
+ Функция устанавливает, разрешено ли автоматическое получение параметров IP
+ (IP-адрес, маска подсети, адрес шлюза) с использованием DHCP/linklocal или
+ используются статически заданные параметры.
+ @param[in] cfg Описатель конфигурации сетевого интерфейса
+ @param[in] en Если разрешено автоматическое получение параметров, то
+ возвращается 1, иначе --- 0
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigSetAutoIPEnabled(t_e502_eth_config_hnd cfg, uint32_t en);
+/***************************************************************************//**
+ @brief Получить состояние автоматического получения параметров IP
+
+ Функция возвращает, получил ли модуль параметры IP
+ (IP-адрес, маска подсети, адрес шлюза) с использованием DHCP/linklocal
+ @param[in] cfg Описатель конфигурации сетевого интерфейса
+ @param[out] state Состояние автоматического получения параметров IP
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigGetAutoIPState(t_e502_eth_config_hnd cfg, uint32_t *state);
+
+/***************************************************************************//**
+ @brief Определение, разрешен ли пользовательский MAC-адрес
+
+ Функция возвращает, разрешен ли MAC-адрес, заданный пользователем, или
+ используется заводской MAC-адрес.
+
+ @param[in] cfg Описатель конфигурации сетевого интерфейса.
+ @param[out] en Если разрешен пользовательский MAC-адрес, то
+ возвращается 1, иначе (если используется заводской) --- 0
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigGetUserMACEnabled(t_e502_eth_config_hnd cfg, uint32_t *en);
+/***************************************************************************//**
+ @brief Определение, разрешен ли пользовательский MAC-адрес
+
+ Функция возвращает, разрешен ли MAC-адрес, заданный пользователем, или
+ используется заводской MAC-адрес.
+
+ @param[in] cfg Описатель конфигурации сетевого интерфейса.
+ @param[in] en Если разрешен пользовательский MAC-адрес, то
+ возвращается 1, иначе (если используется заводской) --- 0
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigSetUserMACEnabled(t_e502_eth_config_hnd cfg, uint32_t en);
+
+/***************************************************************************//**
+ @brief Получение установленного статического IP-адреса
+
+ Функция возвращает заданный в конфигурации статический IP-адрес, который
+ используется устройством, если запрещено автоматическое получение IP-параметров.
+
+ @param[in] cfg Описатель конфигурации сетевого интерфейса.
+ @param[out] ip_addr Заданный IP-адрес в виде 32-битного слова (аналогично
+ параметру ip_addr функции E502_OpenByIpAddr()).
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigGetIPv4Addr(t_e502_eth_config_hnd cfg, uint32_t *ip_addr);
+/***************************************************************************//**
+ @brief Установка статического IP-адреса
+
+ Функция устанавливает в конфигурации заданный статический IP-адрес, который
+ будет использоваться устройством, если запрещено автоматическое получение IP-параметров.
+
+ @param[in] cfg Описатель конфигурации сетевого интерфейса.
+ @param[in] ip_addr Устанавливаемый IP-адрес в виде 32-битного слова (аналогично
+ параметру ip_addr функции E502_OpenByIpAddr()).
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigSetIPv4Addr(t_e502_eth_config_hnd cfg, uint32_t ip_addr);
+
+/***************************************************************************//**
+ @brief Получение установленной статической маски подсети
+
+ Функция возвращает заданное в конфигурации значение маски подсети, которая
+ используется устройством, если запрещено автоматическое получение IP-параметров.
+
+ @param[in] cfg Описатель конфигурации сетевого интерфейса.
+ @param[out] mask Маска подсети в виде 32-битного слова (аналогично
+ параметру ip_addr функции E502_OpenByIpAddr()).
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigGetIPv4Mask(t_e502_eth_config_hnd cfg, uint32_t *mask);
+/***************************************************************************//**
+ @brief Установка статической маски подсети
+
+ Функция устанавливает в конфигурации значение маски подсети, которая будет
+ использоваться устройством, если запрещено автоматическое получение IP-параметров.
+
+ @param[in] cfg Описатель конфигурации сетевого интерфейса.
+ @param[in] mask Устанавливаемое значение маски подсети в виде 32-битного слова
+ (аналогично параметру ip_addr функции E502_OpenByIpAddr()).
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigSetIPv4Mask(t_e502_eth_config_hnd cfg, uint32_t mask);
+
+/***************************************************************************//**
+ @brief Получение установленного статического адреса шлюза
+
+ Функция возвращает заданное в конфигурации значение адреса шлюза, который
+ используется устройством, если запрещено автоматическое получение IP-параметров.
+
+ @param[in] cfg Описатель конфигурации сетевого интерфейса.
+ @param[out] gate Адрес шлюза в виде 32-битного слова (аналогично
+ параметру ip_addr функции E502_OpenByIpAddr()).
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigGetIPv4Gate(t_e502_eth_config_hnd cfg, uint32_t *gate);
+/***************************************************************************//**
+ @brief Установка статического адреса шлюза
+
+ Функция устанавливает в конфигурации значение адреса шлюза, который
+ будет использоваться устройством, если запрещено автоматическое получение IP-параметров.
+
+ @param[in] cfg Описатель конфигурации сетевого интерфейса.
+ @param[in] gate Устанавливаемое значение адреса шлюза в виде 32-битного слова
+ (аналогично параметру ip_addr функции E502_OpenByIpAddr()).
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigSetIPv4Gate(t_e502_eth_config_hnd cfg, uint32_t gate);
+
+/***************************************************************************//**
+ @brief Получение установленного пользовательского MAC-адреса
+
+ Функция возвращает заданное в конфигурации значение пользовательского MAC-адреса,
+ который используется устройством при явном его разрешении (см.
+ E502_EthConfigSetUserMACEnabled()).
+
+ @param[in] cfg Описатель конфигурации сетевого интерфейса.
+ @param[out] mac Пользовательский MAC-адрес устройства в виде массива из
+ #X502_MAC_ADDR_SIZE байт в порядке записи адреса
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigGetUserMac(t_e502_eth_config_hnd cfg, uint8_t *mac);
+/***************************************************************************//**
+ @brief Установка пользовательского MAC-адреса
+
+ Функция устанавливает в конфигурации значение пользовательского MAC-адреса,
+ который будет использоваться устройством при явном его разрешении (см.
+ E502_EthConfigSetUserMACEnabled()).
+
+ @param[in] cfg Описатель конфигурации сетевого интерфейса.
+ @param[in] mac Устанавливаемое значение пользовательского MAC-адрес устройства
+ в виде массива из #X502_MAC_ADDR_SIZE байт в порядке записи адреса
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigSetUserMac(t_e502_eth_config_hnd cfg, const uint8_t *mac);
+/***************************************************************************//**
+ @brief Получение заводского MAC-адреса устройства
+
+ Функция возвращает значение заводского MAC-адреса устройства, которому
+ соответствует переданная первым параметром конфигурация.
+ Заводской MAC-адрес, используемый устройством по-умолчанию, записывается
+ производителем (в "Л Кард") при производстве устройства вместе с его серийным
+ номером и не может быть изменен пользователем. Если пользователю нужно
+ изменить MAC-адрес устройства, то он должен задать пользовательский
+ MAC-адрес с помощью E502_EthConfigGetUserMac() и разрешить его использование
+ через E502_EthConfigSetUserMACEnabled(). При этом всегда есть возможность
+ снова вернуться к использованию оригинального заводского MAC-адреса.
+
+
+ @param[in] cfg Описатель конфигурации сетевого интерфейса.
+ @param[out] mac Заводской MAC-адрес устройства в виде массива из
+ #X502_MAC_ADDR_SIZE байт в порядке записи адреса
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigGetFactoryMac(t_e502_eth_config_hnd cfg, uint8_t *mac);
+
+
+/***************************************************************************//**
+ @brief Получение установленного имени экземпляра устройства
+
+ Функция возвращает заданное пользователем имя экземпляра устройства. Данное
+ имя может использоваться для обнаружения устройства в сети. Если не задано,
+ то используется имя, образованное названием устройства и его серийным номером.
+ Данное имя должно быть уникально в пределах сети.
+
+ @param[in] cfg Описатель конфигурации сетевого интерфейса.
+ @param[out] name Оканчивающаяся нулем строка с заданным именем экземпляра устройства в формате
+ UTF-8. Массив должен быть рассчитан на #X502_INSTANCE_NAME_SIZE
+ байт данных.
+ @return Код ошибки.
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigGetInstanceName(t_e502_eth_config_hnd cfg, char *name);
+/***************************************************************************//**
+ @brief Установка имени экземпляра устройства
+
+ Функция устанавливает имя экземпляра устройства, которое может использоваться
+ для обнаружения устройства локальной в сети.
+
+ @param[in] cfg Описатель конфигурации сетевого интерфейса.
+ @param[in] name Оканчивающаяся нулем строка с заданным именем экземпляра устройства в формате
+ UTF-8. Максимальный размер массива (включая завершающий ноль)
+ составляет #X502_INSTANCE_NAME_SIZE байт данных.
+ @return Код ошибки.
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigSetInstanceName(t_e502_eth_config_hnd cfg, const char *name);
+
+
+/***************************************************************************//**
+ @brief Установка нового пароля для смены конфигурации
+
+ Функция устанавливает новое значение пароля, которое должно будет использоваться
+ для смены конфигурации через E502_EthConfigWrite().
+
+ При этом значение при сохранении конфигурации с установленным новым паролем
+ необходимо для успешной смены конфигурации в E502_EthConfigWrite() передать
+ значение пароля, которое было установлено до этого. Если функция завершится
+ успешно, то для последующего изменения конфигурации в E502_EthConfigWrite()
+ нужно будет передавать уже новое установленное значение пароля.
+
+ @param[in] cfg Описатель конфигурации сетевого интерфейса.
+ @param[in] new_passwd Оканчивающаяся нулем строка, содержащая новое значение
+ пароля для смены конфигурации сетевого интерфейса.
+ Максимальный размер массива (включая завершающий ноль)
+ составляет #X502_PASSWORD_SIZE байт данных.
+ @return Код ошибки.
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthConfigSetNewPassword(t_e502_eth_config_hnd cfg, const char *new_passwd);
+
+
+/** @} */
+
+
+/***************************************************************************//**
+ @addtogroup func_misc
+ @{
+*******************************************************************************/
+
+
+/***************************************************************************//**
+ @brief Перевод модуля E-502 в режим загрузчика
+
+ Функция переводит устройство в режим загрузчика для возможности обновления
+ прошивки контроллера Cortex-M4 модуля E-502 с помощью утилиты lboot.
+
+ В зависимости от используемого текущего интерфейса для соединения с модулем,
+ модуль переводится в режим загрузки прошивки по интерфейсу USB (если соединение
+ было по USB) или по TFTP (если соединение было по интерфейсу Ethernet).
+
+ При успешном вызове данной функции дальнейшая работа с текущем соединением невозможна,
+ т.е. единственным допустимым следующим вызовом является X502_Close().
+
+ При переходе в загрузчик находится в режиме загрузчика порядка 30с после чего,
+ если не поступало запросов на перепрошивку загрузчик возвращает управление штатной прошивке.
+ Пока модуль находится в режиме загрузчика с ним невозможно установить соединение
+ с помощью функций данной библиотеки.
+
+
+ @param[in] hnd Описатель устройства.
+ @return Код ошибки.
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_SwitchToBootloader(t_x502_hnd hnd);
+
+/***************************************************************************//**
+ @brief Перезагрузка прошивки ПЛИС
+
+ По данной команде контроллер Cortex-M4 модуля E-502 выполняет сброс ПЛИС
+ и загрузку прошивки ПЛИС из внутренней Flash-памяти.
+
+ Это сервисная функция, которая используется главным образом для обновления
+ прошивки ПЛИС.
+
+ @param[in] hnd Описатель устройства.
+ @return Код ошибки.
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_ReloadFPGA(t_x502_hnd hnd);
+
+
+
+/***************************************************************************//**
+ @brief Передача управляющей команды контроллеру Cortex-M4.
+
+ Функция предназначена для передачи пользовательских управляющих команд
+ контроллеру для случая модифицированной прошивки Cortex-M4.
+
+
+ @param[in] hnd Описатель модуля.
+ @param[in] cmd_code Код команды - определяет, что за команда выполняется.
+ @param[in] par Параметр, передаваемый с командой (значение зависит
+ от кода команды).
+ @param[in] snd_data Опциональные данные, передаваемые вместе с командой.
+ Если данные не передаются, то должен передаваться
+ нулевой указатель и snd_size = 0.
+ @param[in] snd_size Количество байт, передаваемых в snd_data
+ @param[out] rcv_data Массив, в который будут переданы данные, возвращенные
+ процессором по завершению команды. Если данные не
+ должны возвращаться, то должен передаваться нулевой
+ указатель, а rcv_size = 0.
+ @param[in] rcv_size Количество байт, которое ожидается, что
+ вернет контроллер по выполнению команды. Массив
+ rcv_data должен быть рассчитан на данное количество
+ слов.
+ @param[in] tout Таймаут в течении которого будет ожидаться, когда
+ контроллер завершит выполнение команды. Функция
+ возвратит управление либо по завершению команды,
+ либо по таймауту.
+ @param[out] recvd_size Если не является нулевым указателем, то в эту
+ переменную будет сохранено количество байт,
+ которое реально вернул контроллер после выполнения
+ команды (контроллер имеет право вернуть меньше данных,
+ чем запрашивалось в rcv_size). Если указатель нулевой,
+ то возвращаение меньшего количества данных считается
+ ошибкой.
+ @return Код ошибки.
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_CortexExecCmd(t_x502_hnd hnd, uint32_t cmd_code, uint32_t par,
+ const uint8_t* snd_data, uint32_t snd_size,
+ uint8_t* rcv_data, uint32_t rcv_size,
+ uint32_t tout, uint32_t* recvd_size);
+
+
+/** @} */
+
+
+
+/***************************************************************************//**
+ @addtogroup func_eth_svc_browse Функции для поиска модулей в локальной сети
+ @{
+*******************************************************************************/
+
+/***************************************************************************//**
+ @brief Начало сеанса поиска модулей в локальной сети
+
+ При вызове данной функции запускается процесс поиска сервисов, соответствующих
+ модулям E-502, в локальной сети и создается контекст текущего сеанса поиска.
+ Этот контекст используется для дальнейших вызовов E502_EthSvcBrowseGetEvent().
+ После завершения поиска должна быть вызвана функция E502_EthSvcBrowseStop().
+ Для запуска сеанса необходима запущенная служба (демон) обнаружения ---
+ поддерживаются Bonjour для ОС Windows и Avahi для ОС Linux.
+ @param[out] pcontext Указатель, в который при успешном выполнении
+ сохраняется контекст сеанса поиска устройств.
+ @param[in] flags Флаги (резерв). Должен всегда передаваться 0.
+ @return Код ошибки.
+ ******************************************************************************/
+X502_EXPORT(int32_t) E502_EthSvcBrowseStart(t_e502_eth_svc_browse_hnd *pcontext,
+ uint32_t flags);
+
+/***************************************************************************//**
+ @brief Получение информации о изменении присутствия модулей в локальной сети
+
+ Данная функция позволяет как получить список присутствующих модулей
+ (сетевых сервисов) в локальной сети, так и отслеживать
+ в дальнейшем изменение их состояния.
+
+ Функция ждет первого изменения состояния и возвращает информацию о нем.
+ Информация состоит из события (появление сетевого сервиса, исчезновение,
+ изменение параметров) и из описателя сетевого сервиса, которому соответствует событие.
+
+ После начала поиска с помощью E502_EthSvcBrowseStart() контекст не содержит
+ информации о наличие сетевых сервисов. Если уже есть подключенные в локальной сети
+ модули E-502, то информация о них будет возвращена в следующих
+ E502_EthSvcBrowseGetEvent() с событием #E502_ETH_SVC_EVENT_ADD, за
+ каждый вызов по одному устройству.
+
+ Если за заданный таймаут не произошло ни одного изменения, то функция
+ завершится успешно по окончанию таймаута и вернет событие #E502_ETH_SVC_EVENT_NONE.
+
+ При желании можно продолжать вызвать данную функцию для непрерывного отслеживания
+ состояния подключения модулей.
+
+ @param[in] context Описатель контекста поиска, созданный при вызове
+ E502_EthSvcBrowseStart().
+ @param[out] svc Если возвращенное событие не равно #E502_ETH_SVC_EVENT_NONE,
+ то в данной переменной сохраняется созданный описатель
+ сетевого сервиса, соответствующего указанному событию. Этот
+ описатель должен быть всегда уничтожен вручную
+ с помощью E502_EthSvcRecordFree().
+ @param[out] event В данную переменную сохраняется код события (один из
+ #t_e502_eth_svc_event). Если за указанное время не
+ произошло ни одного события, то возвращается код
+ #E502_ETH_SVC_EVENT_NONE.
+ @param[out] flags В данной переменной сохраняются дополнительные коды
+ флагов (резерв). Может быть передан нулевой указатель,
+ если значение флагов не интересует.
+ @param[in] tout Таймаут (в мс) на время ожидания события
+ @return Код ошибки.
+ ******************************************************************************/
+X502_EXPORT(int32_t) E502_EthSvcBrowseGetEvent(t_e502_eth_svc_browse_hnd context,
+ t_e502_eth_svc_record_hnd *svc ,
+ uint32_t *event, uint32_t *flags,
+ uint32_t tout);
+
+/***************************************************************************//**
+ @brief Останов сеанса поиска модулей в локальной сети
+
+ При вызове данной функции процесс поиска сетевых сервисов, соответствующий
+ указанному контексту, останавливается. Все ресурсы, выделенные на этапе
+ E502_EthSvcBrowseStart() освобождаются. Контекст с этого момента становится
+ недействительным.
+ Вызову E502_EthSvcBrowseStart() всегда должен соответствовать последующий
+ вызов E502_EthSvcBrowseStop() для корректного освобождения ресурсов.
+
+ @param[in] context Описатель контекста поиска, созданный при вызове
+ E502_EthSvcBrowseStart().
+ @return Код ошибки.
+ ******************************************************************************/
+X502_EXPORT(int32_t) E502_EthSvcBrowseStop(t_e502_eth_svc_browse_hnd context);
+
+
+
+/***************************************************************************//**
+ @brief Освобождение описателя сетевого сервиса
+
+ Освобождение памяти, выделенной под описатель сетевого сервиса при вызове
+ E502_EthSvcBrowseGetEvent().
+
+ @param[in] svc Описатель сетевого сервиса
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthSvcRecordFree(t_e502_eth_svc_record_hnd svc);
+
+/***************************************************************************//**
+ @brief Получить имя экземпляра по описателю сервиса
+
+ Функция возвращает имя экземпляра сервиса. Это имя соответствует имени,
+ которое установлено в сетевых настройках модуля, соответствующего указанному
+ сервису, с помощью E502_EthConfigSetInstanceName().
+ Следует отметить, что данное имя, в отличие от остальных строк, представлено
+ в кодировке UTF-8, которая совпадает с обычной ASCII строкой только для
+ символов английского алфавита.
+ Функция не выполняет запросов к самому модулю.
+
+ @param[in] svc Описатель сетевого сервиса
+ @param[out] name Массив на #X502_INSTANCE_NAME_SIZE байт, в который будет
+ сохранено название экземпляра
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthSvcRecordGetInstanceName(t_e502_eth_svc_record_hnd svc, char *name);
+
+/***************************************************************************//**
+ @brief Получить серийный номер модуля по описателю сетевого сервиса
+
+ Функция возвращает серийный номер модуля E-502, соответствующего сетевому
+ сервису, на который указывает переданный описатель.
+ Функция не выполняет запросов к самому модулю.
+
+ @param[in] svc Описатель сетевого сервиса
+ @param[out] serial Массив на #X502_SERIAL_SIZE байт, в который будет
+ сохранен серийный номер
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthSvcRecordGetDevSerial(t_e502_eth_svc_record_hnd svc, char *serial);
+
+/***************************************************************************//**
+ @brief Получить имя усройства модуля по описателю сетевого сервиса
+
+ Функция возвращает имя устройства модуля ("E502" или "E16"), соответствующего сетевому
+ сервису, на который указывает переданный описатель.
+ Функция не выполняет запросов к самому модулю.
+
+ @param[in] svc Описатель сетевого сервиса
+ @param[out] devname Массив на #X502_DEVNAME_SIZE байт, в который будет
+ сохранено имя устройства
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthSvcRecordGetDevName(t_e502_eth_svc_record_hnd rec, char *devname);
+
+
+/***************************************************************************//**
+ @brief Получить IP адрес сетевого сервиса
+
+ Функция получает IP-адрес модуля E-502, соответствующего сетевому
+ сервису, на который указывает переданный описатель. Функция при необходимости
+ может выполнять запросы к самому модулю для получения этого адреса, если
+ информации о адресе нет в кеше.
+
+ @param[in] svc Описатель сетевого сервиса
+ @param[out] addr IP-адрес модуля в виде 32-битного слова (аналогично
+ параметру ip_addr функции E502_OpenByIpAddr())
+ @param[in] tout Время ожидания ответа от модуля в случае необходимости
+ выполнить запрос для установления адреса.
+ @return Код ошибки
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthSvcRecordResolveIPv4Addr(t_e502_eth_svc_record_hnd svc,
+ uint32_t *addr, uint32_t tout);
+
+
+/***************************************************************************//**
+ @brief Проверка, указывают ли оба описателя на один экземпляр сервиса
+
+ Функция проверяет, указывают ли оба описателя сервисов на один и тот же
+ экземпляр. Если приложение сохраняет список описателей сервисов при их
+ обнаружении, то данная функция может использоваться, например, при событиях
+ #E502_ETH_SVC_EVENT_REMOVE или #E502_ETH_SVC_EVENT_CHANGED, чтобы понять,
+ какой записи в сохраненном списке соответствует событие (т.е. функция
+ E502_EthSvcBrowseGetEvent() вернет новый описатель, но указывающий на тот
+ же экземпляр, что и при событии #E502_ETH_SVC_EVENT_ADD)
+
+ @param[in] svc1 Первый описатель сетевого сервиса для сравнения
+ @param[in] svc2 Второй описатель сетевого сервиса для сравнения
+ @return Код ошибки. Возвращает #X502_ERR_OK, если оба описателя
+ указывают на один экземпляр.
+*******************************************************************************/
+X502_EXPORT(int32_t) E502_EthSvcRecordIsSameInstance(t_e502_eth_svc_record_hnd svc1,
+ t_e502_eth_svc_record_hnd svc2);
+
+
+/***************************************************************************//**
+ @brief Получить список записей, присутствующих в локальной сети
+
+ Функция является оберткой E502_EthSvcBrowse...()
+ Функция находит все подключенные по интерфейсу Ethernet модули и инициализирует
+ записи о каждом найденном устройстве и сохраняет их в переданный список
+ (если не нулевой).
+ Возвращенные в списке записи должны быть очищены после использования
+ с помощью X502_FreeDevRecordList().
+
+ @param[out] list Массив для сохранения записей о найденных устройствах.
+ Должен содержать место для сохранения не менее size записей.
+ Может быть NULL, если size=0, а devcnt!=NULL, в случае,
+ если нужно только получить количество модулей в системе.
+ @param[in] size Определяет, сколько максимально записей может
+ быть сохранено в массив list. Будут сохранены только
+ первые size записей, если устройств найдено больше.
+ @param[in] flags Флаги из #t_x502_getdevs_flags, определяющие поведение
+ функции.
+ @param[out] devcnt Если не нулевой указатель, то в данную переменную сохраняется
+ общее число найденных модулей, подключенных по Ethernet (может быть больше size).
+ @param[in] event_tout Время на время ожидания события в мс функции E502_EthSvcBrowseGetEvent()
+ @param[in] tcp_tout Время для установления подключения в мс функции E502_MakeDevRecordByEthSvc()
+ @return Если <0 --- код ошибки, иначе количество сохраненных
+ записей о найденных устройствах (всегда <= size).
+ Именно на этот размер нужно сделать в дальнейшем
+ X502_FreeDevRecordList() для освобождения памяти,
+ выделенной под информацию, на которую ссылается запись.
+ ******************************************************************************/
+X502_EXPORT(int32_t) E502_SearchEthForDevicesIPv4Addr(t_x502_devrec* rec_list, uint32_t flags, uint32_t size,
+ uint32_t *devcount, uint32_t event_tout, uint32_t tcp_tout);
+
+X502_EXPORT(int32_t) E502_EthConfigSetTcpCmdPort(t_e502_eth_config_hnd cfg, uint16_t port);
+
+X502_EXPORT(int32_t) E502_EthConfigSetTcpDataPort(t_e502_eth_config_hnd cfg, uint16_t port);
+
+X502_EXPORT(int32_t) E502_EthConfigGetTcpCmdPort(t_e502_eth_config_hnd cfg, uint16_t *port);
+
+X502_EXPORT(int32_t) E502_EthConfigGetTcpDataPort(t_e502_eth_config_hnd cfg, uint16_t *port);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // E502API_H
diff --git a/e502/e502api.rc.in b/e502/e502api.rc.in
new file mode 100644
index 0000000..b246665
--- /dev/null
+++ b/e502/e502api.rc.in
@@ -0,0 +1,48 @@
+#include
+
+#define LIB_VERSION @X502API_VER_MAJOR@,@X502API_VER_MINOR@,@X502API_VER_PATCH@,0
+#define VER_DEBUG VS_FF_DEBUG
+
+
+1 VERSIONINFO
+ FILEVERSION LIB_VERSION
+ PRODUCTVERSION LIB_VERSION
+#ifndef NDEBUG
+ FILEFLAGS 0
+#else
+ FILEFLAGS VER_DEBUG
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+ BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "04090000"
+ BEGIN
+ VALUE "CompanyName", "L-Card"
+ VALUE "FileDescription", "Library for E502"
+ VALUE "FileVersion", "@X502API_VERSION@"
+ VALUE "OriginalFilename", "e502api.dll"
+ VALUE "ProductName", "e502api"
+ VALUE "ProductVersion", "@X502API_VERSION@"
+ VALUE "LegalCopyright", "© 2015 L-Card Ltd."
+ END
+
+ BLOCK "04190000"
+ BEGIN
+ VALUE "CompanyName", "Л Кард"
+ VALUE "FileDescription", "Библиотека для работы с модулем E502"
+ VALUE "FileVersion", "@X502API_VERSION@"
+ VALUE "OriginalFilename", "e502api.dll"
+ VALUE "ProductName", "e502api"
+ VALUE "ProductVersion", "@X502API_VERSION@"
+ VALUE "LegalCopyright", "© 2015 ООО 'Л Кард'"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ VALUE "Translation", 0x419, 1251
+ END
+ END
diff --git a/e502/e502api_dnssd.c b/e502/e502api_dnssd.c
new file mode 100644
index 0000000..8c1e840
--- /dev/null
+++ b/e502/e502api_dnssd.c
@@ -0,0 +1,1059 @@
+#ifdef ENABLE_DNSSD
+#ifdef ENABLE_BONJOUR
+ #include "dns_sd.h"
+
+
+ typedef uint32_t t_svc_iface_idx;
+ typedef struct {
+ uint16_t txtLen;
+ const unsigned char *txtStr;
+ } t_svc_txt_record;
+#elif defined ENABLE_AVAHI
+ #include
+ #include
+ #include
+
+ #include
+ #include
+ #include
+
+ typedef AvahiIfIndex t_svc_iface_idx;
+ typedef AvahiStringList* t_svc_txt_record;
+#else
+ #error Invalid DNSSD backend
+#endif
+#include "ltimer.h"
+#include "e502api_tcp_private.h"
+
+
+
+#include
+
+#define E502_SVC_REC_SIGN 0xE502CCA5
+#define E502_SVC_BROWSE_SIGN 0xE502BBA5
+
+#define SERVICE_CONTEXT_MAX_CNT 1024
+
+
+#define E502_TCP_SERVICE_NAME "_lcard_acqdev._tcp"
+
+
+#define E502_CHECK_SVC_REC(svc) ((svc != NULL) ? (svc)->sign == E502_SVC_REC_SIGN ? X502_ERR_OK \
+ : X502_ERR_INVALID_SVC_RECORD_HANDLE : X502_ERR_INVALID_SVC_RECORD_HANDLE)
+
+#define E502_CHECK_BROWSE_CONTEXT(svc) ((svc != NULL) ? (svc)->sign == E502_SVC_BROWSE_SIGN ? X502_ERR_OK \
+ : X502_ERR_INVALID_SVC_BROWSE_HANDLE : X502_ERR_INVALID_SVC_BROWSE_HANDLE)
+
+typedef struct st_e502_eth_svc_record {
+ uint32_t sign;
+ char devname[X502_DEVNAME_SIZE];
+ char serial[X502_SERIAL_SIZE];
+ char service_name[X502_INSTANCE_NAME_SIZE];
+ char *domain;
+ char *hosttarget;
+ uint16_t port;
+ t_svc_iface_idx iface_idx;
+ uint32_t ipv4_addr;
+ int op_in_progr;
+ int32_t op_err;
+} t_svc_record;
+
+
+
+typedef struct {
+#if defined ENABLE_BONJOUR
+ DNSServiceRef sdref;
+ DNSServiceRef sdref_addr;
+#elif defined ENABLE_AVAHI
+ AvahiServiceResolver *resolver;
+#endif
+ t_e502_eth_svc_event event;
+ int init_done;
+ int fail;
+ t_e502_eth_svc_record_hnd rec;
+} t_service_context;
+
+typedef struct st_e502_eth_svc_browse_context {
+ uint32_t sign;
+#if defined ENABLE_BONJOUR
+ DNSServiceRef main_ref;
+ DNSServiceRef browse_ref;
+ int socket;
+#elif defined ENABLE_AVAHI
+ AvahiSimplePoll *poll;
+ AvahiClient *client;
+ AvahiServiceBrowser *sb;
+#endif
+ int32_t err;
+ unsigned svc_cnt;
+ t_service_context services[SERVICE_CONTEXT_MAX_CNT];
+} t_e502_service_browse_context;
+
+
+
+#if defined ENABLE_BONJOUR
+ void DNSSD_API f_cb_resolve (DNSServiceRef sdRef, DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *fullname, const char *hosttarget,
+ uint16_t port, /* In network byte order */
+ uint16_t txtLen,
+ const unsigned char *txtRecord,
+ void *context);
+
+#elif defined ENABLE_AVAHI
+
+ static void f_resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface,
+ AvahiProtocol protocol, AvahiResolverEvent event,
+ const char *name, const char *type,
+ const char *domain, const char *host_name,
+ const AvahiAddress *address, uint16_t port,
+ AvahiStringList *txt, AvahiLookupResultFlags flags,
+ void* userdata);
+#endif
+
+
+
+static LINLINE void f_set_timeval_left(t_ltimer *tmr, struct timeval *tval) {
+ t_lclock_ticks left = ltimer_expiration(tmr);
+ tval->tv_sec = left / LCLOCK_TICKS_PER_SECOND;
+ tval->tv_usec = (left % LCLOCK_TICKS_PER_SECOND) * 1000000/LCLOCK_TICKS_PER_SECOND;
+}
+
+
+
+static t_e502_eth_svc_record_hnd f_service_record_create(void) {
+ t_e502_eth_svc_record_hnd svc = calloc(1, sizeof(t_svc_record));
+ if (svc != NULL) {
+ svc->sign = E502_SVC_REC_SIGN;
+ }
+ return svc;
+}
+
+static t_e502_eth_svc_record_hnd f_service_record_create_copy(t_e502_eth_svc_record_hnd rec) {
+ t_e502_eth_svc_record_hnd ret = NULL;
+ if (E502_CHECK_SVC_REC(rec) == X502_ERR_OK) {
+ ret = f_service_record_create();
+ memcpy(ret, rec, sizeof(t_svc_record));
+ if (rec->domain != NULL) {
+ size_t len = strlen(rec->domain) + 1;
+ ret->domain = malloc(len);
+ if (ret->domain != NULL) {
+ strcpy(ret->domain, rec->domain);
+ } else {
+ E502_EthSvcRecordFree(ret);
+ ret = NULL;
+ }
+ }
+ ret->hosttarget = NULL;
+ }
+ return ret;
+}
+
+X502_EXPORT(int32_t) E502_EthSvcRecordFree(t_e502_eth_svc_record_hnd rec) {
+ int32_t err = E502_CHECK_SVC_REC(rec);
+ if (err == X502_ERR_OK) {
+ if (rec->domain != NULL)
+ free(rec->domain);
+ if (rec->hosttarget)
+ free(rec->hosttarget);
+ memset(rec, 0, sizeof(t_svc_record));
+ free(rec);
+ }
+ return err;
+}
+
+
+static void f_svc_context_free(t_service_context *svc_context) {
+#if defined ENABLE_BONJOUR
+ if (svc_context->sdref != NULL)
+ DNSServiceRefDeallocate(svc_context->sdref);
+ if (svc_context->sdref_addr)
+ DNSServiceRefDeallocate(svc_context->sdref_addr);
+#elif defined ENABLE_AVAHI
+ if (svc_context->resolver)
+ avahi_service_resolver_free(svc_context->resolver);
+#endif
+ if (svc_context->rec != NULL)
+ E502_EthSvcRecordFree(svc_context->rec);
+ svc_context->event = E502_ETH_SVC_EVENT_NONE;
+ svc_context->init_done = 0;
+}
+
+static int32_t f_browse_context_free(t_e502_eth_svc_browse_hnd context) {
+ int32_t err = E502_CHECK_BROWSE_CONTEXT(context);
+ if (err == X502_ERR_OK) {
+ unsigned i;
+ for (i=0; i < context->svc_cnt; i++) {
+ f_svc_context_free(&context->services[i]);
+ }
+#if defined ENABLE_BONJOUR
+ if (context->browse_ref != NULL)
+ DNSServiceRefDeallocate(context->browse_ref);
+ if (context->main_ref != NULL)
+ DNSServiceRefDeallocate(context->main_ref);
+#elif defined ENABLE_AVAHI
+ if (context->sb != NULL)
+ avahi_service_browser_free(context->sb);
+ if (context->client != NULL)
+ avahi_client_free(context->client);
+ if (context->poll != NULL) {
+ avahi_simple_poll_quit(context->poll);
+ avahi_simple_poll_free(context->poll);
+ }
+#endif
+ memset(context, 0, sizeof(t_e502_service_browse_context));
+ free(context);
+ }
+ return err;
+}
+
+
+#if defined ENABLE_BONJOUR
+ static const char* f_get_txt_rec(t_svc_txt_record txt_record, const char *name, uint8_t *len) {
+ return TXTRecordGetValuePtr(txt_record.txtLen, txt_record.txtStr, name, len);
+ }
+#elif defined ENABLE_AVAHI
+ static const char* f_get_txt_rec(t_svc_txt_record txt_record, const char *name, uint8_t *len) {
+ const char *ret = NULL;
+ int out = 0;
+ int name_len = strlen(name);
+ while (!out) {
+ const char *label = (const char*)txt_record->text;
+ int rec_len = strlen(label);
+ const char *label_end = strchr(label, '=');
+ int label_len = (label_end != NULL) ? label_end - label : rec_len;
+ if ((name_len == label_len) && !memcmp(name, label, label_len)) {
+ int val_len = label_end == NULL ? 0 : rec_len - label_len;
+ if (val_len != 0) {
+ ret = label_end+1;
+ } else {
+ ret = NULL;
+ }
+ *len = val_len;
+ out = 1;
+ }
+
+ if (!out) {
+ if (txt_record->next != NULL) {
+ txt_record = txt_record->next;
+ } else {
+ out = 1;
+ }
+ }
+ }
+ return ret;
+ }
+#endif
+
+/* Анализ текстовой информации о найденном сервисе и проверка, подходит ли он */
+static int32_t f_svc_check_resolved(t_service_context *svc_context,
+ t_svc_txt_record txt_record) {
+ uint8_t len;
+ int32_t err = X502_ERR_OK;
+ const char *devname_ptr = f_get_txt_rec(txt_record, "devname", &len);
+ const char *serial_ptr;
+ if ((devname_ptr != NULL) &&
+ (!strncmp(devname_ptr, E502_DEVICE_NAME, len) || !strncmp(devname_ptr, E16_DEVICE_NAME, len))) {
+ memcpy(svc_context->rec->devname, devname_ptr, len);
+ serial_ptr = f_get_txt_rec(txt_record, "serial", &len);
+ if (serial_ptr != NULL) {
+ if (len >= X502_SERIAL_SIZE)
+ len = X502_SERIAL_SIZE-1;
+ memcpy(svc_context->rec->serial, serial_ptr, len);
+ svc_context->rec->serial[len] = 0;
+ }
+ } else {
+ err = X502_ERR_INVALID_DEVICE;
+ }
+ return err;
+}
+
+
+static void f_add_new_svc(t_e502_eth_svc_browse_hnd browse_context, t_svc_iface_idx iface_idx,
+ const char *svc_name, const char *domain, const char *regtype) {
+ if (browse_context->svc_cnt < SERVICE_CONTEXT_MAX_CNT) {
+ t_service_context *svc_context = &browse_context->services[browse_context->svc_cnt];
+ size_t domain_len = strlen(domain) + 1;
+
+ memset(svc_context, 0, sizeof(t_service_context));
+ svc_context->rec = f_service_record_create();
+ if (svc_context->rec != NULL) {
+ strncpy(svc_context->rec->service_name, svc_name, X502_INSTANCE_NAME_SIZE-1);
+ svc_context->rec->service_name[X502_INSTANCE_NAME_SIZE-1] = '\0';
+ svc_context->rec->domain = malloc(domain_len);
+ if (svc_context->rec->domain != NULL) {
+ strcpy(svc_context->rec->domain, domain);
+
+ svc_context->rec->iface_idx = iface_idx;
+#ifdef ENABLE_BONJOUR
+ svc_context->sdref = browse_context->main_ref;
+ if (DNSServiceResolve(&svc_context->sdref, kDNSServiceFlagsShareConnection,
+ iface_idx, svc_name, regtype, domain,
+ f_cb_resolve, browse_context) == kDNSServiceErr_NoError) {
+ browse_context->svc_cnt++;
+ } else {
+ svc_context->sdref = NULL;
+ E502_EthSvcRecordFree(svc_context->rec);
+ }
+#elif defined ENABLE_AVAHI
+ svc_context->resolver = avahi_service_resolver_new(browse_context->client,
+ iface_idx, AVAHI_PROTO_INET,
+ svc_name, regtype, domain,
+ AVAHI_PROTO_UNSPEC, 0, f_resolve_callback, browse_context);
+ if (svc_context->resolver != NULL) {
+ browse_context->svc_cnt++;
+ } else {
+ E502_EthSvcRecordFree(svc_context->rec);
+ }
+#endif
+ } else {
+ E502_EthSvcRecordFree(svc_context->rec);
+ }
+ }
+ }
+}
+
+static void f_remove_service_idx(t_e502_eth_svc_browse_hnd browse_context, unsigned i) {
+ t_service_context *svc_context = &browse_context->services[i];
+ if (svc_context->init_done && !svc_context->fail) {
+ svc_context->event = E502_ETH_SVC_EVENT_REMOVE;
+ } else {
+ f_svc_context_free(svc_context);
+ if (i != (browse_context->svc_cnt-1)) {
+ memmove(&browse_context->services[i], &browse_context->services[i+1],
+ (browse_context->svc_cnt-i-1)*sizeof(browse_context->services[0]));
+ }
+ browse_context->svc_cnt--;
+ }
+}
+
+
+static void f_svc_fail(t_e502_eth_svc_browse_hnd browse_context, unsigned i) {
+ t_service_context *svc_context = &browse_context->services[i];
+ if (svc_context->init_done) {
+ svc_context->event = E502_ETH_SVC_EVENT_REMOVE;
+ }
+ svc_context->fail = 1;
+ svc_context->init_done = 0;
+}
+
+
+
+static void f_remove_service(t_e502_eth_svc_browse_hnd browse_context, const char *svc_name,
+ const char *domain) {
+ unsigned i;
+ for (i = 0; i < browse_context->svc_cnt; i++) {
+ t_service_context *svc_context = &browse_context->services[i];
+ if (!strcmp(svc_name, svc_context->rec->service_name) &&
+ !strcmp(domain, svc_context->rec->domain)) {
+ f_remove_service_idx(browse_context, i);
+ break;
+ }
+ }
+}
+
+#ifdef ENABLE_BONJOUR
+static uint32_t f_get_addr(const struct sockaddr *address) {
+ uint32_t ipv4_addr = 0;
+ if (address && address->sa_family == AF_INET) {
+ const unsigned char *b = (const unsigned char *) &((struct sockaddr_in *)address)->sin_addr;
+ ipv4_addr = ((uint32_t)b[0] << 24) |
+ ((uint32_t)b[1] << 16) |
+ ((uint32_t)b[2] << 8) |
+ b[3];
+ }
+ return ipv4_addr;
+}
+
+
+
+static void DNSSD_API f_get_addr_info_reply(
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *hostname,
+ const struct sockaddr *address,
+ uint32_t ttl,
+ void *context
+ ) {
+ t_e502_eth_svc_record_hnd rec = (t_e502_eth_svc_record_hnd)context;
+ if (errorCode != kDNSServiceErr_NoError) {
+ rec->op_err = X502_ERR_DNSSD_COMMUNICATION;
+ rec->op_in_progr = 0;
+ } else {
+ if (address && address->sa_family == AF_INET) {
+ rec->ipv4_addr = f_get_addr(address);
+ rec->op_err = X502_ERR_OK;
+ rec->op_in_progr = 0;
+ }
+ }
+}
+
+void DNSSD_API f_cb_gethosttarget(DNSServiceRef sdRef, DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *fullname, const char *hosttarget,
+ uint16_t port, /* In network byte order */
+ uint16_t txtLen,
+ const unsigned char *txtRecord,
+ void *context) {
+
+ t_e502_eth_svc_record_hnd rec = (t_e502_eth_svc_record_hnd)context;
+ if (errorCode != kDNSServiceErr_NoError) {
+ rec->op_err = X502_ERR_DNSSD_COMMUNICATION;
+ } else {
+ size_t hostlen = strlen(hosttarget)+1;
+ rec->port = ntohs(port);
+ if (rec->hosttarget != NULL)
+ free(rec->hosttarget);
+ rec->hosttarget = malloc(hostlen);
+ if (rec->hosttarget != NULL) {
+ strcpy(rec->hosttarget, hosttarget);
+ rec->op_err = X502_ERR_OK;
+ } else {
+ rec->op_err = X502_ERR_MEMORY_ALLOC;
+ }
+ }
+ rec->op_in_progr = 0;
+}
+
+
+static void DNSSD_API f_cb_browse_addr(
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *hostname,
+ const struct sockaddr *address,
+ uint32_t ttl,
+ void *context
+ ) {
+ t_e502_eth_svc_browse_hnd browse_context = (t_e502_eth_svc_browse_hnd)context;
+
+ unsigned i;
+
+ for (i=0; i < browse_context->svc_cnt; i++) {
+ t_service_context *svc_context = &browse_context->services[i];
+ if (svc_context->sdref_addr == sdRef) {
+ if (errorCode == kDNSServiceErr_NoError) {
+ if (address && address->sa_family == AF_INET) {
+ uint32_t new_addr = f_get_addr(address);
+ if (svc_context->init_done == 0) {
+ svc_context->rec->ipv4_addr = new_addr;
+ svc_context->init_done = 1;
+ svc_context->event = E502_ETH_SVC_EVENT_ADD;
+ } else if (svc_context->event == E502_ETH_SVC_EVENT_NONE) {
+ if (new_addr != svc_context->rec->ipv4_addr) {
+ svc_context->rec->ipv4_addr = new_addr;
+ svc_context->event = E502_ETH_SVC_EVENT_CHANGED;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void DNSSD_API f_cb_resolve (DNSServiceRef sdRef, DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *fullname, const char *hosttarget,
+ uint16_t port, /* In network byte order */
+ uint16_t txtLen,
+ const unsigned char *txtRecord,
+ void *context) {
+ t_e502_eth_svc_browse_hnd browse_context = (t_e502_eth_svc_browse_hnd)context;
+
+ unsigned i;
+
+ for (i=0; i < browse_context->svc_cnt; i++) {
+ t_service_context *svc_context = &browse_context->services[i];
+ if (svc_context->sdref == sdRef) {
+ t_svc_txt_record txt_rec;
+ txt_rec.txtLen = txtLen;
+ txt_rec.txtStr = txtRecord;
+ if (f_svc_check_resolved(svc_context, txt_rec) != X502_ERR_OK) {
+ f_remove_service_idx(browse_context, i);
+ } else {
+ if (svc_context->init_done) {
+ if (svc_context->event == E502_ETH_SVC_EVENT_NONE)
+ svc_context->event = E502_ETH_SVC_EVENT_CHANGED;
+ } else {
+ svc_context->sdref_addr = browse_context->main_ref;
+ if (DNSServiceGetAddrInfo(&svc_context->sdref_addr, kDNSServiceFlagsShareConnection,
+ interfaceIndex,
+ kDNSServiceProtocol_IPv4,
+ hosttarget, f_cb_browse_addr,
+ browse_context)
+ != kDNSServiceErr_NoError) {
+ svc_context->sdref_addr = NULL;
+ f_remove_service_idx(browse_context, i);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+static void DNSSD_API f_browse_replay (DNSServiceRef sdRef, DNSServiceFlags flags,
+ uint32_t interfaceIndex, DNSServiceErrorType errorCode,
+ const char *serviceName, const char *regtype,
+ const char *replyDomain, void *context) {
+ t_e502_eth_svc_browse_hnd browse_context = (t_e502_eth_svc_browse_hnd)context;
+ if (errorCode != kDNSServiceErr_NoError) {
+ browse_context->err = X502_ERR_DNSSD_COMMUNICATION;
+ } else {
+ if (flags & kDNSServiceFlagsAdd) {
+ f_add_new_svc(browse_context, interfaceIndex, serviceName, replyDomain, regtype);
+ } else {
+ f_remove_service(browse_context, serviceName, replyDomain);
+ }
+ }
+}
+#elif defined ENABLE_AVAHI
+ static void f_resolve_addr_callback(AvahiServiceResolver *r, AvahiIfIndex interface,
+ AvahiProtocol protocol, AvahiResolverEvent event,
+ const char *name, const char *type,
+ const char *domain, const char *host_name,
+ const AvahiAddress *address, uint16_t port,
+ AvahiStringList *txt, AvahiLookupResultFlags flags,
+ void* userdata) {
+ t_e502_eth_svc_record_hnd rec = (t_e502_eth_svc_record_hnd) userdata;
+ switch (event) {
+ case AVAHI_RESOLVER_FAILURE:
+ rec->op_err = X502_ERR_DNSSD_COMMUNICATION;
+ rec->op_in_progr = 0;
+ break;
+ case AVAHI_RESOLVER_FOUND: {
+ size_t hostlen = strlen(host_name) + 1;
+ rec->port = port;
+ if (rec->hosttarget != NULL)
+ free(rec->hosttarget);
+ rec->hosttarget = malloc(hostlen);
+ if (rec->hosttarget != NULL) {
+ strcpy(rec->hosttarget, host_name);
+ rec->op_err = X502_ERR_OK;
+ rec->ipv4_addr = ntohl(address->data.ipv4.address);
+ } else {
+ rec->op_err = X502_ERR_MEMORY_ALLOC;
+ }
+ rec->op_in_progr = 0;
+ }
+ break;
+ }
+ }
+
+ static void f_resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface,
+ AvahiProtocol protocol, AvahiResolverEvent event,
+ const char *name, const char *type,
+ const char *domain, const char *host_name,
+ const AvahiAddress *address, uint16_t port,
+ AvahiStringList *txt, AvahiLookupResultFlags flags,
+ void* userdata) {
+ t_e502_eth_svc_browse_hnd browse_context = (t_e502_eth_svc_browse_hnd)userdata;
+ unsigned i;
+ for (i=0; i < browse_context->svc_cnt; i++) {
+ t_service_context *svc_context = &browse_context->services[i];
+ if (svc_context->resolver == r) {
+ /* Called whenever a service has been resolved successfully or timed out */
+ switch (event) {
+ case AVAHI_RESOLVER_FAILURE:
+ f_svc_fail(browse_context, i);
+ break;
+ case AVAHI_RESOLVER_FOUND: {
+ int32_t err = f_svc_check_resolved(svc_context, txt);
+ if (err != X502_ERR_OK) {
+ f_remove_service_idx(browse_context, i);
+ } else {
+ if (svc_context->init_done == 0) {
+ svc_context->init_done = 1;
+ svc_context->fail = 0;
+ svc_context->event = E502_ETH_SVC_EVENT_ADD;
+ } else if (svc_context->event == E502_ETH_SVC_EVENT_NONE) {
+ svc_context->event = E502_ETH_SVC_EVENT_CHANGED;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ static void f_browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface,
+ AvahiProtocol protocol, AvahiBrowserEvent event,
+ const char *name, const char *type,
+ const char *domain, AvahiLookupResultFlags flags,
+ void* userdata) {
+ t_e502_eth_svc_browse_hnd context = (t_e502_eth_svc_browse_hnd)userdata;
+
+ switch (event) {
+ case AVAHI_BROWSER_FAILURE:
+ context->err = X502_ERR_DNSSD_COMMUNICATION;
+ avahi_simple_poll_quit(context->poll);
+ return;
+
+ case AVAHI_BROWSER_NEW:
+ f_add_new_svc(context, interface, name, domain, type);
+ break;
+
+ case AVAHI_BROWSER_REMOVE:
+ f_remove_service(context, name, domain);
+ break;
+
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+
+ break;
+ }
+ }
+
+ static void f_client_callback(AvahiClient *c, AvahiClientState state, void * userdata) {
+
+ /* Called whenever the client or server state changes */
+
+ if (state == AVAHI_CLIENT_FAILURE) {
+ t_e502_eth_svc_browse_hnd context = (t_e502_eth_svc_browse_hnd)userdata;
+ context->err = X502_ERR_DNSSD_NOT_RUNNING;
+ avahi_simple_poll_quit(context->poll);
+ }
+ }
+
+
+
+#endif
+
+
+static t_e502_eth_svc_event f_get_event(t_e502_eth_svc_browse_hnd browse_context,
+ t_e502_eth_svc_record_hnd *svc_hnd, uint32_t *flags) {
+ t_e502_eth_svc_event ret = E502_ETH_SVC_EVENT_NONE;
+ unsigned i;
+
+ for (i=0; (i < browse_context->svc_cnt) && (ret == E502_ETH_SVC_EVENT_NONE); i++) {
+ t_service_context *svc = &browse_context->services[i];
+ if (svc->event != E502_ETH_SVC_EVENT_NONE) {
+ ret = svc->event;
+
+ if ((svc->event == E502_ETH_SVC_EVENT_REMOVE) && !svc->fail) {
+ /* так при удалении запись нам больше не нужна, то можно ее
+ * передать пользователю, без создания копии */
+ *svc_hnd = svc->rec;
+ svc->rec = NULL;
+ f_svc_context_free(svc);
+ if (i != (browse_context->svc_cnt-1)) {
+ memmove(&browse_context->services[i], &browse_context->services[i+1],
+ (browse_context->svc_cnt-i-1)*sizeof(browse_context->services[0]));
+ }
+ browse_context->svc_cnt--;
+ } else {
+ /* если событие ADD/CHANGE, то сбрасываем флаг события */
+ svc->event = E502_ETH_SVC_EVENT_NONE;
+ /* далаем копию записи, чтобы пользователь мог ее сохранить и
+ после завершения */
+ *svc_hnd = f_service_record_create_copy(svc->rec);
+ }
+ }
+ }
+ return ret;
+}
+
+
+
+
+X502_EXPORT(int32_t) E502_EthSvcRecordGetInstanceName(t_e502_eth_svc_record_hnd rec, char *name) {
+ int32_t err = name == NULL ? X502_ERR_INVALID_POINTER : E502_CHECK_SVC_REC(rec);
+ if (err == X502_ERR_OK) {
+ strcpy(name, rec->service_name);
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthSvcRecordGetDevSerial(t_e502_eth_svc_record_hnd rec, char *serial) {
+ int32_t err = serial == NULL ? X502_ERR_INVALID_POINTER : E502_CHECK_SVC_REC(rec);
+ if (err == X502_ERR_OK) {
+ strcpy(serial, rec->serial);
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthSvcRecordGetDevName(t_e502_eth_svc_record_hnd rec, char *devname) {
+ int32_t err = devname == NULL ? X502_ERR_INVALID_POINTER : E502_CHECK_SVC_REC(rec);
+ if (err == X502_ERR_OK) {
+ strcpy(devname, rec->devname);
+ }
+ return err;
+}
+
+#ifdef ENABLE_BONJOUR
+static int32_t f_wait_result(DNSServiceRef ref, t_e502_eth_svc_record_hnd rec, t_ltimer *ptmr) {
+ int32_t err = X502_ERR_OK;
+ int sock = DNSServiceRefSockFD(ref);
+ int nfds = sock + 1;
+ int out = 0;
+ do {
+ fd_set readfds;
+ struct timeval tv;
+ int result;
+
+ FD_ZERO(&readfds);
+ FD_SET(sock, &readfds);
+ f_set_timeval_left(ptmr, &tv);
+ result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
+ if (result > 0) {
+ DNSServiceErrorType dnssd_err = DNSServiceProcessResult(ref);
+ if (dnssd_err == kDNSServiceErr_NoError) {
+ if (!rec->op_in_progr) {
+ err = rec->op_err;
+ out = 1;
+ }
+ } else {
+ err = X502_ERR_DNSSD_COMMUNICATION;
+ }
+ } else if (ltimer_expired(ptmr) && rec->op_in_progr) {
+ err = X502_ERR_SVC_RESOLVE_TIMEOUT;
+ rec->op_in_progr = 0;
+ }
+ } while ((err == X502_ERR_OK) && !out);
+ DNSServiceRefDeallocate(ref);
+ return err;
+}
+#endif
+
+X502_EXPORT(int32_t) E502_EthSvcRecordResolveIPv4Addr(t_e502_eth_svc_record_hnd rec,
+ uint32_t *addr, uint32_t tout) {
+ int32_t err = addr == NULL ? X502_ERR_INVALID_POINTER : E502_CHECK_SVC_REC(rec);
+ if (err == X502_ERR_OK) {
+#if defined ENABLE_BONJOUR
+ DNSServiceRef ref;
+ t_ltimer tmr;
+ ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
+
+
+ rec->op_in_progr = 1;
+ if (DNSServiceResolve(&ref, 0, rec->iface_idx, rec->service_name,
+ E502_TCP_SERVICE_NAME, rec->domain,
+ f_cb_gethosttarget, rec) == kDNSServiceErr_NoError) {
+ err = f_wait_result(ref, rec, &tmr);
+ } else {
+ err = X502_ERR_DNSSD_COMMUNICATION;
+ }
+
+ if (err == X502_ERR_OK) {
+ rec->op_in_progr = 1;
+ if (DNSServiceGetAddrInfo(&ref, 0, rec->iface_idx, kDNSServiceProtocol_IPv4,
+ rec->hosttarget, f_get_addr_info_reply, rec)
+ == kDNSServiceErr_NoError) {;
+ err = f_wait_result(ref, rec, &tmr);
+ } else {
+ err = X502_ERR_DNSSD_COMMUNICATION;
+ }
+ }
+#elif defined ENABLE_AVAHI
+ AvahiSimplePoll *poll = NULL;
+ AvahiClient *client = NULL;
+ AvahiServiceResolver *resolver = NULL;
+ poll = avahi_simple_poll_new();
+ if (poll == NULL) {
+ err = X502_ERR_MEMORY_ALLOC;
+ } else {
+ client = avahi_client_new(avahi_simple_poll_get(poll), 0,
+ NULL, NULL, NULL);
+ if (client == NULL) {
+ err = X502_ERR_DNSSD_COMMUNICATION;
+ }
+ }
+
+ if (err == X502_ERR_OK) {
+ t_ltimer tmr;
+ int out = 0;
+ ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
+ resolver = avahi_service_resolver_new(client, rec->iface_idx, AVAHI_PROTO_INET,
+ rec->service_name, E502_TCP_SERVICE_NAME,
+ rec->domain, AVAHI_PROTO_UNSPEC, 0,
+ f_resolve_addr_callback, rec);
+ do {
+ rec->op_in_progr = 1;
+ avahi_simple_poll_iterate(poll, LTIMER_CLOCK_TICKS_TO_MS(ltimer_expiration(&tmr)));
+ if (rec->op_in_progr == 0) {
+ err = rec->op_err;
+ out = 1;
+ } else if (ltimer_expired(&tmr)) {
+ err = X502_ERR_SVC_RESOLVE_TIMEOUT;
+ }
+ } while ((err == X502_ERR_OK) && !out);
+ }
+
+ if (resolver != NULL)
+ avahi_service_resolver_free(resolver);
+ if (client != NULL)
+ avahi_client_free(client);
+ if (poll != NULL) {
+ avahi_simple_poll_quit(poll);
+ avahi_simple_poll_free(poll);
+ }
+#endif
+ if (err == X502_ERR_OK) {
+ *addr = rec->ipv4_addr;
+ }
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthSvcRecordIsSameInstance(t_e502_eth_svc_record_hnd svc1,
+ t_e502_eth_svc_record_hnd svc2) {
+ int32_t err = E502_CHECK_SVC_REC(svc1);
+ if (err == X502_ERR_OK)
+ err = E502_CHECK_SVC_REC(svc2);
+ if (err == X502_ERR_OK) {
+ if (strcmp(svc1->service_name, svc2->service_name) || strcmp(svc1->domain, svc2->domain)) {
+ err = X502_ERR_INSTANCE_MISMATCH;
+ }
+ }
+ return err;
+}
+
+
+X502_EXPORT(int32_t) E502_EthSvcBrowseStart(t_e502_eth_svc_browse_hnd *pcontext, uint32_t flags) {
+ int32_t err = X502_ERR_OK;
+ t_e502_eth_svc_browse_hnd context = calloc(1, sizeof(t_e502_service_browse_context));
+
+ if (context != NULL) {
+ context->sign = E502_SVC_BROWSE_SIGN;
+ } else {
+ err = X502_ERR_MEMORY_ALLOC;
+ }
+
+#ifdef ENABLE_BONJOUR
+ if (err == X502_ERR_OK) {
+ DNSServiceErrorType dnssd_err = DNSServiceCreateConnection(&context->main_ref);
+ if (dnssd_err == kDNSServiceErr_NoError) {
+ context->browse_ref = context->main_ref;
+ dnssd_err = DNSServiceBrowse(&context->browse_ref, kDNSServiceFlagsShareConnection, 0,
+ E502_TCP_SERVICE_NAME, NULL, f_browse_replay, context);
+
+ if (dnssd_err == kDNSServiceErr_NoError) {
+ context->socket = DNSServiceRefSockFD(context->main_ref);
+ }
+ }
+
+ if (dnssd_err != kDNSServiceErr_NoError) {
+ if (dnssd_err == kDNSServiceErr_ServiceNotRunning) {
+ err = X502_ERR_DNSSD_NOT_RUNNING;
+ } else {
+ err = X502_ERR_DNSSD_COMMUNICATION;
+ }
+ }
+ }
+#elif defined ENABLE_AVAHI
+ if (err == X502_ERR_OK) {
+ context->poll = avahi_simple_poll_new();
+ if (context->poll == NULL) {
+ err = X502_ERR_MEMORY_ALLOC;
+ } else {
+ context->client = avahi_client_new(avahi_simple_poll_get(context->poll), 0,
+ f_client_callback, context, NULL);
+ if (context->client == NULL) {
+ err = X502_ERR_DNSSD_COMMUNICATION;
+ } else if (context->err != X502_ERR_OK) {
+ err = context->err;
+ }
+
+ if (err == X502_ERR_OK) {
+ context->sb = avahi_service_browser_new(context->client, AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_INET,
+ E502_TCP_SERVICE_NAME, NULL, 0,
+ f_browse_callback, context);
+ if (context->sb == NULL)
+ err = X502_ERR_DNSSD_COMMUNICATION;
+ }
+ }
+ }
+#endif
+
+ if (err != X502_ERR_OK) {
+ if (context != NULL)
+ f_browse_context_free(context);
+ } else {
+ *pcontext = context;
+ }
+
+ return err;
+}
+
+
+
+
+X502_EXPORT(int32_t) E502_EthSvcBrowseGetEvent(t_e502_eth_svc_browse_hnd context,
+ t_e502_eth_svc_record_hnd *svc, uint32_t *pevent,
+ uint32_t *flags, uint32_t tout) {
+ int32_t err = (svc == NULL) || (pevent == NULL) ? X502_ERR_INVALID_POINTER :
+ E502_CHECK_BROWSE_CONTEXT(context);
+
+ if (err == X502_ERR_OK) {
+ t_ltimer tmr;
+ t_e502_eth_svc_event evt = f_get_event(context, svc, flags);
+ ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
+ context->err = X502_ERR_OK;
+
+ while ((evt == E502_ETH_SVC_EVENT_NONE) && !ltimer_expired(&tmr) && (err == X502_ERR_OK)) {
+#ifdef ENABLE_BONJOUR
+ fd_set readfds;
+ struct timeval tv;
+ int result;
+
+ FD_ZERO(&readfds);
+ FD_SET(context->socket, &readfds);
+ f_set_timeval_left(&tmr, &tv);
+
+ result = select(context->socket + 1, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
+ if (result > 0) {
+ DNSServiceErrorType dnssd_err = DNSServiceProcessResult(context->main_ref);
+ if (dnssd_err == kDNSServiceErr_NoError) {
+ evt = f_get_event(context, svc, flags);
+ } else {
+ err = X502_ERR_DNSSD_COMMUNICATION;
+ }
+ }
+#elif defined ENABLE_AVAHI
+ avahi_simple_poll_iterate(context->poll, LTIMER_CLOCK_TICKS_TO_MS(ltimer_expiration(&tmr)));
+ if (context->err != X502_ERR_OK) {
+ err = context->err;
+ } else {
+ evt = f_get_event(context, svc, flags);
+ }
+#endif
+ }
+ *pevent = evt;
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthSvcBrowseStop(t_e502_eth_svc_browse_hnd context) {
+ return f_browse_context_free(context);
+}
+
+
+X502_EXPORT(int32_t) E502_MakeDevRecordByEthSvc(t_x502_devrec *devrec, t_e502_eth_svc_record_hnd svc,
+ uint32_t flags, uint32_t tout) {
+
+ int32_t err = e502_make_tcp_rec(devrec, flags, tout, svc->devname);
+ if (err==X502_ERR_OK) {
+ t_tcp_devinfo_data *devinfo_data = (t_tcp_devinfo_data *)devrec->internal->iface_data;
+ devinfo_data->svc_rec = f_service_record_create_copy(svc);
+
+ strcpy(devrec->location, svc->service_name);
+ devrec->location_type = X502_LOCATION_TYPE_INSTANCE_NAME;
+ strcpy(devrec->serial, svc->serial);
+ }
+ return err;
+}
+
+int32_t e502_svc_fill_devinfo(t_tcp_devinfo_data *data) {
+ uint32_t addr;
+ int32_t err = E502_EthSvcRecordResolveIPv4Addr(data->svc_rec, &addr, data->open_tout);
+ if (err == X502_ERR_OK) {
+ data->ip_addr = addr;
+ data->cmd_port = data->svc_rec->port;
+ }
+ return err;
+}
+
+#else
+#include "e502api.h"
+X502_EXPORT(int32_t) E502_EthSvcRecordFree(t_e502_eth_svc_record_hnd rec) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+
+X502_EXPORT(int32_t) E502_EthSvcRecordGetInstanceName(t_e502_eth_svc_record_hnd rec, char *name) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+
+X502_EXPORT(int32_t) E502_EthSvcRecordGetDevSerial(t_e502_eth_svc_record_hnd rec, char *serial) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+
+X502_EXPORT(int32_t) E502_EthSvcRecordResolveIPv4Addr(t_e502_eth_svc_record_hnd rec,
+ uint32_t *addr, uint32_t tout) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+
+X502_EXPORT(int32_t) E502_EthSvcRecordIsSameInstance(t_e502_eth_svc_record_hnd svc1,
+ t_e502_eth_svc_record_hnd svc2) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+
+
+X502_EXPORT(int32_t) E502_EthSvcBrowseStart(t_e502_eth_svc_browse_hnd *pcontext, uint32_t flags) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+
+X502_EXPORT(int32_t) E502_EthSvcBrowseGetEvent(t_e502_eth_svc_browse_hnd context,
+ t_e502_eth_svc_record_hnd *svc, uint32_t *pevent,
+ uint32_t *flags, uint32_t tout) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+
+X502_EXPORT(int32_t) E502_EthSvcBrowseStop(t_e502_eth_svc_browse_hnd context) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+
+X502_EXPORT(int32_t) E502_MakeDevRecordByEthSvc(t_x502_devrec *devrec, t_e502_eth_svc_record_hnd svc,
+ uint32_t flags, uint32_t tout) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+#endif
+
+X502_EXPORT(int32_t) E502_SearchEthForDevicesIPv4Addr(t_x502_devrec* rec_list, uint32_t flags, uint32_t size,
+ uint32_t *devcount, uint32_t event_tout, uint32_t tcp_tout){
+
+ uint32_t count = 0;
+ int32_t err = X502_ERR_OK;
+
+ t_e502_eth_svc_browse_hnd browse_hnd;
+ err = E502_EthSvcBrowseStart(&browse_hnd, 0);
+
+ if (!err){
+ int run = 1;
+ while (run) {
+ t_e502_eth_svc_record_hnd svc_hnd;
+ uint32_t event;
+ // обязательно вернет значение после tout мс
+ err = E502_EthSvcBrowseGetEvent(browse_hnd, &svc_hnd, &event, &flags, event_tout);
+ if (!err){
+ if (E502_ETH_SVC_EVENT_NONE == event) {
+ run = 0;
+ } else {
+ if ((NULL != rec_list) && (count < size)) {
+ int32_t res = E502_MakeDevRecordByEthSvc(&rec_list[count], svc_hnd, flags, tcp_tout);
+ if (X502_ERR_OK != res){
+ break;
+ }
+ uint32_t ip_addr;
+ E502_EthSvcRecordResolveIPv4Addr(svc_hnd, &ip_addr ,tcp_tout);
+
+ sprintf(rec_list[count].location, "%d.%d.%d.%d",
+ (ip_addr>>24) & 0xFF,
+ (ip_addr>>16) & 0xFF,
+ (ip_addr>>8) & 0xFF,
+ (ip_addr>>0) & 0xFF);
+ rec_list[count].location_type = X502_LOCATION_TYPE_ADDR;
+ }
+ count++;
+ }
+ } else {
+ run = 0;
+ }
+ }
+ }
+ err = E502_EthSvcBrowseStop(browse_hnd);
+
+ if (NULL != devcount){
+ *devcount = count;
+ }
+
+ return err != X502_ERR_OK ? err : count > (uint32_t)size ? (int32_t)size : (int32_t)count ;
+}
diff --git a/e502/e502api_eth_config.c b/e502/e502api_eth_config.c
new file mode 100644
index 0000000..a300acb
--- /dev/null
+++ b/e502/e502api_eth_config.c
@@ -0,0 +1,307 @@
+#include "e502api_private.h"
+#include "e502_eth_config.h"
+#include
+#include
+
+#if E502_ETHCONFIG_MAC_ADDR_SIZE != X502_MAC_ADDR_SIZE
+ #error "inconsistent E502_ETHCONFIG_MAC_ADDR_SIZE"
+#endif
+#if E502_ETHCONFIG_INSTANCE_NAME_SIZE != X502_INSTANCE_NAME_SIZE
+ #error "inconsistent E502_ETHCONFIG_INSTANCE_NAME_SIZE"
+#endif
+#if E502_ETHCONFIG_PASSWD_SIZE != X502_PASSWORD_SIZE
+ #error "inconsistent E502_ETHCONFIG_PASSWD_SIZE"
+#endif
+
+
+#define E502_ETH_CFG_SIGN 0xE502CFA5
+
+#define E502_ETH_CHECK_CFG(cfg) ((cfg != NULL) ? (cfg)->sign == E502_ETH_CFG_SIGN ? X502_ERR_OK \
+ : X502_ERR_INVALID_CONFIG_HANDLE : X502_ERR_INVALID_CONFIG_HANDLE)
+
+
+
+#define IP_ADDR_TO_UINT32(addr, dword) ((dword) = ((uint32_t)addr[0] << 24) | \
+ ((uint32_t)addr[1] << 16) | \
+ ((uint32_t)addr[2] << 8) | \
+ addr[3]);
+
+#define IP_UINT32_TO_ADDR(dword, addr) do { \
+ addr[0] = (dword >> 24) & 0xFF; \
+ addr[1] = (dword >> 16) & 0xFF; \
+ addr[2] = (dword >> 8) & 0xFF; \
+ addr[3] = (dword) & 0xFF; \
+ } while(0)
+
+
+typedef struct st_e502_eth_config_state {
+ uint32_t sign;
+ uint32_t flags;
+ uint8_t factory_mac[E502_ETHCONFIG_MAC_ADDR_SIZE];
+ t_e502_eth_set_config_params params;
+} t_e502_eth_config_state;
+
+
+
+
+X502_EXPORT(t_e502_eth_config_hnd) E502_EthConfigCreate(void) {
+ t_e502_eth_config_hnd cfg = calloc(1, sizeof(t_e502_eth_config_state));
+ if (cfg != NULL) {
+ cfg->sign = E502_ETH_CFG_SIGN;
+ }
+ return cfg;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigCopy(t_e502_eth_config_hnd src_cfg, t_e502_eth_config_hnd dst_cfg) {
+ int32_t err = E502_ETH_CHECK_CFG(src_cfg);
+ if (err == X502_ERR_OK)
+ err = E502_ETH_CHECK_CFG(dst_cfg);
+ if (err == X502_ERR_OK) {
+ memcpy(dst_cfg, src_cfg, sizeof(t_e502_eth_config_state));
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigFree(t_e502_eth_config_hnd cfg) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK) {
+ memset(cfg, 0, sizeof(t_e502_eth_config_state));
+ free(cfg);
+ }
+ return err;
+}
+
+
+X502_EXPORT(int32_t) E502_EthConfigRead(t_x502_hnd hnd, t_e502_eth_config_hnd cfg) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK) {
+ err = X502_CHECK_HND_OPENED(hnd);
+ }
+ if (err == X502_ERR_OK) {
+ uint32_t recvd_size;
+
+ err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_ETH_CFG_GET, 0,
+ NULL, 0, &cfg->params.cfg, sizeof(t_e502_eth_config), &recvd_size, 0);
+ if (err == X502_ERR_OK) {
+ cfg->flags = 0;
+ memcpy(cfg->factory_mac, hnd->info.factory_mac, sizeof(cfg->factory_mac));
+ }
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigWrite(t_x502_hnd hnd, t_e502_eth_config_hnd cfg, const char *passwd) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK) {
+ err = X502_CHECK_HND_OPENED(hnd);
+ }
+ if (err == X502_ERR_OK) {
+ if (passwd != NULL) {
+ strncpy(cfg->params.passwd, passwd, E502_ETHCONFIG_PASSWD_SIZE-1);
+ cfg->params.passwd[E502_ETHCONFIG_PASSWD_SIZE-1] = '\0';
+ } else {
+ cfg->params.passwd[0] = '\0';
+ }
+
+ err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_ETH_CFG_SET, cfg->flags,
+ &cfg->params, sizeof(cfg->params),
+ NULL, 0, NULL, 0);
+ memset(cfg->params.passwd, 0, E502_ETHCONFIG_PASSWD_SIZE);
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigSetNewPassword(t_e502_eth_config_hnd cfg, const char *new_passwd) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK) {
+ cfg->flags |= E502_ETH_CONFIG_FLAGS_SET_NEW_PASSWD;
+ if (new_passwd != NULL) {
+ strncpy(cfg->params.new_passwd, new_passwd, E502_ETHCONFIG_PASSWD_SIZE);
+ cfg->params.new_passwd[E502_ETHCONFIG_PASSWD_SIZE-1] = '\0';
+ } else {
+ cfg->params.new_passwd[0] = '\0';
+ }
+ }
+ return err;
+}
+
+
+X502_EXPORT(int32_t) E502_EthConfigGetEnabled(t_e502_eth_config_hnd cfg, uint32_t *en) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ *en = cfg->params.cfg.flags & E502_ETH_FLAGS_IFACE_ENABLED;
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigSetEnabled(t_e502_eth_config_hnd cfg, uint32_t en) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK) {
+ if (en) {
+ cfg->params.cfg.flags |= E502_ETH_FLAGS_IFACE_ENABLED;
+ } else {
+ cfg->params.cfg.flags &= ~E502_ETH_FLAGS_IFACE_ENABLED;
+ }
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigGetAutoIPState(t_e502_eth_config_hnd cfg, uint32_t *state) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ *state = cfg->params.cfg.flags & E502_ETH_FLAGS_AUTO_IP_STATE_MASK;
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigGetAutoIPEnabled(t_e502_eth_config_hnd cfg, uint32_t *en) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ *en = cfg->params.cfg.flags & E502_ETH_FLAGS_AUTO_IP;
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigSetAutoIPEnabled(t_e502_eth_config_hnd cfg, uint32_t en) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK) {
+ if (en) {
+ cfg->params.cfg.flags |= E502_ETH_FLAGS_AUTO_IP;
+ } else {
+ cfg->params.cfg.flags &= ~E502_ETH_FLAGS_AUTO_IP;
+ }
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigGetUserMACEnabled(t_e502_eth_config_hnd cfg, uint32_t *en) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ *en = cfg->params.cfg.flags & E502_ETH_FLAGS_USER_MAC;
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigSetUserMACEnabled(t_e502_eth_config_hnd cfg, uint32_t en) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK) {
+ if (en) {
+ cfg->params.cfg.flags |= E502_ETH_FLAGS_USER_MAC;
+ } else {
+ cfg->params.cfg.flags &= ~E502_ETH_FLAGS_USER_MAC;
+ }
+ }
+ return err;
+}
+
+
+X502_EXPORT(int32_t) E502_EthConfigGetIPv4Addr(t_e502_eth_config_hnd cfg, uint32_t *addr) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ IP_ADDR_TO_UINT32(cfg->params.cfg.ipv4.addr, *addr);
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigSetIPv4Addr(t_e502_eth_config_hnd cfg, uint32_t addr) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ IP_UINT32_TO_ADDR(addr, cfg->params.cfg.ipv4.addr);
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigGetIPv4Mask(t_e502_eth_config_hnd cfg, uint32_t *mask) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ IP_ADDR_TO_UINT32(cfg->params.cfg.ipv4.mask, *mask);
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigSetIPv4Mask(t_e502_eth_config_hnd cfg, uint32_t mask) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ IP_UINT32_TO_ADDR(mask, cfg->params.cfg.ipv4.mask);
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigGetIPv4Gate(t_e502_eth_config_hnd cfg, uint32_t *gate) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ IP_ADDR_TO_UINT32(cfg->params.cfg.ipv4.gate, *gate);
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigSetIPv4Gate(t_e502_eth_config_hnd cfg, uint32_t gate) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ IP_UINT32_TO_ADDR(gate, cfg->params.cfg.ipv4.gate);
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigGetUserMac(t_e502_eth_config_hnd cfg, uint8_t *mac) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ memcpy(mac, cfg->params.cfg.mac, E502_ETHCONFIG_MAC_ADDR_SIZE);
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigSetUserMac(t_e502_eth_config_hnd cfg, const uint8_t *mac) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ memcpy(cfg->params.cfg.mac, mac, E502_ETHCONFIG_MAC_ADDR_SIZE);
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigGetFactoryMac(t_e502_eth_config_hnd cfg, uint8_t *mac) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ memcpy(mac, cfg->factory_mac, E502_ETHCONFIG_MAC_ADDR_SIZE);
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigGetInstanceName(t_e502_eth_config_hnd cfg, char *name) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK) {
+ strncpy(name, cfg->params.cfg.inst_name, E502_ETHCONFIG_INSTANCE_NAME_SIZE);
+ name[E502_ETHCONFIG_INSTANCE_NAME_SIZE-1] = '\0';
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigSetInstanceName(t_e502_eth_config_hnd cfg, const char *name) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK) {
+ if (name != NULL) {
+ strncpy(cfg->params.cfg.inst_name, name, E502_ETHCONFIG_INSTANCE_NAME_SIZE);
+ cfg->params.cfg.inst_name[E502_ETHCONFIG_INSTANCE_NAME_SIZE-1] = '\0';
+ } else {
+ cfg->params.cfg.inst_name[0] = '\0';
+ }
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigSetTcpCmdPort(t_e502_eth_config_hnd cfg, uint16_t port) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK) {
+ cfg->params.cfg.tcp_cmd_port = port;
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigSetTcpDataPort(t_e502_eth_config_hnd cfg, uint16_t port) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK) {
+ cfg->params.cfg.tcp_data_port = port;
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigGetTcpCmdPort(t_e502_eth_config_hnd cfg, uint16_t *port) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ *port = cfg->params.cfg.tcp_cmd_port;
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthConfigGetTcpDataPort(t_e502_eth_config_hnd cfg, uint16_t *port) {
+ int32_t err = E502_ETH_CHECK_CFG(cfg);
+ if (err == X502_ERR_OK)
+ *port = cfg->params.cfg.tcp_data_port;
+ return err;
+}
diff --git a/e502/e502api_private.h b/e502/e502api_private.h
new file mode 100644
index 0000000..64261c0
--- /dev/null
+++ b/e502/e502api_private.h
@@ -0,0 +1,47 @@
+#ifndef E502API_PRIVATE_H
+#define E502API_PRIVATE_H
+
+#include "e502api.h"
+#include "x502api_private.h"
+#include "e502_cm4_defs.h"
+#include "lboot_req.h"
+
+
+#define E502_CM4_DEVFLAGS (X502_DEVFLAGS_IFACE_SUPPORT_ETH \
+ | X502_DEVFLAGS_INDUSTRIAL \
+ | X502_DEVFLAGS_FPGA_LOADED \
+ | X502_DEVFLAGS_DAC_TYPE \
+ )
+
+
+#define E502_DEVICE_NAME "E502"
+#define E16_DEVICE_NAME "E16"
+
+int32_t e502_iface_fpga_read(t_x502_hnd hnd, uint32_t addr, uint32_t *val);
+int32_t e502_iface_fpga_write(t_x502_hnd hnd, uint32_t addr, uint32_t val);
+int32_t e502_iface_fpga_mode_init(t_x502_hnd hnd);
+int32_t e502_iface_stream_running(t_x502_hnd hnd, uint32_t ch, int32_t* running);
+int32_t e502_iface_bf_mem_block_rd(t_x502_hnd hnd, uint32_t addr, uint32_t *block, uint32_t size);
+int32_t e502_iface_bf_mem_block_wr(t_x502_hnd hnd, uint32_t addr, const uint32_t *block, uint32_t size);
+int32_t e502_iface_bf_firm_load(t_x502_hnd hnd, const char *filename);
+
+
+
+int32_t e502_iface_flash_rd(t_x502_hnd hnd, uint32_t addr, uint8_t* data, uint32_t size);
+int32_t e502_iface_flash_wr(t_x502_hnd hnd, uint32_t addr, const uint8_t* data, uint32_t size);
+int32_t e502_iface_flash_erase(t_x502_hnd hnd, uint32_t addr, uint32_t size);
+int32_t e502_iface_flash_set_prot(t_x502_hnd hnd, uint32_t prot, const uint8_t* prot_data, uint32_t size);
+int32_t e502_iface_reload_dev_info(t_x502_hnd hnd);
+
+int32_t e502_iface_cycle_load_start(t_x502_hnd hnd, uint32_t size);
+int32_t e502_iface_cycle_setup(t_x502_hnd hnd, uint32_t flags);
+int32_t e502_iface_cycle_stop(t_x502_hnd hnd, uint32_t flags);
+int32_t e502_iface_cycle_check_setup(t_x502_hnd hnd, uint32_t *done);
+int32_t e502_iface_check_feature(t_x502_hnd hnd, uint32_t feature);
+
+void e502_devinfo_init(t_x502_info *info, const t_lboot_devinfo *lboot_info);
+int32_t e502_fill_devflags(t_x502_hnd hnd);
+
+
+
+#endif // E502API_PRIVATE_H
diff --git a/e502/e502api_tcp.c b/e502/e502api_tcp.c
new file mode 100644
index 0000000..480cc15
--- /dev/null
+++ b/e502/e502api_tcp.c
@@ -0,0 +1,902 @@
+#ifdef ENABLE_TCP
+#include "e502api_private.h"
+#include "e502_tcp_protocol.h"
+#include "ltimer.h"
+#include "e502_fpga_regs.h"
+#include "e502api_tcp_private.h"
+#include "osspec.h"
+
+#include
+#include
+
+#if defined _WIN32
+ #include
+ typedef int socklen_t;
+ typedef SOCKET t_socket;
+
+ #define SOCK_ERR_SIGBREAK() 0
+
+ #define L_SOCK_LAST_ERR_BLOCK() (WSAEWOULDBLOCK == WSAGetLastError())
+ #define L_SOCK_LAST_ERR_RESET() (WSAECONNRESET == WSAGetLastError())
+#else
+ #include
+
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+
+ #include
+ //#include
+
+ #include
+
+
+ typedef int t_socket;
+
+ #define INVALID_SOCKET -1
+ #define SOCKET_ERROR -1
+ #define SOCK_ERR_SIGBREAK() (EINTR == errno)
+
+ #define L_SOCK_LAST_ERR_BLOCK() ((EAGAIN==errno) || (EWOULDBLOCK==errno))
+#ifdef ECONNRESET
+ #define L_SOCK_LAST_ERR_RESET() (ECONNRESET==errno)
+#endif
+
+ #define closesocket(sock) close(sock)
+#endif
+
+#ifndef MSG_NOSIGNAL
+ #define MSG_NOSIGNAL 0
+#endif
+
+#define E502_TCP_REQ_TOUT 5000
+#define E502_TCP_STOP_WAIT_TOUT 5000
+#define X502_MUTEX_TCP_IOCTL_LOCK_TOUT 5000
+#define X502_MUTEX_TCP_DATA_LOCK_TOUT 5000
+
+
+
+#define TCP_CTL_REQ_MAX_SIZE 512
+#define TCP_IN_STREAM_BUF_MIN 128
+
+#define TCP_IOCTL_INLINE_MAX_DATA_SIZE 64
+
+#if 0
+ #define dprintf(...) fprintf(stderr, __VA_ARGS__)
+#else
+ #define dprintf(...)
+#endif
+
+typedef struct {
+ t_socket cmd_sock;
+ t_socket data_sock;
+ uint32_t ip_addr;
+ uint32_t open_tout;
+ uint16_t data_port;
+ t_mutex ioctl_mutex;
+ t_mutex data_mutex;
+ uint32_t data_chs_en;
+
+ uint32_t recv_part_wrd; /**< принятое неполностью слово */
+ uint32_t send_part_wrd; /**< переданное неполностью слово */
+ uint8_t recv_part_size; /**< кол-во принятых байт в последнем неполном слове */
+ uint8_t send_part_size; /**< кол-во неотправленных байт в последнем переданном не полностью слове */
+} t_tcp_iface_data;
+
+static int32_t f_iface_free_devinfo_ptr(t_x502_devrec_inptr *devinfo_ptr);
+static int32_t f_iface_open(t_x502_hnd hnd, const t_x502_devrec *devrec);
+static int32_t f_iface_close(t_x502_hnd hnd);
+static int32_t f_iface_stream_cfg(t_x502_hnd hnd, uint32_t ch, t_x502_stream_ch_params *params);
+static int32_t f_iface_stream_start(t_x502_hnd hnd, uint32_t ch, uint32_t signle);
+static int32_t f_iface_stream_stop(t_x502_hnd hnd, uint32_t ch, uint32_t flags);
+static int32_t f_iface_stream_free(t_x502_hnd hnd, uint32_t ch, uint32_t flags);
+static int32_t f_iface_stream_read(t_x502_hnd hnd, uint32_t *buf, uint32_t size, uint32_t tout);
+static int32_t f_iface_stream_write(t_x502_hnd hnd, const uint32_t *buf, uint32_t size, uint32_t tout) ;
+static int32_t f_iface_stream_get_rdy_cnt(t_x502_hnd hnd, uint32_t ch, uint32_t *rdy_cnt);
+
+static int32_t f_iface_gen_ioctl(t_x502_hnd hnd, uint32_t cmd_code, uint32_t param,
+ const void* snd_data, uint32_t snd_size,
+ void* rcv_data, uint32_t recv_size, uint32_t* recvd_size,
+ uint32_t tout);
+
+static const t_x502_dev_iface f_tcp_iface = {
+ E502_REGS_ARM_HARD_ID,
+ TCP_IN_STREAM_BUF_MIN,
+ TCP_CTL_REQ_MAX_SIZE,
+ TCP_CTL_REQ_MAX_SIZE/4,
+ TCP_CTL_REQ_MAX_SIZE, //flash rd size
+ TCP_CTL_REQ_MAX_SIZE, //flash wr size
+ f_iface_free_devinfo_ptr,
+ f_iface_open,
+ f_iface_close,
+ e502_iface_fpga_read,
+ e502_iface_fpga_write,
+ f_iface_stream_cfg,
+ f_iface_stream_start,
+ f_iface_stream_stop,
+ f_iface_stream_free,
+ e502_iface_stream_running,
+ f_iface_stream_read,
+ f_iface_stream_write,
+ f_iface_stream_get_rdy_cnt,
+ e502_iface_bf_mem_block_rd,
+ e502_iface_bf_mem_block_wr,
+ e502_iface_bf_firm_load,
+ e502_iface_flash_rd,
+ e502_iface_flash_wr,
+ e502_iface_flash_erase,
+ e502_iface_flash_set_prot,
+ e502_iface_reload_dev_info,
+ e502_iface_cycle_load_start,
+ e502_iface_cycle_setup,
+ e502_iface_cycle_stop,
+ e502_iface_cycle_check_setup,
+ e502_iface_fpga_mode_init,
+ f_iface_gen_ioctl,
+ e502_iface_check_feature
+};
+
+
+static void f_set_timeval_left(t_ltimer* tmr, struct timeval* tval) {
+ t_lclock_ticks left = ltimer_expiration(tmr);
+ tval->tv_sec = left / LCLOCK_TICKS_PER_SECOND;
+ tval->tv_usec = (left % LCLOCK_TICKS_PER_SECOND) * 1000000/LCLOCK_TICKS_PER_SECOND;
+}
+
+static int32_t f_con_sock(t_socket *psock, uint32_t ip_addr, uint16_t port, uint32_t tout) {
+ int32_t err = X502_ERR_OK;
+ struct sockaddr_in peer;
+ int connected = 0;
+
+ t_socket s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (s == INVALID_SOCKET)
+ err = X502_ERR_SOCKET_OPEN;
+
+ /* Переводим сокет в неблокирующий режим работы */
+ if (err == X502_ERR_OK) {
+#ifdef _WIN32
+ ULONG nonblocking = 1;
+ if (SOCKET_ERROR == ioctlsocket(s, FIONBIO, &nonblocking))
+ err = X502_ERR_SOCKET_OPEN;
+#else
+ int n = fcntl(s, F_GETFL, 0);
+ if (fcntl(s, F_SETFL, n|O_NONBLOCK)==-1) {
+ err = X502_ERR_SOCKET_OPEN;
+ }
+#endif
+ }
+
+ if (err == X502_ERR_OK) {
+ /* заполняем структуру с адресом LTR-сервера */
+ memset(&peer, 0, sizeof(peer));
+ peer.sin_family = AF_INET;
+ peer.sin_port = htons(port);
+ peer.sin_addr.s_addr = htonl(ip_addr);
+ }
+
+ while (!connected && (err==X502_ERR_OK)) {
+ t_ltimer tmr;
+ ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
+
+ if (SOCKET_ERROR == connect(s, (struct sockaddr*)&peer, sizeof(peer))) {
+ int sockerr = 0;
+ fd_set fd_w, fd_e;
+#ifdef _WIN32
+ if (WSAEWOULDBLOCK != WSAGetLastError()) {
+#else
+ if (errno != EINPROGRESS) {
+#endif
+ err = X502_ERR_SOCKET_OPEN;
+ } else {
+ struct timeval tval;
+
+ FD_ZERO(&fd_w);
+ FD_SET(s, &fd_w);
+ FD_ZERO(&fd_e);
+ FD_SET(s, &fd_e);
+
+ f_set_timeval_left(&tmr, &tval);
+
+ if (select((int)s+1, NULL, &fd_w, &fd_e, &tval) < 1)
+ err = X502_ERR_CONNECTION_TOUT;
+ }
+
+ if (err == X502_ERR_OK) {
+ /* судя по man - если произошла ошибка, то сокет становится writable!
+ так что в fd_w тоже нужно проверять ошибку */
+ socklen_t optlen = sizeof(sockerr);
+ if (SOCKET_ERROR == getsockopt(s, SOL_SOCKET, SO_ERROR,
+ (char*)&sockerr, &optlen)) {
+ err = X502_ERR_SOCKET_OPEN;
+ } else if (sockerr) {
+#ifdef EHOSTUNREACH
+ if (sockerr == EHOSTUNREACH) {
+ err = X502_ERR_HOST_UNREACHABLE;
+ }
+#endif
+#ifdef ECONNRESET
+ if (sockerr == ECONNRESET) {
+ err = X502_ERR_CONNECTION_RESET;
+ }
+#endif
+ if (err == X502_ERR_OK)
+ err = X502_ERR_TCP_CONNECTION_ERROR;
+ }
+ }
+
+ /* проверяем, что соединились успешно */
+ if ((err == X502_ERR_OK) && !sockerr && (FD_ISSET(s, &fd_w)))
+ connected = 1;
+ } else {
+ /* удалось соединится без ожидания */
+ connected = 1;
+ }
+ } /* while (!connected && !err) */
+
+
+ if (err != X502_ERR_OK) {
+ if (s!=INVALID_SOCKET) {
+ closesocket(s);
+ }
+ } else if (psock!=NULL) {
+ *psock = s;
+ }
+ return err;
+}
+
+
+static int32_t f_recv(t_socket s, uint8_t *buf, uint32_t size, t_ltimer *ptmr) {
+ int32_t err = X502_ERR_OK;
+ uint32_t offset = 0;
+ int timed_out = 0;
+ fd_set fd_r;
+ struct timeval tval;
+
+ if ((err == X502_ERR_OK) && (size!=0)) {
+ while (!err && !timed_out && (offset < size)) {
+ FD_ZERO(&fd_r);
+ FD_SET(s, &fd_r);
+ f_set_timeval_left(ptmr, &tval);
+ switch (select((int)s+1, &fd_r, NULL, NULL, &tval)) {
+ case SOCKET_ERROR:
+ /* Если пришел сигнал, то это не ошибка приема.
+ * Но скорее всего управление стоит вернуть сразу, хотя
+ * может сделать опцию... */
+ if (SOCK_ERR_SIGBREAK()) {
+ ltimer_set(ptmr, 0);
+ timed_out = 1;
+ } else {
+ err = X502_ERR_RECV;
+ }
+ break;
+ case 0: // таймаут
+ timed_out = 1;
+ break;
+ default: { /* дождались готовности на чтение */
+ int res = recv(s, buf + offset, size - offset, 0);
+ if (SOCKET_ERROR == res) {
+ if (!L_SOCK_LAST_ERR_BLOCK()) {
+ err = X502_ERR_RECV;
+ }
+ } else if (0 == res) {
+ /* соединение закрыто */
+ err = X502_ERR_CONNECTION_CLOSED_BY_DEV;
+ } else {
+ offset += res;
+ }
+ }
+ break;
+ }
+ } /* switch (select(ch->sock+1, &fd_r, NULL, NULL, &tval)) */
+ }
+
+ return err ? err : (int32_t)offset;
+}
+
+int32_t f_send(t_socket s, const uint8_t *buf, uint32_t size, t_ltimer *ptmr) {
+ int32_t err = X502_ERR_OK;
+ uint32_t offset = 0;
+ int timed_out = 0;
+ fd_set fd_w;
+
+ if ((err == X502_ERR_OK) && (size != 0)) {
+ while ((err == X502_ERR_OK) && !timed_out && (offset < size)) {
+ /* Сначала пробуем сделать запись без ожидания */
+ int res = send(s, buf + offset, size - offset, MSG_NOSIGNAL);
+ if (res == SOCKET_ERROR) {
+ struct timeval tval;
+ if (L_SOCK_LAST_ERR_BLOCK()) {
+ /* Надо ждать освобождения сокета */
+ FD_ZERO(&fd_w);
+ FD_SET(s, &fd_w);
+ f_set_timeval_left(ptmr, &tval);
+ switch (select((int)s+1, NULL, &fd_w, NULL, &tval)) {
+ case SOCKET_ERROR:
+ if (SOCK_ERR_SIGBREAK()) {
+ ltimer_set(ptmr, 0);
+ timed_out = 1;
+ } else {
+ err = X502_ERR_SEND;
+ }
+ break;
+ case 0: // таймаут
+ timed_out = 1;
+ break;
+ default:
+ if (ltimer_expired(ptmr))
+ timed_out = 1;
+ break;
+ }
+ } else {
+ err = X502_ERR_SEND;
+ }
+ } else { // no error
+ offset += res;
+ }
+ }
+ }
+
+ return (err) ? err : (int)offset;
+}
+
+
+static int32_t f_recv_exact(t_socket s, uint8_t *buf, uint32_t size, t_ltimer *ptmr) {
+ int32_t ret = f_recv(s, buf, size, ptmr);
+ return ret == (int32_t)size ? X502_ERR_OK : ret < 0 ? ret : X502_ERR_RECV_INSUFFICIENT_WORDS;
+}
+
+static int32_t f_send_exact(t_socket s, const uint8_t *buf, uint32_t size, t_ltimer *ptmr) {
+ int32_t ret = f_send(s, buf, size, ptmr);
+ return ret == (int32_t)size ? X502_ERR_OK : ret < 0 ? ret : X502_ERR_SEND_INSUFFICIENT_WORDS;
+}
+
+
+
+static int32_t f_iface_gen_ioctl(t_x502_hnd hnd, uint32_t cmd_code, uint32_t param,
+ const void* snd_data, uint32_t snd_size,
+ void* rcv_data, uint32_t recv_size, uint32_t* recvd_size, uint32_t tout) {
+ struct {
+ t_e502_tcp_cmd_hdr hdr;
+ uint8_t data[TCP_IOCTL_INLINE_MAX_DATA_SIZE];
+ } cmd;
+ t_e502_tcp_resp_hdr cmd_resp;
+ int32_t err = X502_ERR_OK;
+ t_ltimer tmr;
+ t_tcp_iface_data* iface_data = (t_tcp_iface_data*)hnd->iface_data;
+ t_socket s = iface_data->cmd_sock;
+
+ err = osspec_mutex_lock(iface_data->ioctl_mutex, X502_MUTEX_TCP_IOCTL_LOCK_TOUT);
+ if (err == X502_ERR_OK) {
+ ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout == 0 ? E502_TCP_REQ_TOUT : tout));
+ cmd.hdr.sign = E502_TCP_CMD_SIGNATURE;
+ cmd.hdr.cmd = cmd_code;
+ cmd.hdr.par = param;
+ cmd.hdr.data_len = snd_size;
+ cmd.hdr.resp_len = recv_size;
+
+ /* чтобы избежать двух передач по TCP, если данных, передаваемых с командой,
+ * меньше заданного порога, то объединяем их вместе с заголовком и посылаем
+ * за один вызов send */
+ if (snd_size <= TCP_IOCTL_INLINE_MAX_DATA_SIZE) {
+ if (snd_size > 0)
+ memcpy(cmd.data, snd_data, snd_size);
+ err = f_send_exact(s, (uint8_t*)&cmd, E502_TCP_CMD_HDR_SIZE + snd_size, &tmr);
+ } else {
+ err = f_send_exact(s, (uint8_t*)&cmd.hdr, E502_TCP_CMD_HDR_SIZE, &tmr);
+ if (err == X502_ERR_OK) {
+ err = f_send_exact(s, snd_data, snd_size, &tmr);
+ }
+ }
+
+ if (err == X502_ERR_OK) {
+ err = f_recv_exact(s, (uint8_t*)&cmd_resp, E502_TCP_CMD_RESP_SIZE, &tmr);
+ if (err == X502_ERR_RECV_INSUFFICIENT_WORDS) {
+ err = X502_ERR_NO_CMD_RESPONSE;
+ }
+ }
+ if ((err == X502_ERR_OK) && (cmd_resp.len > 0)) {
+ if (cmd_resp.len > recv_size) {
+ err = X502_ERR_IOCTL_INVALID_RESP_SIZE;
+ } else {
+ err = f_recv_exact(s, rcv_data, cmd_resp.len, &tmr);
+ }
+ }
+
+ if (err == X502_ERR_OK) {
+ if (recvd_size != NULL) {
+ *recvd_size = cmd_resp.len;
+ } else if (cmd_resp.len != recv_size) {
+ err = X502_ERR_IOCTL_INVALID_RESP_SIZE;
+ }
+ }
+
+ if ((err == X502_ERR_OK) && (cmd_resp.res!=0)) {
+ err = cmd_resp.res;
+ }
+
+ osspec_mutex_release(iface_data->ioctl_mutex);
+ }
+
+ return err;
+}
+
+static int32_t f_iface_free_devinfo_ptr(t_x502_devrec_inptr *devinfo_ptr) {
+ t_tcp_devinfo_data *devinfo_data = (t_tcp_devinfo_data*)devinfo_ptr->iface_data;
+#ifdef ENABLE_DNSSD
+ if ((devinfo_data != NULL) && (devinfo_data->svc_rec != NULL))
+ E502_EthSvcRecordFree(devinfo_data->svc_rec);
+#endif
+ free(devinfo_ptr->iface_data);
+ free(devinfo_ptr);
+ return X502_ERR_OK;
+}
+
+static int32_t f_iface_open(t_x502_hnd hnd, const t_x502_devrec *devrec) {
+ int32_t err = X502_ERR_OK;
+ t_tcp_devinfo_data *devinfo_data = (t_tcp_devinfo_data*)devrec->internal->iface_data;
+ t_socket s = INVALID_SOCKET;
+#ifdef ENABLE_DNSSD
+ if (devinfo_data->svc_rec) {
+ err = e502_svc_fill_devinfo(devinfo_data);
+ }
+#endif
+
+ if (err == X502_ERR_OK) {
+ err = f_con_sock(&s, devinfo_data->ip_addr, devinfo_data->cmd_port, devinfo_data->open_tout);
+ if (err == X502_ERR_OK) {
+ int flag = 1;
+ if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag))==SOCKET_ERROR)
+ err = X502_ERR_SOCKET_OPEN;
+ }
+ }
+
+ if (err == X502_ERR_OK) {
+ t_tcp_iface_data *iface_data = malloc(sizeof(t_tcp_iface_data));
+ if (iface_data == NULL) {
+ err = X502_ERR_MEMORY_ALLOC;
+ } else {
+ t_lboot_devinfo lboot_info;
+ iface_data->cmd_sock = s;
+ iface_data->data_sock = INVALID_SOCKET;
+ iface_data->ip_addr = devinfo_data->ip_addr;
+ iface_data->open_tout = devinfo_data->open_tout;
+ iface_data->data_port = devinfo_data->data_port;
+ iface_data->ioctl_mutex = osspec_mutex_create();
+ iface_data->data_mutex = osspec_mutex_create();
+ iface_data->data_chs_en = 0;
+ if ((iface_data->ioctl_mutex == OSSPEC_INVALID_MUTEX)
+ || (iface_data->data_mutex == OSSPEC_INVALID_MUTEX)) {
+ err = X502_ERR_MUTEX_CREATE;
+ } else {
+ hnd->iface_data = iface_data;
+
+ err = hnd->iface_hnd->gen_ioctl(hnd, E502_CM4_CMD_GET_MODULE_INFO, 0, NULL, 0, &lboot_info,
+ sizeof(lboot_info), NULL, 0);
+
+ if (err == X502_ERR_OK) {
+ if (strcmp(lboot_info.devname, devrec->devname)) {
+ err = X502_ERR_INVALID_DEVICE;
+ } else {
+ e502_devinfo_init(&hnd->info, &lboot_info);
+ err = e502_fill_devflags(hnd);
+ }
+ }
+ }
+
+ if (err != X502_ERR_OK) {
+ if (iface_data->ioctl_mutex != OSSPEC_INVALID_MUTEX)
+ osspec_mutex_destroy(iface_data->ioctl_mutex);
+ if (iface_data->data_mutex != OSSPEC_INVALID_MUTEX)
+ osspec_mutex_destroy(iface_data->data_mutex);
+ hnd->iface_data = NULL;
+ free(iface_data);
+ }
+ }
+ }
+
+ if ((err != X502_ERR_OK) && (s != INVALID_SOCKET)) {
+ closesocket(s);
+ }
+
+ return err;
+}
+
+static int32_t f_iface_close(t_x502_hnd hnd) {
+ int32_t err = X502_ERR_OK;
+ t_tcp_iface_data *tcp_data = (t_tcp_iface_data*)hnd->iface_data;
+ if (tcp_data != NULL) {
+ if (tcp_data->data_sock!=INVALID_SOCKET) {
+ closesocket(tcp_data->data_sock);
+ tcp_data->data_sock = INVALID_SOCKET;
+ }
+
+ if (tcp_data->cmd_sock!=INVALID_SOCKET) {
+ closesocket(tcp_data->cmd_sock);
+ tcp_data->cmd_sock = INVALID_SOCKET;
+ }
+
+ if (tcp_data->ioctl_mutex != OSSPEC_INVALID_MUTEX) {
+ osspec_mutex_destroy(tcp_data->ioctl_mutex);
+ tcp_data->ioctl_mutex = OSSPEC_INVALID_MUTEX;
+ }
+
+ if (tcp_data->data_mutex != OSSPEC_INVALID_MUTEX) {
+ osspec_mutex_destroy(tcp_data->data_mutex);
+ tcp_data->data_mutex = OSSPEC_INVALID_MUTEX;
+ }
+
+
+ free(hnd->iface_data);
+ hnd->iface_data = NULL;
+ }
+ return err;
+}
+
+static int32_t f_iface_stream_cfg(t_x502_hnd hnd, uint32_t ch, t_x502_stream_ch_params *params) {
+ int32_t err = X502_ERR_OK;
+
+ t_tcp_iface_data *tcp_data = (t_tcp_iface_data*)hnd->iface_data;
+ err = osspec_mutex_lock(tcp_data->data_mutex, X502_MUTEX_TCP_DATA_LOCK_TOUT);
+ if (err == X502_ERR_OK) {
+ if (tcp_data->data_sock == INVALID_SOCKET) {
+ err = f_iface_gen_ioctl(hnd, E502_CM4_CMD_DROP_DATA_CON, 0, NULL, 0, NULL, 0, NULL, 0);
+ if (err == X502_ERR_OK) {
+ err = f_con_sock(&tcp_data->data_sock, tcp_data->ip_addr, tcp_data->data_port, tcp_data->open_tout);
+ }
+
+ if (err == X502_ERR_OK) {
+ int flag = 1;
+ if (setsockopt(tcp_data->data_sock, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag))==SOCKET_ERROR)
+ err = X502_ERR_SOCKET_OPEN;
+ }
+ }
+
+ if (err == X502_ERR_OK) {
+ unsigned buf_size = params->buf_size*4;
+ if (ch==X502_STREAM_CH_IN) {
+ tcp_data->recv_part_size = 0;
+ if (setsockopt(tcp_data->data_sock, SOL_SOCKET, SO_RCVBUF, (char*)&buf_size, sizeof(buf_size))==SOCKET_ERROR) {
+ err = X502_ERR_SOCKET_SET_BUF_SIZE;
+ }
+ else {
+ dprintf("set SO_RCVBUF to %d\n", buf_size);
+ }
+#ifndef _WIN32
+ setsockopt(tcp_data->data_sock, SOL_SOCKET, SO_RCVBUFFORCE, (char*)&buf_size, sizeof(buf_size));
+#endif
+ socklen_t opt_len = sizeof(buf_size);
+ if (getsockopt(tcp_data->data_sock, SOL_SOCKET, SO_RCVBUF, (char*)&buf_size, &opt_len) != SOCKET_ERROR) {
+ dprintf("get SO_RCVBUF = %d\n", buf_size);
+ }
+#ifdef _WIN32
+ else {
+ dprintf("getsockopt error = %d\n", WSAGetLastError());
+ }
+#endif
+ } else {
+ tcp_data->send_part_size = 0;
+ if (setsockopt(tcp_data->data_sock, SOL_SOCKET, SO_SNDBUF, (char*)&buf_size, sizeof(buf_size))==SOCKET_ERROR) {
+ err = X502_ERR_SOCKET_SET_BUF_SIZE;
+ }
+ }
+ }
+
+ if (err == X502_ERR_OK) {
+ tcp_data->data_chs_en |= (1UL << ch);
+ }
+
+ osspec_mutex_release(tcp_data->data_mutex);
+ }
+
+ return err;
+}
+
+static int32_t f_iface_stream_start(t_x502_hnd hnd, uint32_t ch, uint32_t flags) {
+ int32_t err = 0;
+
+ if (!err && !(flags & X502_STREAM_FLAG_NO_REQUEST)) {
+ err = f_iface_gen_ioctl(hnd, E502_CM4_CMD_STREAM_START, (ch<<16),
+ NULL, 0, NULL, 0, NULL, 0);
+ }
+ return err;
+}
+
+static int32_t f_iface_stream_stop(t_x502_hnd hnd, uint32_t ch, uint32_t flags) {
+ int32_t err = 0;
+
+ if (!(flags & X502_STREAM_FLAG_NO_REQUEST)) {
+ int32_t running;
+
+ err = hnd->iface_hnd->stream_running(hnd, ch, &running);
+ if (!err && running) {
+ err = f_iface_gen_ioctl(hnd, E502_CM4_CMD_STREAM_STOP, (ch << 16),
+ NULL, 0, NULL, 0, NULL, 0);
+ }
+ }
+ return err;
+}
+
+static int32_t f_iface_stream_free(t_x502_hnd hnd, uint32_t ch, uint32_t flags) {
+ t_tcp_iface_data *tcp_data = (t_tcp_iface_data *)hnd->iface_data;
+ int32_t err = osspec_mutex_lock(tcp_data->data_mutex, X502_MUTEX_TCP_DATA_LOCK_TOUT);
+ if (err == X502_ERR_OK) {
+ err = hnd->iface_hnd->stream_stop(hnd, ch, flags);
+ if (err == X502_ERR_OK) {
+ tcp_data->data_chs_en &= ~(1UL << ch);
+
+ if ((tcp_data->data_chs_en == 0) && (tcp_data->data_sock != INVALID_SOCKET)) {
+ closesocket(tcp_data->data_sock);
+ tcp_data->data_sock = INVALID_SOCKET;
+ }
+ }
+ osspec_mutex_release(tcp_data->data_mutex);
+ }
+ return err;
+}
+
+static int32_t f_iface_stream_read(t_x502_hnd hnd, uint32_t *buf, uint32_t size, uint32_t tout) {
+ t_tcp_iface_data *tcp_data = (t_tcp_iface_data*)hnd->iface_data;
+ int32_t recvd;
+ t_ltimer tmr;
+
+ if (tcp_data->data_sock == INVALID_SOCKET) {
+ recvd = X502_ERR_NO_DATA_CONNECTION;
+ } else {
+ ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
+
+ if (tcp_data->recv_part_size != 0) {
+ buf[0] = tcp_data->recv_part_wrd;
+ }
+ recvd = f_recv(tcp_data->data_sock, (uint8_t*)buf + tcp_data->recv_part_size,
+ size *sizeof(buf[0]) - tcp_data->recv_part_size, &tmr);
+ if (recvd > 0) {
+ recvd += tcp_data->recv_part_size;
+ tcp_data->recv_part_size = recvd % sizeof(buf[0]);
+ recvd /= sizeof(buf[0]);
+ if (tcp_data->recv_part_size!=0) {
+ tcp_data->recv_part_wrd = buf[recvd];
+ }
+ }
+ }
+ return recvd;
+}
+
+static int32_t f_iface_stream_write(t_x502_hnd hnd, const uint32_t *buf, uint32_t size, uint32_t tout) {
+ int32_t sent = 0;
+ t_ltimer tmr;
+ t_tcp_iface_data *tcp_data = (t_tcp_iface_data*)hnd->iface_data;
+
+ if (tcp_data->data_sock == INVALID_SOCKET) {
+ sent = X502_ERR_NO_DATA_CONNECTION;
+ } else {
+ ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
+
+ /* проверяем, не осталось ли не переданного некратного слова => если осталось
+ * то пробуем сперва дослать его */
+ if (tcp_data->send_part_size!=0) {
+ sent = f_send(tcp_data->data_sock, (uint8_t*)&tcp_data->send_part_wrd,
+ tcp_data->send_part_size, &tmr);
+ if (sent >= 0) {
+ tcp_data->send_part_size -= (uint8_t)sent;
+ if (tcp_data->send_part_size != 0) {
+ tcp_data->send_part_wrd >>= 8*sent;
+ }
+ sent = 0;
+ }
+ }
+
+ /* новые данные пересылаем только если старое неполное слово точно ушло */
+ if ((sent == 0) && (tcp_data->send_part_size==0)) {
+ sent = f_send(tcp_data->data_sock, (uint8_t*)buf, size * sizeof(buf[0]), &tmr);
+ if (sent >= 0) {
+ /* если не полностью передали последнее слово, то нужно сохранить
+ * остаток слова, чтобы потом передать его */
+ tcp_data->send_part_size = sent % sizeof(buf[0]);
+ sent /= sizeof(buf[0]);
+ if (tcp_data->send_part_size!=0) {
+ tcp_data->send_part_wrd = buf[sent] >> (8*tcp_data->send_part_size);
+ tcp_data->send_part_size = sizeof(buf[0]) - tcp_data->send_part_size;
+ sent++;
+ }
+ }
+ }
+ }
+
+ return sent;
+}
+
+static int32_t f_iface_stream_get_rdy_cnt(t_x502_hnd hnd, uint32_t ch, uint32_t *rdy_cnt) {
+ int32_t err = 0;
+ t_tcp_iface_data *tcp_data = (t_tcp_iface_data *)hnd->iface_data;
+#ifdef _WIN32
+ if (ch == X502_STREAM_CH_IN) {
+ u_long val;
+ if (ioctlsocket(tcp_data->data_sock, FIONREAD, &val) == SOCKET_ERROR) {
+ err = X502_ERR_IOCTL_FAILD;
+ } else {
+ *rdy_cnt = val/4;
+ }
+ } else {
+ err = X502_ERR_NOT_IMPLEMENTED;
+ }
+#else
+ if (ch == X502_STREAM_CH_IN) {
+ int val;
+ if (ioctl(tcp_data->data_sock, SIOCINQ, &val)==-1) {
+ err = X502_ERR_IOCTL_FAILD;
+ } else {
+ *rdy_cnt = val/4;
+ }
+ } else {
+ err = X502_ERR_NOT_IMPLEMENTED;
+#if 0
+ /* Данный вариант в реальности не работает корректно */
+ int buf_len, val;
+ socklen_t optlen = sizeof(buf_len);
+ if (getsockopt(tcp_data->data_sock, SOL_SOCKET, SO_SNDBUF, (char*)&buf_len, &optlen)==SOCKET_ERROR) {
+ err = X502_ERR_IOCTL_FAILD;
+ } else if (ioctl(tcp_data->data_sock, SIOCOUTQ, &val)==-1) {
+ err = X502_ERR_IOCTL_FAILD;
+ } else {
+ *rdy_cnt = (buf_len - val)/4;
+ }
+#endif
+ }
+#endif
+ return err;
+}
+
+
+int32_t e502_make_tcp_rec(t_x502_devrec *devrec, uint32_t flags, uint32_t tout, char const *devname) {
+ int32_t err = (devrec == NULL) ? X502_ERR_INVALID_DEVICE_RECORD : X502_ERR_OK;
+
+ X502_DevRecordInit(devrec);
+
+ if (err==X502_ERR_OK) {
+ t_tcp_devinfo_data *devinfo_data = malloc(sizeof(t_tcp_devinfo_data));
+ t_x502_devrec_inptr *devinfo_ptr = malloc(sizeof(t_x502_devrec_inptr));
+
+ if ((devinfo_data==NULL) || (devinfo_ptr == NULL)) {
+ err = X502_ERR_MEMORY_ALLOC;
+ } else {
+ strcpy(devrec->devname, devname);
+ devinfo_data->cmd_port = E502_TCP_DEFAULT_CMD_PORT;
+ devinfo_data->data_port = E502_TCP_DEFAULT_DATA_PORT;
+ devinfo_data->open_tout = tout;
+ devinfo_data->flags = flags;
+ devinfo_ptr->iface = &f_tcp_iface;
+ devinfo_ptr->iface_data = devinfo_data;
+
+
+ devrec->internal = devinfo_ptr;
+ devrec->iface = X502_IFACE_ETH;
+ devrec->flags = X502_DEVFLAGS_IFACE_SUPPORT_USB | X502_DEVFLAGS_IFACE_SUPPORT_ETH;
+ }
+
+ if (err != X502_ERR_OK) {
+ free(devinfo_data);
+ free(devinfo_ptr);
+ }
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_MakeDevRecordByIpAddr2(t_x502_devrec *devrec, uint32_t ip_addr,
+ uint32_t flags, uint32_t tout, char const *devname) {
+ int32_t err = e502_make_tcp_rec(devrec, flags, tout, devname);
+ if (err == X502_ERR_OK) {
+ t_tcp_devinfo_data *devinfo_data = (t_tcp_devinfo_data *)devrec->internal->iface_data;
+ devinfo_data->ip_addr = ip_addr;
+#ifdef ENABLE_DNSSD
+ devinfo_data->svc_rec = NULL;
+#endif
+
+ sprintf(devrec->location, "%d.%d.%d.%d",
+ (ip_addr>>24) & 0xFF,
+ (ip_addr>>16) & 0xFF,
+ (ip_addr>>8) & 0xFF,
+ (ip_addr>>0) & 0xFF);
+ devrec->location_type = X502_LOCATION_TYPE_ADDR;
+
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_MakeDevRecordByIpAddr(t_x502_devrec *devrec, uint32_t ip_addr,
+ uint32_t flags, uint32_t tout) {
+ return E502_MakeDevRecordByIpAddr2(devrec, ip_addr, flags, tout, E502_DEVICE_NAME);
+}
+
+X502_EXPORT(int32_t) E502_EthDevRecordSetCmdPort(t_x502_devrec *devrec, uint16_t cmd_port) {
+ int32_t err = ((devrec == NULL) || (devrec->internal->iface != &f_tcp_iface)) ?
+ X502_ERR_INVALID_DEVICE_RECORD : X502_ERR_OK;
+ if (err == X502_ERR_OK) {
+ t_tcp_devinfo_data *devinfo_data = (t_tcp_devinfo_data *)devrec->internal->iface_data;
+ devinfo_data->cmd_port = cmd_port;
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_EthDevRecordSetDataPort(t_x502_devrec *devrec, uint16_t data_port) {
+ int32_t err = ((devrec == NULL) || (devrec->internal->iface != &f_tcp_iface)) ?
+ X502_ERR_INVALID_DEVICE_RECORD : X502_ERR_OK;
+ if (err == X502_ERR_OK) {
+ t_tcp_devinfo_data *devinfo_data = (t_tcp_devinfo_data *)devrec->internal->iface_data;
+ devinfo_data->data_port = data_port;
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E16_OpenByIpAddr(t_x502_hnd hnd, uint32_t ip_addr, uint32_t flags, uint32_t tout) {
+ int32_t err = X502_CHECK_HND(hnd);
+ if (err == X502_ERR_OK) {
+ t_x502_devrec devinfo;
+ err = E502_MakeDevRecordByIpAddr2(&devinfo, ip_addr, flags, tout, E16_DEVICE_NAME);
+ if (err == X502_ERR_OK) {
+ err = X502_OpenByDevRecord(hnd, &devinfo);
+
+ X502_FreeDevRecordList(&devinfo, 1);
+ }
+ }
+ return err;
+}
+
+X502_EXPORT(int32_t) E502_OpenByIpAddr(t_x502_hnd hnd, uint32_t ip_addr, uint32_t flags, uint32_t tout) {
+ int32_t err = X502_CHECK_HND(hnd);
+ if (err == X502_ERR_OK) {
+ t_x502_devrec devinfo;
+ err = E502_MakeDevRecordByIpAddr(&devinfo, ip_addr, flags, tout);
+ if (err == X502_ERR_OK) {
+ err = X502_OpenByDevRecord(hnd, &devinfo);
+
+ X502_FreeDevRecordList(&devinfo, 1);
+ }
+ }
+ return err;
+}
+
+
+X502_EXPORT(int32_t) E502_GetIpAddr(t_x502_hnd hnd, uint32_t *ip_addr) {
+ int32_t err = X502_CHECK_HND_OPENED(hnd);
+ if (err == X502_ERR_OK) {
+ if (hnd->iface != X502_IFACE_ETH) {
+ err = X502_ERR_INVALID_OP_FOR_IFACE;
+ } else {
+ t_tcp_iface_data *tcp_data = (t_tcp_iface_data *)hnd->iface_data;
+ *ip_addr = tcp_data->ip_addr;
+ }
+ }
+ return err;
+}
+#else
+#include "e502api.h"
+X502_EXPORT(int32_t) E502_MakeDevRecordByIpAddr(t_x502_devrec *devrec, uint32_t ip_addr,
+ uint32_t flags, uint32_t tout) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+
+X502_EXPORT(int32_t) E502_EthDevRecordSetCmdPort(t_x502_devrec *devrec, uint16_t cmd_port) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+
+X502_EXPORT(int32_t) E502_EthDevRecordSetDataPort(t_x502_devrec *devrec, uint16_t data_port) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+
+
+X502_EXPORT(int32_t) E502_OpenByIpAddr(t_x502_hnd hnd, uint32_t ip_addr, uint32_t flags, uint32_t tout) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+
+
+X502_EXPORT(int32_t) E502_GetIpAddr(t_x502_hnd hnd, uint32_t *ip_addr) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+#endif
diff --git a/e502/e502api_tcp_private.h b/e502/e502api_tcp_private.h
new file mode 100644
index 0000000..152532e
--- /dev/null
+++ b/e502/e502api_tcp_private.h
@@ -0,0 +1,21 @@
+#ifndef E502API_TCP_PRIVATE_H
+#define E502API_TCP_PRIVATE_H
+
+#include "e502api_private.h"
+
+typedef struct {
+ uint16_t cmd_port;
+ uint16_t data_port;
+ uint32_t ip_addr;
+ uint32_t open_tout;
+ uint32_t flags;
+#ifdef ENABLE_DNSSD
+ t_e502_eth_svc_record_hnd svc_rec;
+#endif
+} t_tcp_devinfo_data;
+
+int32_t e502_make_tcp_rec(t_x502_devrec *devrec, uint32_t flags, uint32_t tout, char const *devname);
+int32_t e502_svc_fill_devinfo(t_tcp_devinfo_data *data);
+
+#endif // E502API_TCP_PRIVATE_H
+
diff --git a/e502/e502api_usb.c b/e502/e502api_usb.c
new file mode 100644
index 0000000..6f3d720
--- /dev/null
+++ b/e502/e502api_usb.c
@@ -0,0 +1,1153 @@
+#ifdef ENABLE_USB
+#include "libusb-1.0/libusb.h"
+#include "e502api_private.h"
+#include "lboot_req.h"
+#include "e502_fpga_regs.h"
+#include "osspec.h"
+#include "ltimer.h"
+
+#include
+#include
+
+#define E502_USB_VID 0x2A52
+#define E502_USB_PID 0xE502
+#define E502_USB_REQ_TOUT 3000
+
+#define E440_USB_VID 0x0471
+#define E440_USB_PID 0x0440
+
+#define E16_USB_VID 0x2A52
+#define E16_USB_PID 0x0E16
+
+#define USB_CTL_REQ_MAX_SIZE 512
+
+
+#define MUTEX_STREAM_LOCK_TOUT 2000
+
+
+#define STREAM_STOP_TOUT 1000
+
+#define USB_IN_STREAM_BUF_MIN 64
+
+
+#define USB_BULK_RX_MAX_TRANSF_CNT 20
+#define USB_BULK_RX_MAX_TRANSF_SIZE (32*1024)
+#define USB_BULK_RX_TRANSFER_TOUT -1
+
+#define USB_BULK_TX_MAX_TRANSF_CNT 20
+#define USB_BULK_TX_MAX_TRANSF_SIZE (32*1024)
+#define USB_BULK_TX_TRANSFER_TOUT -1
+
+#ifndef LIBUSB_CALL
+ #define LIBUSB_CALL
+#endif
+
+static int f_lusb_init_done = 0;
+
+
+struct st_transf_info;
+
+
+typedef enum {
+ TRANSF_CPL_BUSY = 0,
+ TRANSF_CPL_COMPLETED = 1,
+ TRANSF_CPL_IDLE = 2
+} t_transf_state;
+
+typedef struct {
+ uint32_t *addr;
+ uint32_t size;
+ uint32_t transf_pos;
+ volatile enum {
+ RX_STATE_IDLE,
+ RX_STATE_BUSY,
+ RX_STATE_RDY,
+ RX_STATE_ERR
+ } state;
+} t_rx_cpl_part; /* результат выполнения запросов */
+
+typedef struct st_transf_info {
+ t_x502_hnd dev;
+ uint8_t addr; /* адрес конечной точки */
+ uint16_t pkt_size;
+ t_mutex mutex;
+ t_thread thread;
+ t_event user_wake_evt;
+ t_event usb_wake_evt;
+
+ volatile int stop_req;
+ int usb_rdy;
+ uint32_t *data; /* промежуточный буфер, используемый во всех запросах */
+ unsigned buf_size;
+
+
+ int err;
+
+ union {
+ struct {
+ t_rx_cpl_part *cpls;
+ uint32_t cpl_cnt;
+ uint32_t cpl_get_pos;
+ uint32_t transf_size;
+ uint32_t buf_get_rdy;
+ } rx;
+ struct {
+ uint32_t buf_pos_put;
+ uint32_t buf_put_rdy;
+ uint32_t busy_size;
+ } tx;
+ };
+} t_transf_info;
+
+
+typedef struct {
+ libusb_device_handle *devhnd;
+ t_transf_info streams[X502_STREAM_CH_CNT];
+} t_usb_iface_data;
+
+
+static int32_t f_ioreq(libusb_device_handle *handle, uint32_t cmd_code, uint32_t param,
+ const void* snd_data, uint32_t snd_size,
+ void* rcv_data, uint32_t recv_size, uint32_t* recvd_size, uint32_t tout);
+
+static int32_t f_iface_free_devinfo_ptr(t_x502_devrec_inptr *devinfo_ptr);
+static int32_t f_iface_open(t_x502_hnd hnd, const t_x502_devrec *devrec);
+static int32_t f_iface_close(t_x502_hnd hnd);
+static int32_t f_iface_stream_cfg(t_x502_hnd hnd, uint32_t ch, t_x502_stream_ch_params *params);
+static int32_t f_iface_stream_start(t_x502_hnd hnd, uint32_t ch, uint32_t flags);
+static int32_t f_iface_stream_stop(t_x502_hnd hnd, uint32_t ch, uint32_t flags);
+static int32_t f_iface_stream_free(t_x502_hnd hnd, uint32_t ch, uint32_t flags);
+static int32_t f_iface_stream_read(t_x502_hnd hnd, uint32_t *buf, uint32_t size, uint32_t tout);
+static int32_t f_iface_stream_write(t_x502_hnd hnd, const uint32_t *buf, uint32_t size, uint32_t tout) ;
+static int32_t f_iface_stream_get_rdy_cnt(t_x502_hnd hnd, uint32_t ch, uint32_t *rdy_cnt);
+
+static int32_t f_iface_gen_ioctl(t_x502_hnd hnd, uint32_t cmd_code, uint32_t param,
+ const void* snd_data, uint32_t snd_size,
+ void* rcv_data, uint32_t recv_size, uint32_t* recvd_size,
+ uint32_t tout);
+
+static const t_x502_dev_iface f_usb_iface = {
+ E502_REGS_ARM_HARD_ID,
+ USB_IN_STREAM_BUF_MIN,
+ USB_CTL_REQ_MAX_SIZE,
+ USB_CTL_REQ_MAX_SIZE/4,
+ USB_CTL_REQ_MAX_SIZE, //flash rd size
+ USB_CTL_REQ_MAX_SIZE, //flash wr size
+ f_iface_free_devinfo_ptr,
+ f_iface_open,
+ f_iface_close,
+ e502_iface_fpga_read,
+ e502_iface_fpga_write,
+ f_iface_stream_cfg,
+ f_iface_stream_start,
+ f_iface_stream_stop,
+ f_iface_stream_free,
+ e502_iface_stream_running,
+ f_iface_stream_read,
+ f_iface_stream_write,
+ f_iface_stream_get_rdy_cnt,
+ e502_iface_bf_mem_block_rd,
+ e502_iface_bf_mem_block_wr,
+ e502_iface_bf_firm_load,
+ e502_iface_flash_rd,
+ e502_iface_flash_wr,
+ e502_iface_flash_erase,
+ e502_iface_flash_set_prot,
+ e502_iface_reload_dev_info,
+ e502_iface_cycle_load_start,
+ e502_iface_cycle_setup,
+ e502_iface_cycle_stop,
+ e502_iface_cycle_check_setup,
+ e502_iface_fpga_mode_init,
+ f_iface_gen_ioctl,
+ e502_iface_check_feature
+};
+
+
+
+
+
+
+static int32_t f_iface_open(t_x502_hnd hnd, const t_x502_devrec *devrec) {
+ int32_t err, usberr;
+ libusb_device *dev = (libusb_device *)devrec->internal->iface_data;
+ libusb_device_handle *devhnd = NULL;
+
+
+ usberr = libusb_open(dev, &devhnd);
+ if (!usberr) {
+ usberr = libusb_claim_interface(devhnd, 0);
+ }
+
+ err = usberr == LIBUSB_SUCCESS ? X502_ERR_OK :
+ usberr == LIBUSB_ERROR_BUSY ? X502_ERR_ALREADY_OPENED :
+ usberr == LIBUSB_ERROR_ACCESS ? X502_ERR_DEVICE_ACCESS_DENIED :
+ usberr == LIBUSB_ERROR_NO_DEVICE ? X502_ERR_DEVICE_DISCONNECTED :
+ X502_ERR_DEVICE_OPEN;
+
+ if (err == X502_ERR_OK) {
+ t_lboot_devinfo lboot_info;
+
+ err = f_ioreq(devhnd, E502_CM4_CMD_GET_MODULE_INFO, 0, NULL, 0, &lboot_info,
+ sizeof(lboot_info), NULL, 0);
+ if (err == X502_ERR_OK) {
+ if (strcmp(lboot_info.devname, devrec->devname)) {
+ err = X502_ERR_INVALID_DEVICE;
+ } else {
+ e502_devinfo_init(&hnd->info, &lboot_info);
+ }
+ }
+ }
+
+ if (err == X502_ERR_OK) {
+ t_usb_iface_data *usb_data = calloc(1, sizeof(t_usb_iface_data));
+ unsigned stream;
+ struct libusb_config_descriptor* cfg;
+
+ usb_data->devhnd = devhnd;
+
+ /* Чтобы определить номера используемых конечных точек, получаем
+ * конфигурацию устройства и находим по одной конечной точки типа
+ * bulk на каждое направление (in/out) */
+ usberr = libusb_get_config_descriptor(dev, 0, &cfg);
+ if (!usberr) {
+ int intf_idx, s, fnd_in=0, fnd_out=0;
+ for (intf_idx = 0; intf_idx < cfg->bNumInterfaces; intf_idx++) {
+ for (s=0; s < cfg->interface[intf_idx].num_altsetting; s++) {
+ int e;
+ for (e=0; e < cfg->interface[intf_idx].altsetting[s].bNumEndpoints; e++) {
+ const struct libusb_endpoint_descriptor *ep_descr =
+ &cfg->interface[intf_idx].altsetting[s].endpoint[e];
+ if ((ep_descr->bmAttributes & 0x3)==
+ LIBUSB_TRANSFER_TYPE_BULK ) {
+ if ((ep_descr->bEndpointAddress & 0x80)
+ == LIBUSB_ENDPOINT_IN) {
+ if (!fnd_in) {
+ usb_data->streams[X502_STREAM_CH_IN].addr = ep_descr->bEndpointAddress;
+ usb_data->streams[X502_STREAM_CH_IN].pkt_size = ep_descr->wMaxPacketSize;
+ fnd_in = 1;
+ }
+ } else {
+ if (!fnd_out) {
+ usb_data->streams[X502_STREAM_CH_OUT].addr = ep_descr->bEndpointAddress;
+ usb_data->streams[X502_STREAM_CH_OUT].pkt_size = ep_descr->wMaxPacketSize;
+ fnd_out = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ libusb_free_config_descriptor(cfg);
+ if (!fnd_in || !fnd_out)
+ err = X502_ERR_INVALID_USB_CONFIGURATION;
+ } else {
+ err = X502_ERR_INVALID_USB_CONFIGURATION;
+ }
+
+ for (stream=0; stream < X502_STREAM_CH_CNT; stream++) {
+ usb_data->streams[stream].mutex = osspec_mutex_create();
+ usb_data->streams[stream].user_wake_evt = osspec_event_create(0);
+ usb_data->streams[stream].usb_wake_evt = osspec_event_create(0);
+ usb_data->streams[stream].dev = hnd;
+ usb_data->streams[stream].thread = OSSPEC_INVALID_THREAD;
+
+ if ((usb_data->streams[stream].mutex == OSSPEC_INVALID_MUTEX) ||
+ (usb_data->streams[stream].user_wake_evt == OSSPEC_INVALID_EVENT) ||
+ (usb_data->streams[stream].usb_wake_evt == OSSPEC_INVALID_EVENT)) {
+ err = X502_ERR_MUTEX_CREATE;
+ }
+ }
+
+ if (err == X502_ERR_OK) {
+ hnd->iface_data = usb_data;
+ } else {
+ for (stream=0; stream < X502_STREAM_CH_CNT; stream++) {
+ if (usb_data->streams[stream].mutex != OSSPEC_INVALID_MUTEX)
+ osspec_mutex_destroy(usb_data->streams[stream].mutex);
+ if (usb_data->streams[stream].user_wake_evt != OSSPEC_INVALID_EVENT)
+ osspec_event_destroy(usb_data->streams[stream].user_wake_evt);
+ if (usb_data->streams[stream].usb_wake_evt != OSSPEC_INVALID_EVENT)
+ osspec_event_destroy(usb_data->streams[stream].usb_wake_evt);
+ }
+ free(usb_data);
+ }
+ }
+
+ if (err != X502_ERR_OK) {
+ if (devhnd != NULL) {
+ libusb_release_interface(devhnd, 0);
+ libusb_close(devhnd);
+ }
+ }
+
+ return err;
+}
+
+static int32_t f_iface_close(t_x502_hnd hnd) {
+ t_usb_iface_data *usb_data = (t_usb_iface_data*)hnd->iface_data;
+ int32_t err = 0;
+ if (usb_data != NULL) {
+ unsigned ch;
+
+ for (ch=0; ch < sizeof(usb_data->streams)/sizeof(usb_data->streams[0]); ch++) {
+ if (usb_data->streams[ch].mutex != OSSPEC_INVALID_MUTEX) {
+ osspec_mutex_destroy(usb_data->streams[ch].mutex);
+ usb_data->streams[ch].mutex = OSSPEC_INVALID_MUTEX;
+ }
+ if (usb_data->streams[ch].user_wake_evt != OSSPEC_INVALID_EVENT) {
+ osspec_event_destroy(usb_data->streams[ch].user_wake_evt);
+ usb_data->streams[ch].user_wake_evt = OSSPEC_INVALID_EVENT;
+ }
+ if (usb_data->streams[ch].usb_wake_evt != OSSPEC_INVALID_EVENT) {
+ osspec_event_destroy(usb_data->streams[ch].usb_wake_evt);
+ usb_data->streams[ch].usb_wake_evt = OSSPEC_INVALID_EVENT;
+ }
+ }
+
+ libusb_release_interface(usb_data->devhnd, 0);
+ libusb_close(usb_data->devhnd);
+
+
+ free(hnd->iface_data);
+ hnd->iface_data = NULL;
+ }
+ return err;
+}
+
+
+static int32_t f_iface_free_devinfo_ptr(t_x502_devrec_inptr *devinfo_ptr) {
+ libusb_device *dev = (libusb_device*)devinfo_ptr->iface_data;
+ libusb_unref_device(dev);
+ free(devinfo_ptr);
+ return X502_ERR_OK;
+}
+
+
+
+static void LIBUSB_CALL f_usb_transf_cb(struct libusb_transfer *transfer) {
+ *((int*)transfer->user_data) = TRANSF_CPL_COMPLETED;
+}
+
+
+static OSSPEC_THREAD_FUNC_RET OSSPEC_THREAD_FUNC_CALL f_usb_rx_thread_func(void *arg) {
+ t_usb_iface_data *usb_data = (t_usb_iface_data*)arg;
+ struct libusb_transfer *transfers[USB_BULK_RX_MAX_TRANSF_CNT]; /* запросы usb */
+ volatile int transf_completed[USB_BULK_RX_MAX_TRANSF_CNT];
+ t_transf_info *info = &usb_data->streams[X502_STREAM_CH_IN];
+ unsigned transf_pos=0;
+ unsigned cpl_pos = 0;
+ unsigned transf_check_pos=0;
+ unsigned cpl_check_pos=0;
+
+ unsigned idx;
+ int err = 0;
+ info->err = 0;
+
+ for (idx = 0; idx < USB_BULK_RX_MAX_TRANSF_CNT; idx++) {
+ transfers[idx] = libusb_alloc_transfer(0);
+ transf_completed[idx] = TRANSF_CPL_IDLE;
+ if (transfers[idx]==NULL) {
+ err = X502_ERR_MEMORY_ALLOC;
+ }
+ }
+
+ while (!err && !info->stop_req) {
+ /* обрабатываем завершенные передачи в порядке добавления */
+ while (transf_completed[transf_check_pos] == TRANSF_CPL_COMPLETED) {
+ t_rx_cpl_part *cpl = &info->rx.cpls[cpl_check_pos];
+ struct libusb_transfer *transfer = transfers[transf_check_pos];
+
+ osspec_mutex_lock(info->mutex, MUTEX_STREAM_LOCK_TOUT);
+ if ((transfer->status == LIBUSB_TRANSFER_COMPLETED)
+ || (transfer->status == LIBUSB_TRANSFER_TIMED_OUT)
+ || (transfer->status == LIBUSB_TRANSFER_CANCELLED)) {
+ cpl->size = transfer->actual_length/sizeof(uint32_t);
+ info->rx.buf_get_rdy += cpl->size;
+ cpl->state = RX_STATE_RDY;
+ } else {
+ cpl->state = RX_STATE_ERR;
+ }
+ transf_completed[transf_check_pos] = TRANSF_CPL_IDLE;
+ osspec_event_set(info->user_wake_evt);
+
+ if (++transf_check_pos == USB_BULK_RX_MAX_TRANSF_CNT)
+ transf_check_pos = 0;
+ if (++cpl_check_pos == info->rx.cpl_cnt)
+ cpl_check_pos = 0;
+
+ osspec_mutex_release(info->mutex);
+ }
+
+
+ /* пробуем запустить новые трансферы на передачу */
+ while ((transf_completed[transf_pos] == TRANSF_CPL_IDLE) &&
+ (info->rx.cpls[cpl_pos].state == RX_STATE_IDLE) && !err) {
+
+ info->rx.cpls[cpl_pos].addr = &info->data[info->rx.transf_size*cpl_pos];
+ libusb_fill_bulk_transfer(transfers[transf_pos], usb_data->devhnd, info->addr,
+ (unsigned char*)info->rx.cpls[cpl_pos].addr,
+ info->rx.transf_size*sizeof(info->data[0]),
+ f_usb_transf_cb, (void*)&transf_completed[transf_pos],
+ USB_BULK_RX_TRANSFER_TOUT);
+ info->rx.cpls[cpl_pos].state = RX_STATE_BUSY;
+ transf_completed[transf_pos] = TRANSF_CPL_BUSY;
+
+ if (libusb_submit_transfer(transfers[transf_pos])==0) {
+ osspec_mutex_lock(info->mutex, MUTEX_STREAM_LOCK_TOUT);
+ osspec_mutex_release(info->mutex);
+ if (++transf_pos == USB_BULK_RX_MAX_TRANSF_CNT)
+ transf_pos = 0;
+ } else {
+ err = X502_ERR_RECV;
+ transf_completed[transf_pos] = TRANSF_CPL_IDLE;
+ info->rx.cpls[cpl_pos].state = RX_STATE_ERR;
+ }
+
+ if (++cpl_pos == info->rx.cpl_cnt)
+ cpl_pos = 0;
+ }
+
+ if (!err) {
+ int wt_buf_rdy = 0;
+
+
+ osspec_mutex_lock(info->mutex, MUTEX_STREAM_LOCK_TOUT);
+ if (info->rx.cpls[cpl_pos].state != RX_STATE_IDLE) {
+ wt_buf_rdy = 1;
+ osspec_event_clear(info->usb_wake_evt);
+ }
+ osspec_mutex_release(info->mutex);
+
+
+ if (transf_completed[transf_check_pos] != TRANSF_CPL_IDLE) {
+ int lusberr;
+ /* иначе ждем событий от USB */
+ struct timeval tv;
+ tv.tv_sec =0;
+ tv.tv_usec = 200 * 1000;
+ lusberr = libusb_handle_events_timeout_completed(NULL, &tv, (int*)&transf_completed[transf_check_pos]);
+
+ if ((lusberr != LIBUSB_SUCCESS) && (lusberr != LIBUSB_ERROR_INTERRUPTED)) {
+ err = X502_ERR_RECV;
+ }
+
+ } else if (wt_buf_rdy) {
+ /* если буфер занят - ждем события от потока чтения */
+ osspec_event_wait(info->usb_wake_evt, OSSPEC_TIMEOUT_INFINITY);
+ }
+ }
+ }
+
+ /* отменяем все поставленные запросы */
+ for (idx = 0; idx < USB_BULK_RX_MAX_TRANSF_CNT; idx++) {
+ unsigned pos = (transf_check_pos + idx) % USB_BULK_RX_MAX_TRANSF_CNT;
+ if (transf_completed[pos] == TRANSF_CPL_BUSY) {
+ if (libusb_cancel_transfer(transfers[pos]) !=LIBUSB_SUCCESS)
+ transf_completed[pos] = TRANSF_CPL_COMPLETED;
+ }
+ }
+
+ /* ждем завершения всех запросов */
+ while (transf_completed[transf_check_pos] != TRANSF_CPL_IDLE) {
+ if (transf_completed[transf_check_pos] == TRANSF_CPL_BUSY) {
+ struct timeval ztv = {0,0};
+ libusb_handle_events_timeout(NULL, &ztv);
+ } else {
+ transf_completed[transf_check_pos] = TRANSF_CPL_IDLE;
+ if (++transf_check_pos == USB_BULK_RX_MAX_TRANSF_CNT)
+ transf_check_pos = 0;
+ }
+ }
+
+ /* очищаем ресурсы */
+ for (idx = 0; idx < USB_BULK_RX_MAX_TRANSF_CNT; idx++) {
+ if (transfers[idx] != NULL) {
+ libusb_free_transfer(transfers[idx]);
+ }
+ }
+
+ if (info->err == X502_ERR_OK)
+ info->err = err;
+
+ return 0;
+}
+
+
+
+static OSSPEC_THREAD_FUNC_RET OSSPEC_THREAD_FUNC_CALL f_usb_tx_thread_func(void *arg) {
+ t_usb_iface_data *usb_data = (t_usb_iface_data*)arg;
+ struct libusb_transfer *transfers[USB_BULK_TX_MAX_TRANSF_CNT]; /* запросы usb */
+ t_transf_info *info = &usb_data->streams[X502_STREAM_CH_OUT];
+ volatile int transf_completed[USB_BULK_RX_MAX_TRANSF_CNT];
+ unsigned transf_pos=0;
+ unsigned transf_check_pos=0;
+ unsigned idx;
+ int err = 0;
+ int last_full_packet = 0; /* признак, что последний пакет был полный, чтобы
+ послять нулевой пакет */
+ unsigned snd_pos = 0;
+
+
+ info->err = 0;
+ info->tx.busy_size = 0;
+
+ for (idx = 0; (idx < USB_BULK_TX_MAX_TRANSF_CNT) && (err == X502_ERR_OK); idx++) {
+ transfers[idx] = libusb_alloc_transfer(0);
+ transf_completed[idx] = TRANSF_CPL_IDLE;
+ if (transfers[idx]==NULL) {
+ err = X502_ERR_MEMORY_ALLOC;
+ }
+ }
+
+ while (!err && !info->stop_req) {
+ uint32_t snd_rdy_size;
+ osspec_mutex_lock(info->mutex, MUTEX_STREAM_LOCK_TOUT);
+ snd_rdy_size = info->buf_size - info->tx.buf_put_rdy - info->tx.busy_size;
+
+ if (snd_rdy_size == 0)
+ osspec_event_clear(info->usb_wake_evt);
+ osspec_mutex_release(info->mutex);
+
+ /* обрабатываем завершенные передачи */
+ while (transf_completed[transf_check_pos] == TRANSF_CPL_COMPLETED) {
+ struct libusb_transfer *transfer = transfers[transf_check_pos];
+ if ((transfer->status != LIBUSB_TRANSFER_COMPLETED)
+ && (transfer->status != LIBUSB_TRANSFER_CANCELLED)) {
+ err = X502_ERR_SEND;
+ }
+
+
+ osspec_mutex_lock(info->mutex, MUTEX_STREAM_LOCK_TOUT);
+ info->tx.buf_put_rdy += transfer->length/sizeof(info->data[0]);
+ info->tx.busy_size -= transfer->length/sizeof(info->data[0]);
+ osspec_event_set(info->user_wake_evt);
+ osspec_mutex_release(info->mutex);
+
+ transf_completed[transf_check_pos] = TRANSF_CPL_IDLE;
+
+ if (++transf_check_pos == USB_BULK_RX_MAX_TRANSF_CNT)
+ transf_check_pos = 0;
+ }
+
+
+ while (((snd_rdy_size != 0) || (last_full_packet && (info->tx.busy_size==0)))
+ && (transf_completed[transf_pos] == TRANSF_CPL_IDLE)
+ && (err == X502_ERR_OK)) {
+ uint32_t snd_size = snd_rdy_size;
+ if (snd_size > USB_BULK_TX_MAX_TRANSF_SIZE)
+ snd_size = USB_BULK_TX_MAX_TRANSF_SIZE;
+ if (snd_size > (info->buf_size - snd_pos))
+ snd_size = info->buf_size - snd_pos;
+
+ libusb_fill_bulk_transfer(transfers[transf_pos], usb_data->devhnd, info->addr,
+ (unsigned char*)&info->data[snd_pos],
+ snd_size*sizeof(info->data[0]),
+ f_usb_transf_cb,
+ (void*)&transf_completed[transf_pos],
+ USB_BULK_TX_TRANSFER_TOUT);
+
+
+
+ transf_completed[transf_pos] = TRANSF_CPL_BUSY;
+ if (libusb_submit_transfer(transfers[transf_pos])==0) {
+ osspec_mutex_lock(info->mutex, MUTEX_STREAM_LOCK_TOUT);
+ info->tx.busy_size+=snd_size;
+ osspec_mutex_release(info->mutex);
+ if (++transf_pos == USB_BULK_TX_MAX_TRANSF_CNT)
+ transf_pos = 0;
+ snd_pos+=snd_size;
+ if (snd_pos==info->buf_size)
+ snd_pos = 0;
+ snd_rdy_size-=snd_size;
+
+ last_full_packet = (snd_size != 0) && ((snd_size % (info->pkt_size/sizeof(info->data[0]))) == 0);
+ } else {
+ transf_completed[transf_pos] = TRANSF_CPL_IDLE;
+ err = X502_ERR_SEND;
+ }
+ }
+
+
+ if (err == X502_ERR_OK) {
+ if (info->tx.busy_size!=0) {
+ /* иначе ждем событий от USB */
+ struct timeval tv;
+ tv.tv_sec =0;
+ tv.tv_usec = 200 * 1000;
+ libusb_handle_events_timeout_completed(NULL, &tv,
+ (int*)&transf_completed[transf_check_pos]);
+ } else if ((snd_rdy_size==0) && !last_full_packet) {
+ /* если буфер занят - ждем события от потока чтения */
+ osspec_event_wait(info->usb_wake_evt, OSSPEC_TIMEOUT_INFINITY);
+ }
+ }
+ }
+
+ /* отменяем все поставленные запросы */
+ for (idx = 0; idx < USB_BULK_RX_MAX_TRANSF_CNT; idx++) {
+ unsigned pos = (transf_check_pos + idx) % USB_BULK_RX_MAX_TRANSF_CNT;
+ if (transf_completed[pos] == TRANSF_CPL_BUSY) {
+ if (libusb_cancel_transfer(transfers[pos]) !=LIBUSB_SUCCESS)
+ transf_completed[pos] = TRANSF_CPL_COMPLETED;
+ }
+ }
+
+ /* ждем завершения всех запросов */
+ while (transf_completed[transf_check_pos] != TRANSF_CPL_IDLE) {
+ if (transf_completed[transf_check_pos] == TRANSF_CPL_BUSY) {
+ struct timeval ztv = {0,0};
+ libusb_handle_events_timeout(NULL, &ztv);
+ } else {
+ transf_completed[transf_check_pos] = TRANSF_CPL_IDLE;
+ if (++transf_check_pos == USB_BULK_RX_MAX_TRANSF_CNT)
+ transf_check_pos = 0;
+ }
+ }
+
+ /* очищаем ресурсы */
+ for (idx = 0; idx < USB_BULK_RX_MAX_TRANSF_CNT; idx++) {
+ if (transfers[idx] != NULL) {
+ libusb_free_transfer(transfers[idx]);
+ }
+ }
+
+ if (info->err == X502_ERR_OK)
+ info->err = err;
+
+
+ return 0;
+}
+
+
+static void f_stream_init(uint32_t ch, t_transf_info *info) {
+ info->stop_req = 0;
+ info->err = 0;
+ if (ch==X502_STREAM_CH_IN) {
+ unsigned i;
+ info->rx.cpl_get_pos = 0;
+ info->rx.buf_get_rdy = 0;
+
+
+ for (i=0; i < info->rx.cpl_cnt; i++) {
+ info->rx.cpls[i].state = RX_STATE_IDLE;
+ }
+ } else {
+ info->tx.buf_pos_put = 0;
+ info->tx.buf_put_rdy = info->buf_size;
+ }
+}
+
+static int32_t f_iface_stream_cfg(t_x502_hnd hnd, uint32_t ch, t_x502_stream_ch_params *params) {
+ t_usb_iface_data *usb_data = (t_usb_iface_data *)hnd->iface_data;
+ t_transf_info *info = &usb_data->streams[ch];
+ int err = 0;
+
+ /** @todo вариант изменения размеров буфера в работе */
+ if (info->data == NULL) {
+ info->buf_size = params->buf_size;
+
+
+ if (ch==X502_STREAM_CH_IN) {
+ info->rx.transf_size = ((params->step+127)/128)*128;
+ if (info->rx.transf_size > USB_BULK_RX_MAX_TRANSF_SIZE)
+ info->rx.transf_size = USB_BULK_RX_MAX_TRANSF_SIZE;
+
+ info->rx.cpl_cnt = info->buf_size/info->rx.transf_size;
+ /* буфер разбиваем на столько частей, чтобы можно было запустить
+ * в параллель все USB_BULK_RX_MAX_TRANSF_CNT трансферов */
+ if (info->rx.cpl_cnt < USB_BULK_RX_MAX_TRANSF_CNT)
+ info->rx.cpl_cnt = USB_BULK_RX_MAX_TRANSF_CNT;
+ info->buf_size = info->rx.cpl_cnt*info->rx.transf_size;
+
+ info->rx.cpls = malloc(info->rx.cpl_cnt * sizeof(info->rx.cpls[0]));
+ if (info->rx.cpls == NULL) {
+ err = X502_ERR_MEMORY_ALLOC;
+ }
+ }
+
+ if (err == X502_ERR_OK) {
+ info->data = malloc(info->buf_size*sizeof(info->data[0]));
+ if (info->data == NULL) {
+ err = X502_ERR_MEMORY_ALLOC;
+ }
+ }
+
+ if (err == X502_ERR_OK) {
+ f_stream_init(ch, info);
+ }
+
+ if (err == X502_ERR_OK) {
+ uint16_t send_step = params->step > 0xFFFF ? 0xFFFF : params->step;
+ err = f_ioreq(usb_data->devhnd, E502_CM4_CMD_STREAM_SET_STEP,
+ (ch<<16) | send_step, NULL, 0, NULL, 0, NULL, 0);
+ }
+
+
+ if (err != X502_ERR_OK) {
+ f_iface_stream_free(hnd, ch, 0);
+ }
+ }
+
+
+ return err;
+}
+
+
+
+
+
+
+static int32_t f_iface_stream_start(t_x502_hnd hnd, uint32_t ch, uint32_t flags) {
+ t_usb_iface_data *usb_data = (t_usb_iface_data *)hnd->iface_data;
+ int32_t err = X502_ERR_OK;
+
+ if (err == X502_ERR_OK) {
+ t_transf_info *info = &usb_data->streams[ch];
+ f_stream_init(ch, info);
+ info->thread = osspec_thread_create(ch == X502_STREAM_CH_IN ?
+ f_usb_rx_thread_func : f_usb_tx_thread_func,
+ usb_data, 0);
+ if (info->thread == OSSPEC_INVALID_THREAD) {
+ err = X502_ERR_THREAD_START;
+ }
+ }
+
+ if (!err && !(flags & X502_STREAM_FLAG_NO_REQUEST)) {
+ err = f_ioreq(usb_data->devhnd, E502_CM4_CMD_STREAM_START, (ch<<16),
+ NULL, 0, NULL, 0, NULL, 0);
+ }
+
+ return err;
+}
+
+static int32_t f_iface_stream_stop(t_x502_hnd hnd, uint32_t ch, uint32_t flags) {
+ t_usb_iface_data *usb_data = (t_usb_iface_data *)hnd->iface_data;
+ t_transf_info *info = &usb_data->streams[ch];
+ int err = X502_ERR_OK;
+ int ioctl_err = X502_ERR_OK;
+ int32_t running;
+
+ if (!(flags & X502_STREAM_FLAG_NO_REQUEST)) {
+ ioctl_err = hnd->iface_hnd->stream_running(hnd, ch, &running);
+ if (!ioctl_err && running) {
+ ioctl_err = f_ioreq(usb_data->devhnd, E502_CM4_CMD_STREAM_STOP, (ch << 16),
+ NULL, 0, NULL, 0, NULL, 0);
+ }
+ }
+
+
+ if (info->thread != OSSPEC_INVALID_THREAD) {
+ info->stop_req = 1;
+ osspec_event_set(info->usb_wake_evt);
+ err = osspec_thread_wait(info->thread, STREAM_STOP_TOUT);
+ info->thread = OSSPEC_INVALID_THREAD;
+ }
+
+ return err != X502_ERR_OK ? err : ioctl_err;
+}
+
+
+static int32_t f_iface_stream_free(t_x502_hnd hnd, uint32_t ch, uint32_t flags) {
+ int32_t err;
+ t_usb_iface_data *usb_data = (t_usb_iface_data *)hnd->iface_data;
+ t_transf_info *info = &usb_data->streams[ch];
+
+ err = hnd->iface_hnd->stream_stop(hnd, ch, flags);
+
+ free(info->data);
+ info->data = NULL;
+
+ if (ch==X502_STREAM_CH_IN) {
+ free(info->rx.cpls);
+ info->rx.cpls = NULL;
+ info->rx.cpl_cnt = 0;
+ info->rx.transf_size = 0;
+ }
+
+ return err;
+}
+
+
+
+
+static int32_t f_iface_stream_read(t_x502_hnd hnd, uint32_t *buf, uint32_t size, uint32_t tout) {
+ int32_t recvd = 0;
+ t_usb_iface_data *usb_data = (t_usb_iface_data *)hnd->iface_data;
+ t_transf_info *info = &usb_data->streams[X502_STREAM_CH_IN];
+ t_ltimer tmr;
+ int32_t err = info->data == NULL ? X502_ERR_STREAM_IS_NOT_RUNNING : X502_ERR_OK;
+
+ if (err == X502_ERR_OK) {
+ ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
+
+ do {
+ int check_next = 1;
+ err = info->err;
+
+ while (!err && check_next) {
+ t_rx_cpl_part *cpl = &info->rx.cpls[info->rx.cpl_get_pos];
+ int cur_state = cpl->state;
+ check_next = 0;
+
+ if (cur_state == RX_STATE_ERR) {
+ err = X502_ERR_RECV;
+ } else if (cur_state == RX_STATE_RDY) {
+ osspec_mutex_lock(info->mutex, MUTEX_STREAM_LOCK_TOUT);
+
+ if (cpl->size) {
+ uint32_t cpy_size = cpl->size;
+ if (cpy_size > size)
+ cpy_size = size;
+ if (cpy_size) {
+ memcpy(&buf[recvd], cpl->addr, cpy_size*sizeof(buf[0]));
+ recvd+=cpy_size;
+ size-=cpy_size;
+ cpl->size -= cpy_size;
+ cpl->addr += cpy_size;
+ info->rx.buf_get_rdy -= cpy_size;
+ }
+ }
+
+ if (cpl->size==0) {
+ cpl->state = RX_STATE_IDLE;
+ osspec_event_set(info->usb_wake_evt);
+ check_next = 1;
+ }
+ osspec_mutex_release(info->mutex);
+ }
+
+ if (check_next) {
+ if (++info->rx.cpl_get_pos==info->rx.cpl_cnt)
+ info->rx.cpl_get_pos = 0;
+ }
+ }
+
+
+
+ if (!err && (size!=0)) {
+ osspec_mutex_lock(info->mutex, MUTEX_STREAM_LOCK_TOUT);
+ if ((info->rx.cpls[info->rx.cpl_get_pos].state == RX_STATE_IDLE)
+ || ((info->rx.cpls[info->rx.cpl_get_pos].state == RX_STATE_BUSY))) {
+ osspec_event_clear(info->user_wake_evt);
+ }
+ osspec_mutex_release(info->mutex);
+
+ osspec_event_wait(info->user_wake_evt, LTIMER_CLOCK_TICKS_TO_MS(ltimer_expiration(&tmr)));
+ }
+ } while (!err && (size!=0) && !ltimer_expired(&tmr));
+ }
+
+ return err == X502_ERR_OK ? recvd : err;
+}
+
+
+
+static int32_t f_iface_stream_write(t_x502_hnd hnd, const uint32_t *buf, uint32_t size, uint32_t tout) {
+ t_usb_iface_data *usb_data = (t_usb_iface_data *)hnd->iface_data;
+ t_transf_info *info = &usb_data->streams[X502_STREAM_CH_OUT];
+ t_ltimer tmr;
+ int32_t sent = 0;
+ int32_t err = info->data == NULL ? X502_ERR_STREAM_IS_NOT_RUNNING : X502_ERR_OK;
+
+ if (err == X502_ERR_OK) {
+ ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
+
+ do {
+ uint32_t rdy_size, snd_size;
+ snd_size = rdy_size = info->tx.buf_put_rdy;
+ err = info->err;
+
+ if (!err) {
+ if (rdy_size == 0) {
+ osspec_event_wait(info->user_wake_evt,
+ LTIMER_CLOCK_TICKS_TO_MS(ltimer_expiration(&tmr)));
+ } else {
+ uint32_t end_size;
+ uint32_t put_pos;
+ if (snd_size > size)
+ snd_size = size;
+ end_size = info->buf_size - info->tx.buf_pos_put;
+ if (snd_size >= end_size) {
+ memcpy(&info->data[info->tx.buf_pos_put], &buf[sent], end_size*sizeof(buf[0]));
+ if (snd_size!=end_size) {
+ memcpy(&info->data[0], &buf[sent+end_size], (snd_size-end_size)*sizeof(buf[0]));
+ }
+ put_pos = snd_size-end_size;
+ } else {
+ memcpy(&info->data[info->tx.buf_pos_put], &buf[sent], snd_size*sizeof(buf[0]));
+ put_pos = info->tx.buf_pos_put + snd_size;
+ }
+
+ osspec_mutex_lock(info->mutex, MUTEX_STREAM_LOCK_TOUT);
+ info->tx.buf_pos_put = put_pos;
+ info->tx.buf_put_rdy -= snd_size;
+
+ if (info->tx.buf_put_rdy==0) {
+ osspec_event_clear(info->user_wake_evt);
+ }
+
+ osspec_event_set(info->usb_wake_evt);
+
+ osspec_mutex_release(info->mutex);
+
+
+ sent+=snd_size;
+ size-=snd_size;
+ }
+ }
+ } while (!err && (size!=0) && !ltimer_expired(&tmr));
+ }
+ return err == X502_ERR_OK ? sent : err;
+}
+
+
+
+static int32_t f_iface_stream_get_rdy_cnt(t_x502_hnd hnd, uint32_t ch, uint32_t *rdy_cnt) {
+ t_usb_iface_data *usb_data = (t_usb_iface_data *)hnd->iface_data;
+ int32_t err=0;
+ if (ch == X502_STREAM_CH_IN) {
+ *rdy_cnt = usb_data->streams[X502_STREAM_CH_IN].rx.buf_get_rdy;
+ } else if (ch == X502_STREAM_CH_OUT) {
+ *rdy_cnt = usb_data->streams[X502_STREAM_CH_OUT].tx.buf_put_rdy;
+ } else {
+ err = X502_ERR_INVALID_STREAM_CH;
+
+ }
+ return err;
+}
+
+
+
+
+
+
+static int32_t f_ioreq(libusb_device_handle *handle, uint32_t cmd_code, uint32_t param,
+ const void* snd_data, uint32_t snd_size,
+ void* rcv_data, uint32_t recv_size, uint32_t* recvd_size, uint32_t tout) {
+ int32_t err = 0;
+ uint8_t req_type = LIBUSB_REQUEST_TYPE_VENDOR;
+ uint16_t len;
+ uint8_t* iobuf;
+ int usbres = 0;
+ if (tout == 0)
+ tout = E502_USB_REQ_TOUT;
+
+ if (recv_size > 0) {
+ req_type |= LIBUSB_ENDPOINT_IN;
+ len = recv_size;
+ iobuf = rcv_data;
+ } else {
+ req_type |= LIBUSB_ENDPOINT_OUT;
+ len = snd_size;
+ iobuf = (uint8_t*)snd_data;
+ }
+
+ if (!err) {
+ usbres = libusb_control_transfer(handle,
+ req_type, cmd_code & 0xFF,
+ param & 0xFFFF,
+ (param >> 16) & 0xFFFF,
+ iobuf, len, tout);
+ if (usbres < 0) {
+ err = (usbres == LIBUSB_ERROR_NO_DEVICE ? X502_ERR_DEVICE_DISCONNECTED :
+ X502_ERR_IOCTL_FAILD);
+ } else {
+ if (recvd_size!=NULL) {
+ *recvd_size = usbres;
+ } else if (usbres != len) {
+ err = X502_ERR_IOCTL_INVALID_RESP_SIZE;
+ }
+ }
+
+ //если управляющий запрос не выполнен успешно, то пытаемся получить
+ //код ошибки из устройства
+ if (err == X502_ERR_IOCTL_FAILD) {
+ uint32_t devres;
+ usbres = libusb_control_transfer(handle,
+ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR,
+ E502_CM4_CMD_GET_LAST_ERROR,
+ 0, 0,
+ (uint8_t*)&devres, sizeof(devres),
+ E502_USB_REQ_TOUT);
+
+ //если успешно получили код ошибки устройства - то возвращаем его в качестве результата
+ if ((usbres == sizeof(devres)) && (devres !=0)) {
+ err = devres;
+ }
+ }
+ }
+ return err;
+}
+
+
+static int32_t f_iface_gen_ioctl(t_x502_hnd hnd, uint32_t cmd_code, uint32_t param,
+ const void* snd_data, uint32_t snd_size,
+ void* rcv_data, uint32_t recv_size, uint32_t* recvd_size,
+ uint32_t tout) {
+ t_usb_iface_data *usb_data = (t_usb_iface_data *)hnd->iface_data;
+ return f_ioreq(usb_data->devhnd, cmd_code, param, snd_data, snd_size,
+ rcv_data, recv_size, recvd_size, tout);
+}
+
+static int32_t f_fill_devlist(libusb_device_handle *hnd, t_x502_devrec *info) {
+ int32_t err = X502_ERR_OK;
+ t_x502_devrec_inptr *devinfo_ptr = calloc(1, sizeof(t_x502_devrec_inptr));
+ if (devinfo_ptr==NULL) {
+ err = X502_ERR_MEMORY_ALLOC;
+ }
+
+ if (err == X502_ERR_OK) {
+ t_lboot_devinfo lboot_info;
+
+
+ //получаем информацию о устройстве
+ err = f_ioreq(hnd, E502_CM4_CMD_GET_MODULE_INFO, 0, NULL, 0, &lboot_info,
+ sizeof(lboot_info), NULL, 0);
+
+ if (err == X502_ERR_OK) {
+ uint32_t devflags;
+
+
+ strcpy(info->devname, lboot_info.devname);
+ strcpy(info->serial, lboot_info.serial);
+
+ devinfo_ptr->iface = &f_usb_iface;
+ devinfo_ptr->iface_data = libusb_ref_device(libusb_get_device(hnd));
+ info->internal = devinfo_ptr;
+ info->iface = X502_IFACE_USB;
+ info->flags = X502_DEVFLAGS_IFACE_SUPPORT_USB;
+
+ if (f_ioreq(hnd, E502_CM4_CMD_GET_DEVFLAGS, 0,
+ NULL, 0, &devflags, sizeof(devflags), NULL, 0) == X502_ERR_OK) {
+ info->flags &= ~E502_CM4_DEVFLAGS;
+ info->flags |= devflags;
+ }
+ }
+ }
+
+ if (err != X502_ERR_OK) {
+ free(devinfo_ptr);
+ }
+ return err;
+}
+
+
+
+X502_EXPORT(int32_t) E502_UsbGetDevRecordsList2(t_x502_devrec *list, uint32_t size,
+ uint32_t flags, uint32_t *devcnt, uint16_t idVendor, uint16_t idProduct) {
+ uint32_t curcnt=0;
+ int32_t err = X502_ERR_OK;
+ libusb_device **usb_devlist;
+ ssize_t usb_devlist_size, i;
+
+ if (!f_lusb_init_done) {
+ libusb_init(NULL);
+#ifdef LIBUSB_DEBUG
+ libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_DEBUG);
+#endif
+ f_lusb_init_done = 1;
+ }
+
+
+ //получаем список устройств
+ usb_devlist_size = libusb_get_device_list(NULL, &usb_devlist);
+ for (i=0; i < usb_devlist_size; i++) {
+ //получаем дескриптор устройства для проверки vid, pid
+ struct libusb_device_descriptor devdescr;
+ libusb_device_handle* usbhnd = NULL;
+
+ if (libusb_get_device_descriptor (usb_devlist[i], &devdescr) == 0) {
+ //сравниваем VID, PID с индентификаторами загрузочного устройства
+ if ((devdescr.idVendor == idVendor)
+ && (devdescr.idProduct == idProduct)) {
+ int32_t cur_err;
+ cur_err = libusb_open(usb_devlist[i], &usbhnd);
+ if (cur_err == X502_ERR_OK) {
+ t_x502_devrec info;
+ int info_used = 0;
+ X502_DevRecordInit(&info);
+
+
+ if (f_fill_devlist(usbhnd, &info) == X502_ERR_OK) {
+ /* если нужны только не открытые, то уже открытое
+ * устройство пропускаем */
+ if (!(flags & X502_GETDEVS_FLAGS_ONLY_NOT_OPENED) ||
+ !(info.flags & X502_DEVFLAGS_DEVREC_OPENED)) {
+ /* если есть место в списке - то сохраняем
+ * полученную информацию */
+ if ((list!=NULL) && (curcnt < size)) {
+ list[curcnt] = info;
+ info_used = 1;
+ }
+ curcnt++;
+ }
+ }
+
+ if (!info_used)
+ X502_FreeDevRecordList(&info,1);
+
+ libusb_close(usbhnd);
+ }
+ }
+ }
+ }
+ libusb_free_device_list(usb_devlist, 1);
+
+
+ if (devcnt != NULL)
+ *devcnt = curcnt;
+
+ return err != X502_ERR_OK ? err : curcnt > (uint32_t)size ? (int32_t)size : (int32_t)curcnt ;
+}
+
+X502_EXPORT(int32_t) E440_UsbGetDevRecordsList(t_x502_devrec* list, uint32_t size,
+ uint32_t flags, uint32_t* devcnt) {
+ return E502_UsbGetDevRecordsList2(list, size, flags, devcnt, E440_USB_VID, E440_USB_PID);
+}
+
+X502_EXPORT(int32_t) E502_UsbGetDevRecordsList(t_x502_devrec *list, uint32_t size,
+ uint32_t flags, uint32_t *devcnt) {
+ return E502_UsbGetDevRecordsList2(list, size, flags, devcnt, E502_USB_VID, E502_USB_PID);
+}
+
+X502_EXPORT(int32_t) E502_OpenUsb(t_x502_hnd hnd, const char* serial) {
+ return X502_Open(hnd, serial, E502_DEVICE_NAME, E502_UsbGetDevRecordsList);
+}
+
+X502_EXPORT(int32_t) E502_UsbGetSerialList(char serials[][X502_SERIAL_SIZE], uint32_t size,
+ uint32_t flags, uint32_t *devcnt) {
+ return X502_GetSerialList(serials, size, flags, devcnt, E502_DEVICE_NAME, E502_UsbGetDevRecordsList);
+}
+
+
+X502_EXPORT(int32_t) E16_UsbGetDevRecordsList(t_x502_devrec *list, uint32_t size,
+ uint32_t flags, uint32_t *devcnt) {
+ return E502_UsbGetDevRecordsList2(list, size, flags, devcnt, E16_USB_VID, E16_USB_PID);
+}
+
+X502_EXPORT(int32_t) E16_OpenUsb(t_x502_hnd hnd, const char* serial) {
+ return X502_Open(hnd, serial, E16_DEVICE_NAME, E16_UsbGetDevRecordsList);
+}
+
+X502_EXPORT(int32_t) E16_UsbGetSerialList(char serials[][X502_SERIAL_SIZE], uint32_t size,
+ uint32_t flags, uint32_t *devcnt) {
+ return X502_GetSerialList(serials, size, flags, devcnt, E16_DEVICE_NAME, E16_UsbGetDevRecordsList);
+}
+#else
+#include "e502api.h"
+X502_EXPORT(int32_t) E502_OpenUsb(t_x502_hnd hnd, const char* serial) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+
+X502_EXPORT(int32_t) E502_UsbGetSerialList(char serials[][X502_SERIAL_SIZE], uint32_t size,
+ uint32_t flags, uint32_t *devcnt) {
+ return X502_ERR_NOT_IMPLEMENTED;
+}
+#endif
+
+
+
+
+
+
diff --git a/e502/libusb-1.0/libusb-1.0/Makefile.am b/e502/libusb-1.0/libusb-1.0/Makefile.am
new file mode 100644
index 0000000..80e3705
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/Makefile.am
@@ -0,0 +1,75 @@
+all: libusb-1.0.la libusb-1.0.dll
+
+AUTOMAKE_OPTIONS = subdir-objects
+
+lib_LTLIBRARIES = libusb-1.0.la
+
+POSIX_POLL_SRC = os/poll_posix.c
+LINUX_USBFS_SRC = os/linux_usbfs.c
+DARWIN_USB_SRC = os/darwin_usb.c
+OPENBSD_USB_SRC = os/openbsd_usb.c
+NETBSD_USB_SRC = os/netbsd_usb.c
+WINDOWS_USB_SRC = os/poll_windows.c os/windows_usb.c libusb-1.0.rc libusb-1.0.def
+WINCE_USB_SRC = os/wince_usb.c os/wince_usb.h
+
+EXTRA_DIST = $(LINUX_USBFS_SRC) $(DARWIN_USB_SRC) $(OPENBSD_USB_SRC) \
+ $(NETBSD_USB_SRC) $(WINDOWS_USB_SRC) $(WINCE_USB_SRC) \
+ $(POSIX_POLL_SRC) \
+ os/threads_posix.c os/threads_windows.c \
+ os/linux_udev.c os/linux_netlink.c
+
+if OS_LINUX
+
+if USE_UDEV
+OS_SRC = $(LINUX_USBFS_SRC) $(POSIX_POLL_SRC) \
+ os/linux_udev.c
+else
+OS_SRC = $(LINUX_USBFS_SRC) $(POSIX_POLL_SRC) \
+ os/linux_netlink.c
+endif
+
+endif
+
+if OS_DARWIN
+OS_SRC = $(DARWIN_USB_SRC) $(POSIX_POLL_SRC)
+AM_CFLAGS_EXT = -no-cpp-precomp
+endif
+
+if OS_OPENBSD
+OS_SRC = $(OPENBSD_USB_SRC) $(POSIX_POLL_SRC)
+endif
+
+if OS_NETBSD
+OS_SRC = $(NETBSD_USB_SRC) $(POSIX_POLL_SRC)
+endif
+
+if OS_WINDOWS
+OS_SRC = $(WINDOWS_USB_SRC)
+
+.rc.lo:
+ $(AM_V_GEN)$(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --tag=RC --mode=compile $(RC) $(RCFLAGS) -i $< -o $@
+
+libusb-1.0.rc: version.h version_nano.h
+endif
+
+libusb-1.0.dll: libusb-1.0.def libusb-1.0.la
+if CREATE_IMPORT_LIB
+# Rebuild the import lib from the .def so that MS and MinGW DLLs can be interchanged
+ $(AM_V_GEN)$(DLLTOOL) $(DLLTOOLFLAGS) --kill-at --input-def $(srcdir)/libusb-1.0.def --dllname $@ --output-lib .libs/$@.a
+endif
+
+if THREADS_POSIX
+THREADS_SRC = os/threads_posix.h os/threads_posix.c
+else
+THREADS_SRC = os/threads_windows.h os/threads_windows.c
+endif
+
+libusb_1_0_la_CFLAGS = $(AM_CFLAGS)
+libusb_1_0_la_LDFLAGS = $(LTLDFLAGS)
+libusb_1_0_la_SOURCES = libusbi.h core.c descriptor.c io.c strerror.c sync.c \
+ os/linux_usbfs.h os/darwin_usb.h os/windows_usb.h os/windows_common.h \
+ hotplug.h hotplug.c $(THREADS_SRC) $(OS_SRC) \
+ os/poll_posix.h os/poll_windows.h
+
+hdrdir = $(includedir)/libusb-1.0
+hdr_HEADERS = libusb.h
diff --git a/e502/libusb-1.0/libusb-1.0/Makefile.in b/e502/libusb-1.0/libusb-1.0/Makefile.in
new file mode 100644
index 0000000..0fcff77
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/Makefile.in
@@ -0,0 +1,914 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = libusb
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/depcomp $(hdr_HEADERS)
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/libusb/version.h $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(hdrdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libusb_1_0_la_LIBADD =
+am__libusb_1_0_la_SOURCES_DIST = libusbi.h core.c descriptor.c io.c \
+ strerror.c sync.c os/linux_usbfs.h os/darwin_usb.h \
+ os/windows_usb.h os/windows_common.h hotplug.h hotplug.c \
+ os/threads_windows.h os/threads_windows.c os/threads_posix.h \
+ os/threads_posix.c os/darwin_usb.c os/poll_posix.c \
+ os/linux_usbfs.c os/linux_netlink.c os/linux_udev.c \
+ os/netbsd_usb.c os/openbsd_usb.c os/poll_windows.c \
+ os/windows_usb.c libusb-1.0.rc libusb-1.0.def os/poll_posix.h \
+ os/poll_windows.h
+am__dirstamp = $(am__leading_dot)dirstamp
+@THREADS_POSIX_FALSE@am__objects_1 = \
+@THREADS_POSIX_FALSE@ os/libusb_1_0_la-threads_windows.lo
+@THREADS_POSIX_TRUE@am__objects_1 = os/libusb_1_0_la-threads_posix.lo
+am__objects_2 = os/libusb_1_0_la-darwin_usb.lo
+am__objects_3 = os/libusb_1_0_la-poll_posix.lo
+am__objects_4 = os/libusb_1_0_la-linux_usbfs.lo
+am__objects_5 = os/libusb_1_0_la-netbsd_usb.lo
+am__objects_6 = os/libusb_1_0_la-openbsd_usb.lo
+am__objects_7 = os/libusb_1_0_la-poll_windows.lo \
+ os/libusb_1_0_la-windows_usb.lo libusb-1.0.lo
+@OS_DARWIN_FALSE@@OS_LINUX_FALSE@@OS_NETBSD_FALSE@@OS_OPENBSD_FALSE@@OS_WINDOWS_TRUE@am__objects_8 = $(am__objects_7)
+@OS_DARWIN_FALSE@@OS_LINUX_FALSE@@OS_NETBSD_FALSE@@OS_OPENBSD_TRUE@am__objects_8 = $(am__objects_6) \
+@OS_DARWIN_FALSE@@OS_LINUX_FALSE@@OS_NETBSD_FALSE@@OS_OPENBSD_TRUE@ $(am__objects_3)
+@OS_DARWIN_FALSE@@OS_LINUX_FALSE@@OS_NETBSD_TRUE@am__objects_8 = $(am__objects_5) \
+@OS_DARWIN_FALSE@@OS_LINUX_FALSE@@OS_NETBSD_TRUE@ $(am__objects_3)
+@OS_DARWIN_FALSE@@OS_LINUX_TRUE@@USE_UDEV_FALSE@am__objects_8 = $(am__objects_4) \
+@OS_DARWIN_FALSE@@OS_LINUX_TRUE@@USE_UDEV_FALSE@ $(am__objects_3) \
+@OS_DARWIN_FALSE@@OS_LINUX_TRUE@@USE_UDEV_FALSE@ os/libusb_1_0_la-linux_netlink.lo
+@OS_DARWIN_FALSE@@OS_LINUX_TRUE@@USE_UDEV_TRUE@am__objects_8 = $(am__objects_4) \
+@OS_DARWIN_FALSE@@OS_LINUX_TRUE@@USE_UDEV_TRUE@ $(am__objects_3) \
+@OS_DARWIN_FALSE@@OS_LINUX_TRUE@@USE_UDEV_TRUE@ os/libusb_1_0_la-linux_udev.lo
+@OS_DARWIN_TRUE@am__objects_8 = $(am__objects_2) $(am__objects_3)
+am_libusb_1_0_la_OBJECTS = libusb_1_0_la-core.lo \
+ libusb_1_0_la-descriptor.lo libusb_1_0_la-io.lo \
+ libusb_1_0_la-strerror.lo libusb_1_0_la-sync.lo \
+ libusb_1_0_la-hotplug.lo $(am__objects_1) $(am__objects_8)
+libusb_1_0_la_OBJECTS = $(am_libusb_1_0_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libusb_1_0_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libusb_1_0_la_CFLAGS) \
+ $(CFLAGS) $(libusb_1_0_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libusb_1_0_la_SOURCES)
+DIST_SOURCES = $(am__libusb_1_0_la_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(hdr_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLDFLAGS = @LTLDFLAGS@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OS_DARWIN = @OS_DARWIN@
+OS_LINUX = @OS_LINUX@
+OS_NETBSD = @OS_NETBSD@
+OS_OPENBSD = @OS_OPENBSD@
+OS_WINDOWS = @OS_WINDOWS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_UDEV = @USE_UDEV@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AUTOMAKE_OPTIONS = subdir-objects
+lib_LTLIBRARIES = libusb-1.0.la
+POSIX_POLL_SRC = os/poll_posix.c
+LINUX_USBFS_SRC = os/linux_usbfs.c
+DARWIN_USB_SRC = os/darwin_usb.c
+OPENBSD_USB_SRC = os/openbsd_usb.c
+NETBSD_USB_SRC = os/netbsd_usb.c
+WINDOWS_USB_SRC = os/poll_windows.c os/windows_usb.c libusb-1.0.rc libusb-1.0.def
+WINCE_USB_SRC = os/wince_usb.c os/wince_usb.h
+EXTRA_DIST = $(LINUX_USBFS_SRC) $(DARWIN_USB_SRC) $(OPENBSD_USB_SRC) \
+ $(NETBSD_USB_SRC) $(WINDOWS_USB_SRC) $(WINCE_USB_SRC) \
+ $(POSIX_POLL_SRC) \
+ os/threads_posix.c os/threads_windows.c \
+ os/linux_udev.c os/linux_netlink.c
+
+@OS_DARWIN_TRUE@OS_SRC = $(DARWIN_USB_SRC) $(POSIX_POLL_SRC)
+@OS_LINUX_TRUE@@USE_UDEV_FALSE@OS_SRC = $(LINUX_USBFS_SRC) $(POSIX_POLL_SRC) \
+@OS_LINUX_TRUE@@USE_UDEV_FALSE@ os/linux_netlink.c
+
+@OS_LINUX_TRUE@@USE_UDEV_TRUE@OS_SRC = $(LINUX_USBFS_SRC) $(POSIX_POLL_SRC) \
+@OS_LINUX_TRUE@@USE_UDEV_TRUE@ os/linux_udev.c
+
+@OS_NETBSD_TRUE@OS_SRC = $(NETBSD_USB_SRC) $(POSIX_POLL_SRC)
+@OS_OPENBSD_TRUE@OS_SRC = $(OPENBSD_USB_SRC) $(POSIX_POLL_SRC)
+@OS_WINDOWS_TRUE@OS_SRC = $(WINDOWS_USB_SRC)
+@OS_DARWIN_TRUE@AM_CFLAGS_EXT = -no-cpp-precomp
+@THREADS_POSIX_FALSE@THREADS_SRC = os/threads_windows.h os/threads_windows.c
+@THREADS_POSIX_TRUE@THREADS_SRC = os/threads_posix.h os/threads_posix.c
+libusb_1_0_la_CFLAGS = $(AM_CFLAGS)
+libusb_1_0_la_LDFLAGS = $(LTLDFLAGS)
+libusb_1_0_la_SOURCES = libusbi.h core.c descriptor.c io.c strerror.c sync.c \
+ os/linux_usbfs.h os/darwin_usb.h os/windows_usb.h os/windows_common.h \
+ hotplug.h hotplug.c $(THREADS_SRC) $(OS_SRC) \
+ os/poll_posix.h os/poll_windows.h
+
+hdrdir = $(includedir)/libusb-1.0
+hdr_HEADERS = libusb.h
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj .rc
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libusb/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu libusb/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+os/$(am__dirstamp):
+ @$(MKDIR_P) os
+ @: > os/$(am__dirstamp)
+os/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) os/$(DEPDIR)
+ @: > os/$(DEPDIR)/$(am__dirstamp)
+os/libusb_1_0_la-threads_windows.lo: os/$(am__dirstamp) \
+ os/$(DEPDIR)/$(am__dirstamp)
+os/libusb_1_0_la-threads_posix.lo: os/$(am__dirstamp) \
+ os/$(DEPDIR)/$(am__dirstamp)
+os/libusb_1_0_la-darwin_usb.lo: os/$(am__dirstamp) \
+ os/$(DEPDIR)/$(am__dirstamp)
+os/libusb_1_0_la-poll_posix.lo: os/$(am__dirstamp) \
+ os/$(DEPDIR)/$(am__dirstamp)
+os/libusb_1_0_la-linux_usbfs.lo: os/$(am__dirstamp) \
+ os/$(DEPDIR)/$(am__dirstamp)
+os/libusb_1_0_la-linux_netlink.lo: os/$(am__dirstamp) \
+ os/$(DEPDIR)/$(am__dirstamp)
+os/libusb_1_0_la-linux_udev.lo: os/$(am__dirstamp) \
+ os/$(DEPDIR)/$(am__dirstamp)
+os/libusb_1_0_la-netbsd_usb.lo: os/$(am__dirstamp) \
+ os/$(DEPDIR)/$(am__dirstamp)
+os/libusb_1_0_la-openbsd_usb.lo: os/$(am__dirstamp) \
+ os/$(DEPDIR)/$(am__dirstamp)
+os/libusb_1_0_la-poll_windows.lo: os/$(am__dirstamp) \
+ os/$(DEPDIR)/$(am__dirstamp)
+os/libusb_1_0_la-windows_usb.lo: os/$(am__dirstamp) \
+ os/$(DEPDIR)/$(am__dirstamp)
+
+libusb-1.0.la: $(libusb_1_0_la_OBJECTS) $(libusb_1_0_la_DEPENDENCIES) $(EXTRA_libusb_1_0_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libusb_1_0_la_LINK) -rpath $(libdir) $(libusb_1_0_la_OBJECTS) $(libusb_1_0_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+ -rm -f os/*.$(OBJEXT)
+ -rm -f os/*.lo
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libusb_1_0_la-core.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libusb_1_0_la-descriptor.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libusb_1_0_la-hotplug.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libusb_1_0_la-io.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libusb_1_0_la-strerror.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libusb_1_0_la-sync.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libusb_1_0_la-darwin_usb.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libusb_1_0_la-linux_netlink.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libusb_1_0_la-linux_udev.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libusb_1_0_la-linux_usbfs.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libusb_1_0_la-netbsd_usb.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libusb_1_0_la-openbsd_usb.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libusb_1_0_la-poll_posix.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libusb_1_0_la-poll_windows.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libusb_1_0_la-threads_posix.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libusb_1_0_la-threads_windows.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libusb_1_0_la-windows_usb.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libusb_1_0_la-core.lo: core.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT libusb_1_0_la-core.lo -MD -MP -MF $(DEPDIR)/libusb_1_0_la-core.Tpo -c -o libusb_1_0_la-core.lo `test -f 'core.c' || echo '$(srcdir)/'`core.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libusb_1_0_la-core.Tpo $(DEPDIR)/libusb_1_0_la-core.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='core.c' object='libusb_1_0_la-core.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o libusb_1_0_la-core.lo `test -f 'core.c' || echo '$(srcdir)/'`core.c
+
+libusb_1_0_la-descriptor.lo: descriptor.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT libusb_1_0_la-descriptor.lo -MD -MP -MF $(DEPDIR)/libusb_1_0_la-descriptor.Tpo -c -o libusb_1_0_la-descriptor.lo `test -f 'descriptor.c' || echo '$(srcdir)/'`descriptor.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libusb_1_0_la-descriptor.Tpo $(DEPDIR)/libusb_1_0_la-descriptor.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='descriptor.c' object='libusb_1_0_la-descriptor.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o libusb_1_0_la-descriptor.lo `test -f 'descriptor.c' || echo '$(srcdir)/'`descriptor.c
+
+libusb_1_0_la-io.lo: io.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT libusb_1_0_la-io.lo -MD -MP -MF $(DEPDIR)/libusb_1_0_la-io.Tpo -c -o libusb_1_0_la-io.lo `test -f 'io.c' || echo '$(srcdir)/'`io.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libusb_1_0_la-io.Tpo $(DEPDIR)/libusb_1_0_la-io.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='io.c' object='libusb_1_0_la-io.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o libusb_1_0_la-io.lo `test -f 'io.c' || echo '$(srcdir)/'`io.c
+
+libusb_1_0_la-strerror.lo: strerror.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT libusb_1_0_la-strerror.lo -MD -MP -MF $(DEPDIR)/libusb_1_0_la-strerror.Tpo -c -o libusb_1_0_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libusb_1_0_la-strerror.Tpo $(DEPDIR)/libusb_1_0_la-strerror.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strerror.c' object='libusb_1_0_la-strerror.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o libusb_1_0_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c
+
+libusb_1_0_la-sync.lo: sync.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT libusb_1_0_la-sync.lo -MD -MP -MF $(DEPDIR)/libusb_1_0_la-sync.Tpo -c -o libusb_1_0_la-sync.lo `test -f 'sync.c' || echo '$(srcdir)/'`sync.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libusb_1_0_la-sync.Tpo $(DEPDIR)/libusb_1_0_la-sync.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sync.c' object='libusb_1_0_la-sync.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o libusb_1_0_la-sync.lo `test -f 'sync.c' || echo '$(srcdir)/'`sync.c
+
+libusb_1_0_la-hotplug.lo: hotplug.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT libusb_1_0_la-hotplug.lo -MD -MP -MF $(DEPDIR)/libusb_1_0_la-hotplug.Tpo -c -o libusb_1_0_la-hotplug.lo `test -f 'hotplug.c' || echo '$(srcdir)/'`hotplug.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libusb_1_0_la-hotplug.Tpo $(DEPDIR)/libusb_1_0_la-hotplug.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hotplug.c' object='libusb_1_0_la-hotplug.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o libusb_1_0_la-hotplug.lo `test -f 'hotplug.c' || echo '$(srcdir)/'`hotplug.c
+
+os/libusb_1_0_la-threads_windows.lo: os/threads_windows.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT os/libusb_1_0_la-threads_windows.lo -MD -MP -MF os/$(DEPDIR)/libusb_1_0_la-threads_windows.Tpo -c -o os/libusb_1_0_la-threads_windows.lo `test -f 'os/threads_windows.c' || echo '$(srcdir)/'`os/threads_windows.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libusb_1_0_la-threads_windows.Tpo os/$(DEPDIR)/libusb_1_0_la-threads_windows.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='os/threads_windows.c' object='os/libusb_1_0_la-threads_windows.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o os/libusb_1_0_la-threads_windows.lo `test -f 'os/threads_windows.c' || echo '$(srcdir)/'`os/threads_windows.c
+
+os/libusb_1_0_la-threads_posix.lo: os/threads_posix.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT os/libusb_1_0_la-threads_posix.lo -MD -MP -MF os/$(DEPDIR)/libusb_1_0_la-threads_posix.Tpo -c -o os/libusb_1_0_la-threads_posix.lo `test -f 'os/threads_posix.c' || echo '$(srcdir)/'`os/threads_posix.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libusb_1_0_la-threads_posix.Tpo os/$(DEPDIR)/libusb_1_0_la-threads_posix.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='os/threads_posix.c' object='os/libusb_1_0_la-threads_posix.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o os/libusb_1_0_la-threads_posix.lo `test -f 'os/threads_posix.c' || echo '$(srcdir)/'`os/threads_posix.c
+
+os/libusb_1_0_la-darwin_usb.lo: os/darwin_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT os/libusb_1_0_la-darwin_usb.lo -MD -MP -MF os/$(DEPDIR)/libusb_1_0_la-darwin_usb.Tpo -c -o os/libusb_1_0_la-darwin_usb.lo `test -f 'os/darwin_usb.c' || echo '$(srcdir)/'`os/darwin_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libusb_1_0_la-darwin_usb.Tpo os/$(DEPDIR)/libusb_1_0_la-darwin_usb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='os/darwin_usb.c' object='os/libusb_1_0_la-darwin_usb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o os/libusb_1_0_la-darwin_usb.lo `test -f 'os/darwin_usb.c' || echo '$(srcdir)/'`os/darwin_usb.c
+
+os/libusb_1_0_la-poll_posix.lo: os/poll_posix.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT os/libusb_1_0_la-poll_posix.lo -MD -MP -MF os/$(DEPDIR)/libusb_1_0_la-poll_posix.Tpo -c -o os/libusb_1_0_la-poll_posix.lo `test -f 'os/poll_posix.c' || echo '$(srcdir)/'`os/poll_posix.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libusb_1_0_la-poll_posix.Tpo os/$(DEPDIR)/libusb_1_0_la-poll_posix.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='os/poll_posix.c' object='os/libusb_1_0_la-poll_posix.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o os/libusb_1_0_la-poll_posix.lo `test -f 'os/poll_posix.c' || echo '$(srcdir)/'`os/poll_posix.c
+
+os/libusb_1_0_la-linux_usbfs.lo: os/linux_usbfs.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT os/libusb_1_0_la-linux_usbfs.lo -MD -MP -MF os/$(DEPDIR)/libusb_1_0_la-linux_usbfs.Tpo -c -o os/libusb_1_0_la-linux_usbfs.lo `test -f 'os/linux_usbfs.c' || echo '$(srcdir)/'`os/linux_usbfs.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libusb_1_0_la-linux_usbfs.Tpo os/$(DEPDIR)/libusb_1_0_la-linux_usbfs.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='os/linux_usbfs.c' object='os/libusb_1_0_la-linux_usbfs.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o os/libusb_1_0_la-linux_usbfs.lo `test -f 'os/linux_usbfs.c' || echo '$(srcdir)/'`os/linux_usbfs.c
+
+os/libusb_1_0_la-linux_netlink.lo: os/linux_netlink.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT os/libusb_1_0_la-linux_netlink.lo -MD -MP -MF os/$(DEPDIR)/libusb_1_0_la-linux_netlink.Tpo -c -o os/libusb_1_0_la-linux_netlink.lo `test -f 'os/linux_netlink.c' || echo '$(srcdir)/'`os/linux_netlink.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libusb_1_0_la-linux_netlink.Tpo os/$(DEPDIR)/libusb_1_0_la-linux_netlink.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='os/linux_netlink.c' object='os/libusb_1_0_la-linux_netlink.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o os/libusb_1_0_la-linux_netlink.lo `test -f 'os/linux_netlink.c' || echo '$(srcdir)/'`os/linux_netlink.c
+
+os/libusb_1_0_la-linux_udev.lo: os/linux_udev.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT os/libusb_1_0_la-linux_udev.lo -MD -MP -MF os/$(DEPDIR)/libusb_1_0_la-linux_udev.Tpo -c -o os/libusb_1_0_la-linux_udev.lo `test -f 'os/linux_udev.c' || echo '$(srcdir)/'`os/linux_udev.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libusb_1_0_la-linux_udev.Tpo os/$(DEPDIR)/libusb_1_0_la-linux_udev.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='os/linux_udev.c' object='os/libusb_1_0_la-linux_udev.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o os/libusb_1_0_la-linux_udev.lo `test -f 'os/linux_udev.c' || echo '$(srcdir)/'`os/linux_udev.c
+
+os/libusb_1_0_la-netbsd_usb.lo: os/netbsd_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT os/libusb_1_0_la-netbsd_usb.lo -MD -MP -MF os/$(DEPDIR)/libusb_1_0_la-netbsd_usb.Tpo -c -o os/libusb_1_0_la-netbsd_usb.lo `test -f 'os/netbsd_usb.c' || echo '$(srcdir)/'`os/netbsd_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libusb_1_0_la-netbsd_usb.Tpo os/$(DEPDIR)/libusb_1_0_la-netbsd_usb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='os/netbsd_usb.c' object='os/libusb_1_0_la-netbsd_usb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o os/libusb_1_0_la-netbsd_usb.lo `test -f 'os/netbsd_usb.c' || echo '$(srcdir)/'`os/netbsd_usb.c
+
+os/libusb_1_0_la-openbsd_usb.lo: os/openbsd_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT os/libusb_1_0_la-openbsd_usb.lo -MD -MP -MF os/$(DEPDIR)/libusb_1_0_la-openbsd_usb.Tpo -c -o os/libusb_1_0_la-openbsd_usb.lo `test -f 'os/openbsd_usb.c' || echo '$(srcdir)/'`os/openbsd_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libusb_1_0_la-openbsd_usb.Tpo os/$(DEPDIR)/libusb_1_0_la-openbsd_usb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='os/openbsd_usb.c' object='os/libusb_1_0_la-openbsd_usb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o os/libusb_1_0_la-openbsd_usb.lo `test -f 'os/openbsd_usb.c' || echo '$(srcdir)/'`os/openbsd_usb.c
+
+os/libusb_1_0_la-poll_windows.lo: os/poll_windows.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT os/libusb_1_0_la-poll_windows.lo -MD -MP -MF os/$(DEPDIR)/libusb_1_0_la-poll_windows.Tpo -c -o os/libusb_1_0_la-poll_windows.lo `test -f 'os/poll_windows.c' || echo '$(srcdir)/'`os/poll_windows.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libusb_1_0_la-poll_windows.Tpo os/$(DEPDIR)/libusb_1_0_la-poll_windows.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='os/poll_windows.c' object='os/libusb_1_0_la-poll_windows.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o os/libusb_1_0_la-poll_windows.lo `test -f 'os/poll_windows.c' || echo '$(srcdir)/'`os/poll_windows.c
+
+os/libusb_1_0_la-windows_usb.lo: os/windows_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -MT os/libusb_1_0_la-windows_usb.lo -MD -MP -MF os/$(DEPDIR)/libusb_1_0_la-windows_usb.Tpo -c -o os/libusb_1_0_la-windows_usb.lo `test -f 'os/windows_usb.c' || echo '$(srcdir)/'`os/windows_usb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libusb_1_0_la-windows_usb.Tpo os/$(DEPDIR)/libusb_1_0_la-windows_usb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='os/windows_usb.c' object='os/libusb_1_0_la-windows_usb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libusb_1_0_la_CFLAGS) $(CFLAGS) -c -o os/libusb_1_0_la-windows_usb.lo `test -f 'os/windows_usb.c' || echo '$(srcdir)/'`os/windows_usb.c
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+ -rm -rf os/.libs os/_libs
+install-hdrHEADERS: $(hdr_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(hdr_HEADERS)'; test -n "$(hdrdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(hdrdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(hdrdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(hdrdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(hdrdir)" || exit $$?; \
+ done
+
+uninstall-hdrHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(hdr_HEADERS)'; test -n "$(hdrdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(hdrdir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(hdrdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -rm -f os/$(DEPDIR)/$(am__dirstamp)
+ -rm -f os/$(am__dirstamp)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR) os/$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-hdrHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR) os/$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-hdrHEADERS uninstall-libLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-hdrHEADERS install-html \
+ install-html-am install-info install-info-am \
+ install-libLTLIBRARIES install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-hdrHEADERS \
+ uninstall-libLTLIBRARIES
+
+all: libusb-1.0.la libusb-1.0.dll
+
+@OS_WINDOWS_TRUE@.rc.lo:
+@OS_WINDOWS_TRUE@ $(AM_V_GEN)$(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --tag=RC --mode=compile $(RC) $(RCFLAGS) -i $< -o $@
+
+@OS_WINDOWS_TRUE@libusb-1.0.rc: version.h version_nano.h
+
+libusb-1.0.dll: libusb-1.0.def libusb-1.0.la
+# Rebuild the import lib from the .def so that MS and MinGW DLLs can be interchanged
+@CREATE_IMPORT_LIB_TRUE@ $(AM_V_GEN)$(DLLTOOL) $(DLLTOOLFLAGS) --kill-at --input-def $(srcdir)/libusb-1.0.def --dllname $@ --output-lib .libs/$@.a
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/e502/libusb-1.0/libusb-1.0/core.c b/e502/libusb-1.0/libusb-1.0/core.c
new file mode 100644
index 0000000..f3d7ece
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/core.c
@@ -0,0 +1,2334 @@
+/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
+/*
+ * Core functions for libusb
+ * Copyright © 2012-2013 Nathan Hjelm
+ * Copyright © 2007-2008 Daniel Drake
+ * Copyright © 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
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#ifdef HAVE_SYS_TYPES_H
+#include
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include
+#endif
+#ifdef HAVE_SYSLOG_H
+#include
+#endif
+
+#ifdef __ANDROID__
+#include
+#endif
+
+#include "libusbi.h"
+#include "hotplug.h"
+
+#if defined(OS_LINUX)
+const struct usbi_os_backend * const usbi_backend = &linux_usbfs_backend;
+#elif defined(OS_DARWIN)
+const struct usbi_os_backend * const usbi_backend = &darwin_backend;
+#elif defined(OS_OPENBSD)
+const struct usbi_os_backend * const usbi_backend = &openbsd_backend;
+#elif defined(OS_NETBSD)
+const struct usbi_os_backend * const usbi_backend = &netbsd_backend;
+#elif defined(OS_WINDOWS)
+const struct usbi_os_backend * const usbi_backend = &windows_backend;
+#elif defined(OS_WINCE)
+const struct usbi_os_backend * const usbi_backend = &wince_backend;
+#else
+#error "Unsupported OS"
+#endif
+
+struct libusb_context *usbi_default_context = NULL;
+static const struct libusb_version libusb_version_internal =
+ { LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO,
+ LIBUSB_RC, "http://libusb.info" };
+static int default_context_refcnt = 0;
+static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
+static struct timeval timestamp_origin = { 0, 0 };
+
+usbi_mutex_static_t active_contexts_lock = USBI_MUTEX_INITIALIZER;
+struct list_head active_contexts_list;
+
+/**
+ * \mainpage libusb-1.0 API Reference
+ *
+ * \section intro Introduction
+ *
+ * libusb is an open source library that allows you to communicate with USB
+ * devices from userspace. For more info, see the
+ * libusb homepage.
+ *
+ * This documentation is aimed at application developers wishing to
+ * communicate with USB peripherals from their own software. After reviewing
+ * this documentation, feedback and questions can be sent to the
+ * libusb-devel mailing list.
+ *
+ * This documentation assumes knowledge of how to operate USB devices from
+ * a software standpoint (descriptors, configurations, interfaces, endpoints,
+ * control/bulk/interrupt/isochronous transfers, etc). Full information
+ * can be found in the USB 3.0
+ * Specification which is available for free download. You can probably
+ * find less verbose introductions by searching the web.
+ *
+ * \section features Library features
+ *
+ * - All transfer types supported (control/bulk/interrupt/isochronous)
+ * - 2 transfer interfaces:
+ * -# Synchronous (simple)
+ * -# Asynchronous (more complicated, but more powerful)
+ * - Thread safe (although the asynchronous interface means that you
+ * usually won't need to thread)
+ * - Lightweight with lean API
+ * - Compatible with libusb-0.1 through the libusb-compat-0.1 translation layer
+ * - Hotplug support (on some platforms). See \ref hotplug.
+ *
+ * \section gettingstarted Getting Started
+ *
+ * To begin reading the API documentation, start with the Modules page which
+ * links to the different categories of libusb's functionality.
+ *
+ * One decision you will have to make is whether to use the synchronous
+ * or the asynchronous data transfer interface. The \ref io documentation
+ * provides some insight into this topic.
+ *
+ * Some example programs can be found in the libusb source distribution under
+ * the "examples" subdirectory. The libusb homepage includes a list of
+ * real-life project examples which use libusb.
+ *
+ * \section errorhandling Error handling
+ *
+ * libusb functions typically return 0 on success or a negative error code
+ * on failure. These negative error codes relate to LIBUSB_ERROR constants
+ * which are listed on the \ref misc "miscellaneous" documentation page.
+ *
+ * \section msglog Debug message logging
+ *
+ * libusb uses stderr for all logging. By default, logging is set to NONE,
+ * which means that no output will be produced. However, unless the library
+ * has been compiled with logging disabled, then any application calls to
+ * libusb_set_debug(), or the setting of the environmental variable
+ * LIBUSB_DEBUG outside of the application, can result in logging being
+ * produced. Your application should therefore not close stderr, but instead
+ * direct it to the null device if its output is undesireable.
+ *
+ * The libusb_set_debug() function can be used to enable logging of certain
+ * messages. Under standard configuration, libusb doesn't really log much
+ * so you are advised to use this function to enable all error/warning/
+ * informational messages. It will help debug problems with your software.
+ *
+ * The logged messages are unstructured. There is no one-to-one correspondence
+ * between messages being logged and success or failure return codes from
+ * libusb functions. There is no format to the messages, so you should not
+ * try to capture or parse them. They are not and will not be localized.
+ * These messages are not intended to being passed to your application user;
+ * instead, you should interpret the error codes returned from libusb functions
+ * and provide appropriate notification to the user. The messages are simply
+ * there to aid you as a programmer, and if you're confused because you're
+ * getting a strange error code from a libusb function, enabling message
+ * logging may give you a suitable explanation.
+ *
+ * The LIBUSB_DEBUG environment variable can be used to enable message logging
+ * at run-time. This environment variable should be set to a log level number,
+ * which is interpreted the same as the libusb_set_debug() parameter. When this
+ * environment variable is set, the message logging verbosity level is fixed
+ * and libusb_set_debug() effectively does nothing.
+ *
+ * libusb can be compiled without any logging functions, useful for embedded
+ * systems. In this case, libusb_set_debug() and the LIBUSB_DEBUG environment
+ * variable have no effects.
+ *
+ * libusb can also be compiled with verbose debugging messages always. When
+ * the library is compiled in this way, all messages of all verbosities are
+ * always logged. libusb_set_debug() and the LIBUSB_DEBUG environment variable
+ * have no effects.
+ *
+ * \section remarks Other remarks
+ *
+ * libusb does have imperfections. The \ref caveats "caveats" page attempts
+ * to document these.
+ */
+
+/**
+ * \page caveats Caveats
+ *
+ * \section devresets Device resets
+ *
+ * The libusb_reset_device() function allows you to reset a device. If your
+ * program has to call such a function, it should obviously be aware that
+ * the reset will cause device state to change (e.g. register values may be
+ * reset).
+ *
+ * The problem is that any other program could reset the device your program
+ * is working with, at any time. libusb does not offer a mechanism to inform
+ * you when this has happened, so if someone else resets your device it will
+ * not be clear to your own program why the device state has changed.
+ *
+ * Ultimately, this is a limitation of writing drivers in userspace.
+ * Separation from the USB stack in the underlying kernel makes it difficult
+ * for the operating system to deliver such notifications to your program.
+ * The Linux kernel USB stack allows such reset notifications to be delivered
+ * to in-kernel USB drivers, but it is not clear how such notifications could
+ * be delivered to second-class drivers that live in userspace.
+ *
+ * \section blockonly Blocking-only functionality
+ *
+ * The functionality listed below is only available through synchronous,
+ * blocking functions. There are no asynchronous/non-blocking alternatives,
+ * and no clear ways of implementing these.
+ *
+ * - Configuration activation (libusb_set_configuration())
+ * - Interface/alternate setting activation (libusb_set_interface_alt_setting())
+ * - Releasing of interfaces (libusb_release_interface())
+ * - Clearing of halt/stall condition (libusb_clear_halt())
+ * - Device resets (libusb_reset_device())
+ *
+ * \section configsel Configuration selection and handling
+ *
+ * When libusb presents a device handle to an application, there is a chance
+ * that the corresponding device may be in unconfigured state. For devices
+ * with multiple configurations, there is also a chance that the configuration
+ * currently selected is not the one that the application wants to use.
+ *
+ * The obvious solution is to add a call to libusb_set_configuration() early
+ * on during your device initialization routines, but there are caveats to
+ * be aware of:
+ * -# If the device is already in the desired configuration, calling
+ * libusb_set_configuration() using the same configuration value will cause
+ * a lightweight device reset. This may not be desirable behaviour.
+ * -# libusb will be unable to change configuration if the device is in
+ * another configuration and other programs or drivers have claimed
+ * interfaces under that configuration.
+ * -# In the case where the desired configuration is already active, libusb
+ * may not even be able to perform a lightweight device reset. For example,
+ * take my USB keyboard with fingerprint reader: I'm interested in driving
+ * the fingerprint reader interface through libusb, but the kernel's
+ * USB-HID driver will almost always have claimed the keyboard interface.
+ * Because the kernel has claimed an interface, it is not even possible to
+ * perform the lightweight device reset, so libusb_set_configuration() will
+ * fail. (Luckily the device in question only has a single configuration.)
+ *
+ * One solution to some of the above problems is to consider the currently
+ * active configuration. If the configuration we want is already active, then
+ * we don't have to select any configuration:
+\code
+cfg = libusb_get_configuration(dev);
+if (cfg != desired)
+ libusb_set_configuration(dev, desired);
+\endcode
+ *
+ * This is probably suitable for most scenarios, but is inherently racy:
+ * another application or driver may change the selected configuration
+ * after the libusb_get_configuration() call.
+ *
+ * Even in cases where libusb_set_configuration() succeeds, consider that other
+ * applications or drivers may change configuration after your application
+ * calls libusb_set_configuration().
+ *
+ * One possible way to lock your device into a specific configuration is as
+ * follows:
+ * -# Set the desired configuration (or use the logic above to realise that
+ * it is already in the desired configuration)
+ * -# Claim the interface that you wish to use
+ * -# Check that the currently active configuration is the one that you want
+ * to use.
+ *
+ * The above method works because once an interface is claimed, no application
+ * or driver is able to select another configuration.
+ *
+ * \section earlycomp Early transfer completion
+ *
+ * NOTE: This section is currently Linux-centric. I am not sure if any of these
+ * considerations apply to Darwin or other platforms.
+ *
+ * When a transfer completes early (i.e. when less data is received/sent in
+ * any one packet than the transfer buffer allows for) then libusb is designed
+ * to terminate the transfer immediately, not transferring or receiving any
+ * more data unless other transfers have been queued by the user.
+ *
+ * On legacy platforms, libusb is unable to do this in all situations. After
+ * the incomplete packet occurs, "surplus" data may be transferred. For recent
+ * versions of libusb, this information is kept (the data length of the
+ * transfer is updated) and, for device-to-host transfers, any surplus data was
+ * added to the buffer. Still, this is not a nice solution because it loses the
+ * information about the end of the short packet, and the user probably wanted
+ * that surplus data to arrive in the next logical transfer.
+ *
+ *
+ * \section zlp Zero length packets
+ *
+ * - libusb is able to send a packet of zero length to an endpoint simply by
+ * submitting a transfer of zero length.
+ * - The \ref libusb_transfer_flags::LIBUSB_TRANSFER_ADD_ZERO_PACKET
+ * "LIBUSB_TRANSFER_ADD_ZERO_PACKET" flag is currently only supported on Linux.
+ */
+
+/**
+ * \page contexts Contexts
+ *
+ * It is possible that libusb may be used simultaneously from two independent
+ * libraries linked into the same executable. For example, if your application
+ * has a plugin-like system which allows the user to dynamically load a range
+ * of modules into your program, it is feasible that two independently
+ * developed modules may both use libusb.
+ *
+ * libusb is written to allow for these multiple user scenarios. The two
+ * "instances" of libusb will not interfere: libusb_set_debug() calls
+ * from one user will not affect the same settings for other users, other
+ * users can continue using libusb after one of them calls libusb_exit(), etc.
+ *
+ * This is made possible through libusb's context concept. When you
+ * call libusb_init(), you are (optionally) given a context. You can then pass
+ * this context pointer back into future libusb functions.
+ *
+ * In order to keep things simple for more simplistic applications, it is
+ * legal to pass NULL to all functions requiring a context pointer (as long as
+ * you're sure no other code will attempt to use libusb from the same process).
+ * When you pass NULL, the default context will be used. The default context
+ * is created the first time a process calls libusb_init() when no other
+ * context is alive. Contexts are destroyed during libusb_exit().
+ *
+ * The default context is reference-counted and can be shared. That means that
+ * if libusb_init(NULL) is called twice within the same process, the two
+ * users end up sharing the same context. The deinitialization and freeing of
+ * the default context will only happen when the last user calls libusb_exit().
+ * In other words, the default context is created and initialized when its
+ * reference count goes from 0 to 1, and is deinitialized and destroyed when
+ * its reference count goes from 1 to 0.
+ *
+ * You may be wondering why only a subset of libusb functions require a
+ * context pointer in their function definition. Internally, libusb stores
+ * context pointers in other objects (e.g. libusb_device instances) and hence
+ * can infer the context from those objects.
+ */
+
+/**
+ * @defgroup lib Library initialization/deinitialization
+ * This page details how to initialize and deinitialize libusb. Initialization
+ * must be performed before using any libusb functionality, and similarly you
+ * must not call any libusb functions after deinitialization.
+ */
+
+/**
+ * @defgroup dev Device handling and enumeration
+ * The functionality documented below is designed to help with the following
+ * operations:
+ * - Enumerating the USB devices currently attached to the system
+ * - Choosing a device to operate from your software
+ * - Opening and closing the chosen device
+ *
+ * \section nutshell In a nutshell...
+ *
+ * The description below really makes things sound more complicated than they
+ * actually are. The following sequence of function calls will be suitable
+ * for almost all scenarios and does not require you to have such a deep
+ * understanding of the resource management issues:
+ * \code
+// discover devices
+libusb_device **list;
+libusb_device *found = NULL;
+ssize_t cnt = libusb_get_device_list(NULL, &list);
+ssize_t i = 0;
+int err = 0;
+if (cnt < 0)
+ error();
+
+for (i = 0; i < cnt; i++) {
+ libusb_device *device = list[i];
+ if (is_interesting(device)) {
+ found = device;
+ break;
+ }
+}
+
+if (found) {
+ libusb_device_handle *handle;
+
+ err = libusb_open(found, &handle);
+ if (err)
+ error();
+ // etc
+}
+
+libusb_free_device_list(list, 1);
+\endcode
+ *
+ * The two important points:
+ * - You asked libusb_free_device_list() to unreference the devices (2nd
+ * parameter)
+ * - You opened the device before freeing the list and unreferencing the
+ * devices
+ *
+ * If you ended up with a handle, you can now proceed to perform I/O on the
+ * device.
+ *
+ * \section devshandles Devices and device handles
+ * libusb has a concept of a USB device, represented by the
+ * \ref libusb_device opaque type. A device represents a USB device that
+ * is currently or was previously connected to the system. Using a reference
+ * to a device, you can determine certain information about the device (e.g.
+ * you can read the descriptor data).
+ *
+ * The libusb_get_device_list() function can be used to obtain a list of
+ * devices currently connected to the system. This is known as device
+ * discovery.
+ *
+ * Just because you have a reference to a device does not mean it is
+ * necessarily usable. The device may have been unplugged, you may not have
+ * permission to operate such device, or another program or driver may be
+ * using the device.
+ *
+ * When you've found a device that you'd like to operate, you must ask
+ * libusb to open the device using the libusb_open() function. Assuming
+ * success, libusb then returns you a device handle
+ * (a \ref libusb_device_handle pointer). All "real" I/O operations then
+ * operate on the handle rather than the original device pointer.
+ *
+ * \section devref Device discovery and reference counting
+ *
+ * Device discovery (i.e. calling libusb_get_device_list()) returns a
+ * freshly-allocated list of devices. The list itself must be freed when
+ * you are done with it. libusb also needs to know when it is OK to free
+ * the contents of the list - the devices themselves.
+ *
+ * To handle these issues, libusb provides you with two separate items:
+ * - A function to free the list itself
+ * - A reference counting system for the devices inside
+ *
+ * New devices presented by the libusb_get_device_list() function all have a
+ * reference count of 1. You can increase and decrease reference count using
+ * libusb_ref_device() and libusb_unref_device(). A device is destroyed when
+ * its reference count reaches 0.
+ *
+ * With the above information in mind, the process of opening a device can
+ * be viewed as follows:
+ * -# Discover devices using libusb_get_device_list().
+ * -# Choose the device that you want to operate, and call libusb_open().
+ * -# Unref all devices in the discovered device list.
+ * -# Free the discovered device list.
+ *
+ * The order is important - you must not unreference the device before
+ * attempting to open it, because unreferencing it may destroy the device.
+ *
+ * For convenience, the libusb_free_device_list() function includes a
+ * parameter to optionally unreference all the devices in the list before
+ * freeing the list itself. This combines steps 3 and 4 above.
+ *
+ * As an implementation detail, libusb_open() actually adds a reference to
+ * the device in question. This is because the device remains available
+ * through the handle via libusb_get_device(). The reference is deleted during
+ * libusb_close().
+ */
+
+/** @defgroup misc Miscellaneous */
+
+/* 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. */
+#define DISCOVERED_DEVICES_SIZE_STEP 8
+
+static struct discovered_devs *discovered_devs_alloc(void)
+{
+ struct discovered_devs *ret =
+ malloc(sizeof(*ret) + (sizeof(void *) * DISCOVERED_DEVICES_SIZE_STEP));
+
+ if (ret) {
+ ret->len = 0;
+ ret->capacity = DISCOVERED_DEVICES_SIZE_STEP;
+ }
+ return ret;
+}
+
+/* append a device to the discovered devices collection. may realloc itself,
+ * returning new discdevs. returns NULL on realloc failure. */
+struct discovered_devs *discovered_devs_append(
+ struct discovered_devs *discdevs, struct libusb_device *dev)
+{
+ size_t len = discdevs->len;
+ size_t capacity;
+
+ /* if there is space, just append the device */
+ if (len < discdevs->capacity) {
+ discdevs->devices[len] = libusb_ref_device(dev);
+ discdevs->len++;
+ return discdevs;
+ }
+
+ /* exceeded capacity, need to grow */
+ usbi_dbg("need to increase capacity");
+ capacity = discdevs->capacity + DISCOVERED_DEVICES_SIZE_STEP;
+ discdevs = usbi_reallocf(discdevs,
+ sizeof(*discdevs) + (sizeof(void *) * capacity));
+ if (discdevs) {
+ discdevs->capacity = capacity;
+ discdevs->devices[len] = libusb_ref_device(dev);
+ discdevs->len++;
+ }
+
+ return discdevs;
+}
+
+static void discovered_devs_free(struct discovered_devs *discdevs)
+{
+ size_t i;
+
+ for (i = 0; i < discdevs->len; i++)
+ libusb_unref_device(discdevs->devices[i]);
+
+ free(discdevs);
+}
+
+/* Allocate a new device with a specific session ID. The returned device has
+ * a reference count of 1. */
+struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
+ unsigned long session_id)
+{
+ size_t priv_size = usbi_backend->device_priv_size;
+ struct libusb_device *dev = calloc(1, sizeof(*dev) + priv_size);
+ int r;
+
+ if (!dev)
+ return NULL;
+
+ r = usbi_mutex_init(&dev->lock, NULL);
+ if (r) {
+ free(dev);
+ return NULL;
+ }
+
+ dev->ctx = ctx;
+ dev->refcnt = 1;
+ dev->session_data = session_id;
+ dev->speed = LIBUSB_SPEED_UNKNOWN;
+
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ usbi_connect_device (dev);
+ }
+
+ return dev;
+}
+
+void usbi_connect_device(struct libusb_device *dev)
+{
+ libusb_hotplug_message message;
+ ssize_t ret;
+
+ memset(&message, 0, sizeof(message));
+ message.event = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED;
+ message.device = dev;
+ dev->attached = 1;
+
+ usbi_mutex_lock(&dev->ctx->usb_devs_lock);
+ list_add(&dev->list, &dev->ctx->usb_devs);
+ usbi_mutex_unlock(&dev->ctx->usb_devs_lock);
+
+ /* Signal that an event has occurred for this device if we support hotplug AND
+ * the hotplug pipe is ready. This prevents an event from getting raised during
+ * initial enumeration. */
+ if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_pipe[1] > 0) {
+ ret = usbi_write(dev->ctx->hotplug_pipe[1], &message, sizeof(message));
+ if (sizeof (message) != ret) {
+ usbi_err(DEVICE_CTX(dev), "error writing hotplug message");
+ }
+ }
+}
+
+void usbi_disconnect_device(struct libusb_device *dev)
+{
+ libusb_hotplug_message message;
+ struct libusb_context *ctx = dev->ctx;
+ ssize_t ret;
+
+ memset(&message, 0, sizeof(message));
+ message.event = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT;
+ message.device = dev;
+ usbi_mutex_lock(&dev->lock);
+ dev->attached = 0;
+ usbi_mutex_unlock(&dev->lock);
+
+ usbi_mutex_lock(&ctx->usb_devs_lock);
+ list_del(&dev->list);
+ usbi_mutex_unlock(&ctx->usb_devs_lock);
+
+ /* Signal that an event has occurred for this device if we support hotplug AND
+ * the hotplug pipe is ready. This prevents an event from getting raised during
+ * initial enumeration. libusb_handle_events will take care of dereferencing the
+ * device. */
+ if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_pipe[1] > 0) {
+ ret = usbi_write(dev->ctx->hotplug_pipe[1], &message, sizeof(message));
+ if (sizeof(message) != ret) {
+ usbi_err(DEVICE_CTX(dev), "error writing hotplug message");
+ }
+ }
+}
+
+/* Perform some final sanity checks on a newly discovered device. If this
+ * function fails (negative return code), the device should not be added
+ * to the discovered device list. */
+int usbi_sanitize_device(struct libusb_device *dev)
+{
+ int r;
+ uint8_t num_configurations;
+
+ r = usbi_device_cache_descriptor(dev);
+ if (r < 0)
+ return r;
+
+ num_configurations = dev->device_descriptor.bNumConfigurations;
+ if (num_configurations > USB_MAXCONFIG) {
+ usbi_err(DEVICE_CTX(dev), "too many configurations");
+ return LIBUSB_ERROR_IO;
+ } else if (0 == num_configurations)
+ usbi_dbg("zero configurations, maybe an unauthorized device");
+
+ dev->num_configurations = num_configurations;
+ return 0;
+}
+
+/* Examine libusb's internal list of known devices, looking for one with
+ * a specific session ID. Returns the matching device if it was found, and
+ * NULL otherwise. */
+struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx,
+ unsigned long session_id)
+{
+ struct libusb_device *dev;
+ struct libusb_device *ret = NULL;
+
+ usbi_mutex_lock(&ctx->usb_devs_lock);
+ list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device)
+ if (dev->session_data == session_id) {
+ ret = libusb_ref_device(dev);
+ break;
+ }
+ usbi_mutex_unlock(&ctx->usb_devs_lock);
+
+ return ret;
+}
+
+/** @ingroup dev
+ * Returns a list of USB devices currently attached to the system. This is
+ * your entry point into finding a USB device to operate.
+ *
+ * You are expected to unreference all the devices when you are done with
+ * them, and then free the list with libusb_free_device_list(). Note that
+ * libusb_free_device_list() can unref all the devices for you. Be careful
+ * not to unreference a device you are about to open until after you have
+ * opened it.
+ *
+ * This return value of this function indicates the number of devices in
+ * the resultant list. The list is actually one element larger, as it is
+ * NULL-terminated.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param list output location for a list of devices. Must be later freed with
+ * libusb_free_device_list().
+ * \returns the number of devices in the outputted list, or any
+ * \ref libusb_error according to errors encountered by the backend.
+ */
+ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
+ libusb_device ***list)
+{
+ struct discovered_devs *discdevs = discovered_devs_alloc();
+ struct libusb_device **ret;
+ int r = 0;
+ ssize_t i, len;
+ USBI_GET_CONTEXT(ctx);
+ usbi_dbg("");
+
+ if (!discdevs)
+ return LIBUSB_ERROR_NO_MEM;
+
+ if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ /* backend provides hotplug support */
+ struct libusb_device *dev;
+
+ if (usbi_backend->hotplug_poll)
+ usbi_backend->hotplug_poll();
+
+ usbi_mutex_lock(&ctx->usb_devs_lock);
+ list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) {
+ discdevs = discovered_devs_append(discdevs, dev);
+
+ if (!discdevs) {
+ r = LIBUSB_ERROR_NO_MEM;
+ break;
+ }
+ }
+ usbi_mutex_unlock(&ctx->usb_devs_lock);
+ } else {
+ /* backend does not provide hotplug support */
+ r = usbi_backend->get_device_list(ctx, &discdevs);
+ }
+
+ if (r < 0) {
+ len = r;
+ goto out;
+ }
+
+ /* convert discovered_devs into a list */
+ len = discdevs->len;
+ ret = calloc(len + 1, sizeof(struct libusb_device *));
+ if (!ret) {
+ len = LIBUSB_ERROR_NO_MEM;
+ goto out;
+ }
+
+ ret[len] = NULL;
+ for (i = 0; i < len; i++) {
+ struct libusb_device *dev = discdevs->devices[i];
+ ret[i] = libusb_ref_device(dev);
+ }
+ *list = ret;
+
+out:
+ discovered_devs_free(discdevs);
+ return len;
+}
+
+/** \ingroup dev
+ * Frees a list of devices previously discovered using
+ * libusb_get_device_list(). If the unref_devices parameter is set, the
+ * reference count of each device in the list is decremented by 1.
+ * \param list the list to free
+ * \param unref_devices whether to unref the devices in the list
+ */
+void API_EXPORTED libusb_free_device_list(libusb_device **list,
+ int unref_devices)
+{
+ if (!list)
+ return;
+
+ if (unref_devices) {
+ int i = 0;
+ struct libusb_device *dev;
+
+ while ((dev = list[i++]) != NULL)
+ libusb_unref_device(dev);
+ }
+ free(list);
+}
+
+/** \ingroup dev
+ * Get the number of the bus that a device is connected to.
+ * \param dev a device
+ * \returns the bus number
+ */
+uint8_t API_EXPORTED libusb_get_bus_number(libusb_device *dev)
+{
+ return dev->bus_number;
+}
+
+/** \ingroup dev
+ * Get the number of the port that a device is connected to.
+ * Unless the OS does something funky, or you are hot-plugging USB extension cards,
+ * the port number returned by this call is usually guaranteed to be uniquely tied
+ * to a physical port, meaning that different devices plugged on the same physical
+ * port should return the same port number.
+ *
+ * But outside of this, there is no guarantee that the port number returned by this
+ * call will remain the same, or even match the order in which ports have been
+ * numbered by the HUB/HCD manufacturer.
+ *
+ * \param dev a device
+ * \returns the port number (0 if not available)
+ */
+uint8_t API_EXPORTED libusb_get_port_number(libusb_device *dev)
+{
+ return dev->port_number;
+}
+
+/** \ingroup dev
+ * Get the list of all port numbers from root for the specified device
+ *
+ * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ * \param dev a device
+ * \param port_numbers the array that should contain the port numbers
+ * \param port_numbers_len the maximum length of the array. As per the USB 3.0
+ * specs, the current maximum limit for the depth is 7.
+ * \returns the number of elements filled
+ * \returns LIBUSB_ERROR_OVERFLOW if the array is too small
+ */
+int API_EXPORTED libusb_get_port_numbers(libusb_device *dev,
+ uint8_t* port_numbers, int port_numbers_len)
+{
+ int i = port_numbers_len;
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+
+ if (port_numbers_len <= 0)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ // HCDs can be listed as devices with port #0
+ while((dev) && (dev->port_number != 0)) {
+ if (--i < 0) {
+ usbi_warn(ctx, "port numbers array is too small");
+ return LIBUSB_ERROR_OVERFLOW;
+ }
+ port_numbers[i] = dev->port_number;
+ dev = dev->parent_dev;
+ }
+ if (i < port_numbers_len)
+ memmove(port_numbers, &port_numbers[i], port_numbers_len - i);
+ return port_numbers_len - i;
+}
+
+/** \ingroup dev
+ * Deprecated please use libusb_get_port_numbers instead.
+ */
+int API_EXPORTED libusb_get_port_path(libusb_context *ctx, libusb_device *dev,
+ uint8_t* port_numbers, uint8_t port_numbers_len)
+{
+ UNUSED(ctx);
+
+ return libusb_get_port_numbers(dev, port_numbers, port_numbers_len);
+}
+
+/** \ingroup dev
+ * Get the the parent from the specified device.
+ * \param dev a device
+ * \returns the device parent or NULL if not available
+ * You should issue a \ref libusb_get_device_list() before calling this
+ * function and make sure that you only access the parent before issuing
+ * \ref libusb_free_device_list(). The reason is that libusb currently does
+ * not maintain a permanent list of device instances, and therefore can
+ * only guarantee that parents are fully instantiated within a
+ * libusb_get_device_list() - libusb_free_device_list() block.
+ */
+DEFAULT_VISIBILITY
+libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev)
+{
+ return dev->parent_dev;
+}
+
+/** \ingroup dev
+ * Get the address of the device on the bus it is connected to.
+ * \param dev a device
+ * \returns the device address
+ */
+uint8_t API_EXPORTED libusb_get_device_address(libusb_device *dev)
+{
+ return dev->device_address;
+}
+
+/** \ingroup dev
+ * Get the negotiated connection speed for a device.
+ * \param dev a device
+ * \returns a \ref libusb_speed code, where LIBUSB_SPEED_UNKNOWN means that
+ * the OS doesn't know or doesn't support returning the negotiated speed.
+ */
+int API_EXPORTED libusb_get_device_speed(libusb_device *dev)
+{
+ return dev->speed;
+}
+
+static const struct libusb_endpoint_descriptor *find_endpoint(
+ struct libusb_config_descriptor *config, unsigned char endpoint)
+{
+ int iface_idx;
+ for (iface_idx = 0; iface_idx < config->bNumInterfaces; iface_idx++) {
+ const struct libusb_interface *iface = &config->interface[iface_idx];
+ int altsetting_idx;
+
+ for (altsetting_idx = 0; altsetting_idx < iface->num_altsetting;
+ altsetting_idx++) {
+ const struct libusb_interface_descriptor *altsetting
+ = &iface->altsetting[altsetting_idx];
+ int ep_idx;
+
+ for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) {
+ const struct libusb_endpoint_descriptor *ep =
+ &altsetting->endpoint[ep_idx];
+ if (ep->bEndpointAddress == endpoint)
+ return ep;
+ }
+ }
+ }
+ return NULL;
+}
+
+/** \ingroup dev
+ * Convenience function to retrieve the wMaxPacketSize value for a particular
+ * endpoint in the active device configuration.
+ *
+ * This function was originally intended to be of assistance when setting up
+ * isochronous transfers, but a design mistake resulted in this function
+ * instead. It simply returns the wMaxPacketSize value without considering
+ * its contents. If you're dealing with isochronous transfers, you probably
+ * want libusb_get_max_iso_packet_size() instead.
+ *
+ * \param dev a device
+ * \param endpoint address of the endpoint in question
+ * \returns the wMaxPacketSize value
+ * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * \returns LIBUSB_ERROR_OTHER on other failure
+ */
+int API_EXPORTED libusb_get_max_packet_size(libusb_device *dev,
+ unsigned char endpoint)
+{
+ struct libusb_config_descriptor *config;
+ const struct libusb_endpoint_descriptor *ep;
+ int r;
+
+ r = libusb_get_active_config_descriptor(dev, &config);
+ if (r < 0) {
+ usbi_err(DEVICE_CTX(dev),
+ "could not retrieve active config descriptor");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ ep = find_endpoint(config, endpoint);
+ if (!ep) {
+ r = LIBUSB_ERROR_NOT_FOUND;
+ goto out;
+ }
+
+ r = ep->wMaxPacketSize;
+
+out:
+ libusb_free_config_descriptor(config);
+ return r;
+}
+
+/** \ingroup dev
+ * Calculate the maximum packet size which a specific endpoint is capable is
+ * sending or receiving in the duration of 1 microframe
+ *
+ * Only the active configuration is examined. The calculation is based on the
+ * wMaxPacketSize field in the endpoint descriptor as described in section
+ * 9.6.6 in the USB 2.0 specifications.
+ *
+ * If acting on an isochronous or interrupt endpoint, this function will
+ * multiply the value found in bits 0:10 by the number of transactions per
+ * microframe (determined by bits 11:12). Otherwise, this function just
+ * returns the numeric value found in bits 0:10.
+ *
+ * This function is useful for setting up isochronous transfers, for example
+ * you might pass the return value from this function to
+ * libusb_set_iso_packet_lengths() in order to set the length field of every
+ * isochronous packet in a transfer.
+ *
+ * Since v1.0.3.
+ *
+ * \param dev a device
+ * \param endpoint address of the endpoint in question
+ * \returns the maximum packet size which can be sent/received on this endpoint
+ * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * \returns LIBUSB_ERROR_OTHER on other failure
+ */
+int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev,
+ unsigned char endpoint)
+{
+ struct libusb_config_descriptor *config;
+ const struct libusb_endpoint_descriptor *ep;
+ enum libusb_transfer_type ep_type;
+ uint16_t val;
+ int r;
+
+ r = libusb_get_active_config_descriptor(dev, &config);
+ if (r < 0) {
+ usbi_err(DEVICE_CTX(dev),
+ "could not retrieve active config descriptor");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ ep = find_endpoint(config, endpoint);
+ if (!ep) {
+ r = LIBUSB_ERROR_NOT_FOUND;
+ goto out;
+ }
+
+ val = ep->wMaxPacketSize;
+ ep_type = (enum libusb_transfer_type) (ep->bmAttributes & 0x3);
+
+ r = val & 0x07ff;
+ if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
+ || ep_type == LIBUSB_TRANSFER_TYPE_INTERRUPT)
+ r *= (1 + ((val >> 11) & 3));
+
+out:
+ libusb_free_config_descriptor(config);
+ return r;
+}
+
+/** \ingroup dev
+ * Increment the reference count of a device.
+ * \param dev the device to reference
+ * \returns the same device
+ */
+DEFAULT_VISIBILITY
+libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev)
+{
+ usbi_mutex_lock(&dev->lock);
+ dev->refcnt++;
+ usbi_mutex_unlock(&dev->lock);
+ return dev;
+}
+
+/** \ingroup dev
+ * Decrement the reference count of a device. If the decrement operation
+ * causes the reference count to reach zero, the device shall be destroyed.
+ * \param dev the device to unreference
+ */
+void API_EXPORTED libusb_unref_device(libusb_device *dev)
+{
+ int refcnt;
+
+ if (!dev)
+ return;
+
+ usbi_mutex_lock(&dev->lock);
+ refcnt = --dev->refcnt;
+ usbi_mutex_unlock(&dev->lock);
+
+ if (refcnt == 0) {
+ usbi_dbg("destroy device %d.%d", dev->bus_number, dev->device_address);
+
+ libusb_unref_device(dev->parent_dev);
+
+ if (usbi_backend->destroy_device)
+ usbi_backend->destroy_device(dev);
+
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ /* backend does not support hotplug */
+ usbi_disconnect_device(dev);
+ }
+
+ usbi_mutex_destroy(&dev->lock);
+ free(dev);
+ }
+}
+
+/*
+ * Interrupt the iteration of the event handling thread, so that it picks
+ * up the new fd.
+ */
+void usbi_fd_notification(struct libusb_context *ctx)
+{
+ unsigned char dummy = 1;
+ ssize_t r;
+
+ if (ctx == NULL)
+ return;
+
+ /* record that we are messing with poll fds */
+ usbi_mutex_lock(&ctx->pollfd_modify_lock);
+ ctx->pollfd_modify++;
+ usbi_mutex_unlock(&ctx->pollfd_modify_lock);
+
+ /* write some data on control pipe to interrupt event handlers */
+ r = usbi_write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy));
+ if (r <= 0) {
+ usbi_warn(ctx, "internal signalling write failed");
+ usbi_mutex_lock(&ctx->pollfd_modify_lock);
+ ctx->pollfd_modify--;
+ usbi_mutex_unlock(&ctx->pollfd_modify_lock);
+ return;
+ }
+
+ /* take event handling lock */
+ libusb_lock_events(ctx);
+
+ /* read the dummy data */
+ r = usbi_read(ctx->ctrl_pipe[0], &dummy, sizeof(dummy));
+ if (r <= 0)
+ usbi_warn(ctx, "internal signalling read failed");
+
+ /* we're done with modifying poll fds */
+ usbi_mutex_lock(&ctx->pollfd_modify_lock);
+ ctx->pollfd_modify--;
+ usbi_mutex_unlock(&ctx->pollfd_modify_lock);
+
+ /* Release event handling lock and wake up event waiters */
+ libusb_unlock_events(ctx);
+}
+
+/** \ingroup dev
+ * Open a device and obtain a device handle. A handle allows you to perform
+ * I/O on the device in question.
+ *
+ * Internally, this function adds a reference to the device and makes it
+ * available to you through libusb_get_device(). This reference is removed
+ * during libusb_close().
+ *
+ * This is a non-blocking function; no requests are sent over the bus.
+ *
+ * \param dev the device to open
+ * \param handle output location for the returned device handle pointer. Only
+ * populated when the return code is 0.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NO_MEM on memory allocation failure
+ * \returns LIBUSB_ERROR_ACCESS if the user has insufficient permissions
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+int API_EXPORTED libusb_open(libusb_device *dev,
+ libusb_device_handle **handle)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ struct libusb_device_handle *_handle;
+ size_t priv_size = usbi_backend->device_handle_priv_size;
+ int r;
+ usbi_dbg("open %d.%d", dev->bus_number, dev->device_address);
+
+ if (!dev->attached) {
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ _handle = malloc(sizeof(*_handle) + priv_size);
+ if (!_handle)
+ return LIBUSB_ERROR_NO_MEM;
+
+ r = usbi_mutex_init(&_handle->lock, NULL);
+ if (r) {
+ free(_handle);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ _handle->dev = libusb_ref_device(dev);
+ _handle->auto_detach_kernel_driver = 0;
+ _handle->claimed_interfaces = 0;
+ memset(&_handle->os_priv, 0, priv_size);
+
+ r = usbi_backend->open(_handle);
+ if (r < 0) {
+ usbi_dbg("open %d.%d returns %d", dev->bus_number, dev->device_address, r);
+ libusb_unref_device(dev);
+ usbi_mutex_destroy(&_handle->lock);
+ free(_handle);
+ return r;
+ }
+
+ usbi_mutex_lock(&ctx->open_devs_lock);
+ list_add(&_handle->list, &ctx->open_devs);
+ usbi_mutex_unlock(&ctx->open_devs_lock);
+ *handle = _handle;
+
+ /* At this point, we want to interrupt any existing event handlers so
+ * that they realise the addition of the new device's poll fd. One
+ * example when this is desirable is if the user is running a separate
+ * dedicated libusb events handling thread, which is running with a long
+ * or infinite timeout. We want to interrupt that iteration of the loop,
+ * so that it picks up the new fd, and then continues. */
+ usbi_fd_notification(ctx);
+
+ return 0;
+}
+
+/** \ingroup dev
+ * Convenience function for finding a device with a particular
+ * idVendor/idProduct combination. This function is intended
+ * for those scenarios where you are using libusb to knock up a quick test
+ * application - it allows you to avoid calling libusb_get_device_list() and
+ * worrying about traversing/freeing the list.
+ *
+ * This function has limitations and is hence not intended for use in real
+ * applications: if multiple devices have the same IDs it will only
+ * give you the first one, etc.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param vendor_id the idVendor value to search for
+ * \param product_id the idProduct value to search for
+ * \returns a handle for the first found device, or NULL on error or if the
+ * device could not be found. */
+DEFAULT_VISIBILITY
+libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid(
+ libusb_context *ctx, uint16_t vendor_id, uint16_t product_id)
+{
+ struct libusb_device **devs;
+ struct libusb_device *found = NULL;
+ struct libusb_device *dev;
+ struct libusb_device_handle *handle = NULL;
+ size_t i = 0;
+ int r;
+
+ if (libusb_get_device_list(ctx, &devs) < 0)
+ return NULL;
+
+ while ((dev = devs[i++]) != NULL) {
+ struct libusb_device_descriptor desc;
+ r = libusb_get_device_descriptor(dev, &desc);
+ if (r < 0)
+ goto out;
+ if (desc.idVendor == vendor_id && desc.idProduct == product_id) {
+ found = dev;
+ break;
+ }
+ }
+
+ if (found) {
+ r = libusb_open(found, &handle);
+ if (r < 0)
+ handle = NULL;
+ }
+
+out:
+ libusb_free_device_list(devs, 1);
+ return handle;
+}
+
+static void do_close(struct libusb_context *ctx,
+ struct libusb_device_handle *dev_handle)
+{
+ struct usbi_transfer *itransfer;
+ struct usbi_transfer *tmp;
+
+ libusb_lock_events(ctx);
+
+ /* remove any transfers in flight that are for this device */
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+
+ /* safe iteration because transfers may be being deleted */
+ list_for_each_entry_safe(itransfer, tmp, &ctx->flying_transfers, list, struct usbi_transfer) {
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ if (transfer->dev_handle != dev_handle)
+ continue;
+
+ if (!(itransfer->flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) {
+ usbi_err(ctx, "Device handle closed while transfer was still being processed, but the device is still connected as far as we know");
+
+ if (itransfer->flags & USBI_TRANSFER_CANCELLING)
+ usbi_warn(ctx, "A cancellation for an in-flight transfer hasn't completed but closing the device handle");
+ else
+ usbi_err(ctx, "A cancellation hasn't even been scheduled on the transfer for which the device is closing");
+ }
+
+ /* remove from the list of in-flight transfers and make sure
+ * we don't accidentally use the device handle in the future
+ * (or that such accesses will be easily caught and identified as a crash)
+ */
+ usbi_mutex_lock(&itransfer->lock);
+ list_del(&itransfer->list);
+ transfer->dev_handle = NULL;
+ usbi_mutex_unlock(&itransfer->lock);
+
+ /* it is up to the user to free up the actual transfer struct. this is
+ * just making sure that we don't attempt to process the transfer after
+ * the device handle is invalid
+ */
+ usbi_dbg("Removed transfer %p from the in-flight list because device handle %p closed",
+ transfer, dev_handle);
+ }
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
+ libusb_unlock_events(ctx);
+
+ usbi_mutex_lock(&ctx->open_devs_lock);
+ list_del(&dev_handle->list);
+ usbi_mutex_unlock(&ctx->open_devs_lock);
+
+ usbi_backend->close(dev_handle);
+ libusb_unref_device(dev_handle->dev);
+ usbi_mutex_destroy(&dev_handle->lock);
+ free(dev_handle);
+}
+
+/** \ingroup dev
+ * Close a device handle. Should be called on all open handles before your
+ * application exits.
+ *
+ * Internally, this function destroys the reference that was added by
+ * libusb_open() on the given device.
+ *
+ * This is a non-blocking function; no requests are sent over the bus.
+ *
+ * \param dev_handle the handle to close
+ */
+void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
+{
+ struct libusb_context *ctx;
+ unsigned char dummy = 1;
+ ssize_t r;
+
+ if (!dev_handle)
+ return;
+ usbi_dbg("");
+
+ ctx = HANDLE_CTX(dev_handle);
+
+ /* Similarly to libusb_open(), we want to interrupt all event handlers
+ * at this point. More importantly, we want to perform the actual close of
+ * the device while holding the event handling lock (preventing any other
+ * thread from doing event handling) because we will be removing a file
+ * descriptor from the polling loop. */
+
+ /* record that we are messing with poll fds */
+ usbi_mutex_lock(&ctx->pollfd_modify_lock);
+ ctx->pollfd_modify++;
+ usbi_mutex_unlock(&ctx->pollfd_modify_lock);
+
+ /* write some data on control pipe to interrupt event handlers */
+ r = usbi_write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy));
+ if (r <= 0) {
+ usbi_warn(ctx, "internal signalling write failed, closing anyway");
+ do_close(ctx, dev_handle);
+ usbi_mutex_lock(&ctx->pollfd_modify_lock);
+ ctx->pollfd_modify--;
+ usbi_mutex_unlock(&ctx->pollfd_modify_lock);
+ return;
+ }
+
+ /* take event handling lock */
+ libusb_lock_events(ctx);
+
+ /* read the dummy data */
+ r = usbi_read(ctx->ctrl_pipe[0], &dummy, sizeof(dummy));
+ if (r <= 0)
+ usbi_warn(ctx, "internal signalling read failed, closing anyway");
+
+ /* Close the device */
+ do_close(ctx, dev_handle);
+
+ /* we're done with modifying poll fds */
+ usbi_mutex_lock(&ctx->pollfd_modify_lock);
+ ctx->pollfd_modify--;
+ usbi_mutex_unlock(&ctx->pollfd_modify_lock);
+
+ /* Release event handling lock and wake up event waiters */
+ libusb_unlock_events(ctx);
+}
+
+/** \ingroup dev
+ * Get the underlying device for a handle. This function does not modify
+ * the reference count of the returned device, so do not feel compelled to
+ * unreference it when you are done.
+ * \param dev_handle a device handle
+ * \returns the underlying device
+ */
+DEFAULT_VISIBILITY
+libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle)
+{
+ return dev_handle->dev;
+}
+
+/** \ingroup dev
+ * Determine the bConfigurationValue of the currently active configuration.
+ *
+ * You could formulate your own control request to obtain this information,
+ * but this function has the advantage that it may be able to retrieve the
+ * information from operating system caches (no I/O involved).
+ *
+ * If the OS does not cache this information, then this function will block
+ * while a control transfer is submitted to retrieve the information.
+ *
+ * This function will return a value of 0 in the config output
+ * parameter if the device is in unconfigured state.
+ *
+ * \param dev a device handle
+ * \param config output location for the bConfigurationValue of the active
+ * configuration (only valid for return code 0)
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev,
+ int *config)
+{
+ int r = LIBUSB_ERROR_NOT_SUPPORTED;
+
+ usbi_dbg("");
+ if (usbi_backend->get_configuration)
+ r = usbi_backend->get_configuration(dev, config);
+
+ if (r == LIBUSB_ERROR_NOT_SUPPORTED) {
+ uint8_t tmp = 0;
+ usbi_dbg("falling back to control message");
+ r = libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN,
+ LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000);
+ if (r == 0) {
+ usbi_err(HANDLE_CTX(dev), "zero bytes returned in ctrl transfer?");
+ r = LIBUSB_ERROR_IO;
+ } else if (r == 1) {
+ r = 0;
+ *config = tmp;
+ } else {
+ usbi_dbg("control failed, error %d", r);
+ }
+ }
+
+ if (r == 0)
+ usbi_dbg("active config %d", *config);
+
+ return r;
+}
+
+/** \ingroup dev
+ * Set the active configuration for a device.
+ *
+ * The operating system may or may not have already set an active
+ * configuration on the device. It is up to your application to ensure the
+ * correct configuration is selected before you attempt to claim interfaces
+ * and perform other operations.
+ *
+ * If you call this function on a device already configured with the selected
+ * configuration, then this function will act as a lightweight device reset:
+ * it will issue a SET_CONFIGURATION request using the current configuration,
+ * causing most USB-related device state to be reset (altsetting reset to zero,
+ * endpoint halts cleared, toggles reset).
+ *
+ * You cannot change/reset configuration if your application has claimed
+ * interfaces. It is advised to set the desired configuration before claiming
+ * interfaces.
+ *
+ * Alternatively you can call libusb_release_interface() first. Note if you
+ * do things this way you must ensure that auto_detach_kernel_driver for
+ * dev is 0, otherwise the kernel driver will be re-attached when you
+ * release the interface(s).
+ *
+ * You cannot change/reset configuration if other applications or drivers have
+ * claimed interfaces.
+ *
+ * A configuration value of -1 will put the device in unconfigured state.
+ * The USB specifications state that a configuration value of 0 does this,
+ * however buggy devices exist which actually have a configuration 0.
+ *
+ * You should always use this function rather than formulating your own
+ * SET_CONFIGURATION control request. This is because the underlying operating
+ * system needs to know when such changes happen.
+ *
+ * This is a blocking function.
+ *
+ * \param dev a device handle
+ * \param configuration the bConfigurationValue of the configuration you
+ * wish to activate, or -1 if you wish to put the device in unconfigured state
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist
+ * \returns LIBUSB_ERROR_BUSY if interfaces are currently claimed
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_set_auto_detach_kernel_driver()
+ */
+int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev,
+ int configuration)
+{
+ usbi_dbg("configuration %d", configuration);
+ return usbi_backend->set_configuration(dev, configuration);
+}
+
+/** \ingroup dev
+ * Claim an interface on a given device handle. You must claim the interface
+ * you wish to use before you can perform I/O on any of its endpoints.
+ *
+ * It is legal to attempt to claim an already-claimed interface, in which
+ * case libusb just returns 0 without doing anything.
+ *
+ * If auto_detach_kernel_driver is set to 1 for dev, the kernel driver
+ * will be detached if necessary, on failure the detach error is returned.
+ *
+ * Claiming of interfaces is a purely logical operation; it does not cause
+ * any requests to be sent over the bus. Interface claiming is used to
+ * instruct the underlying operating system that your application wishes
+ * to take ownership of the interface.
+ *
+ * This is a non-blocking function.
+ *
+ * \param dev a device handle
+ * \param interface_number the bInterfaceNumber of the interface you
+ * wish to claim
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the requested interface does not exist
+ * \returns LIBUSB_ERROR_BUSY if another program or driver has claimed the
+ * interface
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns a LIBUSB_ERROR code on other failure
+ * \see libusb_set_auto_detach_kernel_driver()
+ */
+int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev,
+ int interface_number)
+{
+ int r = 0;
+
+ usbi_dbg("interface %d", interface_number);
+ if (interface_number >= USB_MAXINTERFACES)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ if (!dev->dev->attached)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_mutex_lock(&dev->lock);
+ if (dev->claimed_interfaces & (1 << interface_number))
+ goto out;
+
+ r = usbi_backend->claim_interface(dev, interface_number);
+ if (r == 0)
+ dev->claimed_interfaces |= 1 << interface_number;
+
+out:
+ usbi_mutex_unlock(&dev->lock);
+ return r;
+}
+
+/** \ingroup dev
+ * Release an interface previously claimed with libusb_claim_interface(). You
+ * should release all claimed interfaces before closing a device handle.
+ *
+ * This is a blocking function. A SET_INTERFACE control request will be sent
+ * to the device, resetting interface state to the first alternate setting.
+ *
+ * If auto_detach_kernel_driver is set to 1 for dev, the kernel
+ * driver will be re-attached after releasing the interface.
+ *
+ * \param dev a device handle
+ * \param interface_number the bInterfaceNumber of the
+ * previously-claimed interface
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_set_auto_detach_kernel_driver()
+ */
+int API_EXPORTED libusb_release_interface(libusb_device_handle *dev,
+ int interface_number)
+{
+ int r;
+
+ usbi_dbg("interface %d", interface_number);
+ if (interface_number >= USB_MAXINTERFACES)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ usbi_mutex_lock(&dev->lock);
+ if (!(dev->claimed_interfaces & (1 << interface_number))) {
+ r = LIBUSB_ERROR_NOT_FOUND;
+ goto out;
+ }
+
+ r = usbi_backend->release_interface(dev, interface_number);
+ if (r == 0)
+ dev->claimed_interfaces &= ~(1 << interface_number);
+
+out:
+ usbi_mutex_unlock(&dev->lock);
+ return r;
+}
+
+/** \ingroup dev
+ * Activate an alternate setting for an interface. The interface must have
+ * been previously claimed with libusb_claim_interface().
+ *
+ * You should always use this function rather than formulating your own
+ * SET_INTERFACE control request. This is because the underlying operating
+ * system needs to know when such changes happen.
+ *
+ * This is a blocking function.
+ *
+ * \param dev a device handle
+ * \param interface_number the bInterfaceNumber of the
+ * previously-claimed interface
+ * \param alternate_setting the bAlternateSetting of the alternate
+ * setting to activate
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed, or the
+ * requested alternate setting does not exist
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev,
+ int interface_number, int alternate_setting)
+{
+ usbi_dbg("interface %d altsetting %d",
+ interface_number, alternate_setting);
+ if (interface_number >= USB_MAXINTERFACES)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ usbi_mutex_lock(&dev->lock);
+ if (!dev->dev->attached) {
+ usbi_mutex_unlock(&dev->lock);
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ if (!(dev->claimed_interfaces & (1 << interface_number))) {
+ usbi_mutex_unlock(&dev->lock);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+ usbi_mutex_unlock(&dev->lock);
+
+ return usbi_backend->set_interface_altsetting(dev, interface_number,
+ alternate_setting);
+}
+
+/** \ingroup dev
+ * Clear the halt/stall condition for an endpoint. Endpoints with halt status
+ * are unable to receive or transmit data until the halt condition is stalled.
+ *
+ * You should cancel all pending transfers before attempting to clear the halt
+ * condition.
+ *
+ * This is a blocking function.
+ *
+ * \param dev a device handle
+ * \param endpoint the endpoint to clear halt status
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev,
+ unsigned char endpoint)
+{
+ usbi_dbg("endpoint %x", endpoint);
+ if (!dev->dev->attached)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ return usbi_backend->clear_halt(dev, endpoint);
+}
+
+/** \ingroup dev
+ * Perform a USB port reset to reinitialize a device. The system will attempt
+ * to restore the previous configuration and alternate settings after the
+ * reset has completed.
+ *
+ * If the reset fails, the descriptors change, or the previous state cannot be
+ * restored, the device will appear to be disconnected and reconnected. This
+ * means that the device handle is no longer valid (you should close it) and
+ * rediscover the device. A return code of LIBUSB_ERROR_NOT_FOUND indicates
+ * when this is the case.
+ *
+ * This is a blocking function which usually incurs a noticeable delay.
+ *
+ * \param dev a handle of the device to reset
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the
+ * device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+int API_EXPORTED libusb_reset_device(libusb_device_handle *dev)
+{
+ usbi_dbg("");
+ if (!dev->dev->attached)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ return usbi_backend->reset_device(dev);
+}
+
+/** \ingroup asyncio
+ * Allocate up to num_streams usb bulk streams on the specified endpoints. This
+ * function takes an array of endpoints rather then a single endpoint because
+ * some protocols require that endpoints are setup with similar stream ids.
+ * All endpoints passed in must belong to the same interface.
+ *
+ * Note this function may return less streams then requested. Also note that the
+ * same number of streams are allocated for each endpoint in the endpoint array.
+ *
+ * Stream id 0 is reserved, and should not be used to communicate with devices.
+ * If libusb_alloc_streams() returns with a value of N, you may use stream ids
+ * 1 to N.
+ *
+ * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103
+ *
+ * \param dev a device handle
+ * \param num_streams number of streams to try to allocate
+ * \param endpoints array of endpoints to allocate streams on
+ * \param num_endpoints length of the endpoints array
+ * \returns number of streams allocated, or a LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev,
+ uint32_t num_streams, unsigned char *endpoints, int num_endpoints)
+{
+ usbi_dbg("streams %u eps %d", (unsigned) num_streams, num_endpoints);
+
+ if (!dev->dev->attached)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ if (usbi_backend->alloc_streams)
+ return usbi_backend->alloc_streams(dev, num_streams, endpoints,
+ num_endpoints);
+ else
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+/** \ingroup asyncio
+ * Free usb bulk streams allocated with libusb_alloc_streams().
+ *
+ * Note streams are automatically free-ed when releasing an interface.
+ *
+ * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103
+ *
+ * \param dev a device handle
+ * \param endpoints array of endpoints to free streams on
+ * \param num_endpoints length of the endpoints array
+ * \returns LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_free_streams(libusb_device_handle *dev,
+ unsigned char *endpoints, int num_endpoints)
+{
+ usbi_dbg("eps %d", num_endpoints);
+
+ if (!dev->dev->attached)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ if (usbi_backend->free_streams)
+ return usbi_backend->free_streams(dev, endpoints,
+ num_endpoints);
+ else
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+/** \ingroup dev
+ * Determine if a kernel driver is active on an interface. If a kernel driver
+ * is active, you cannot claim the interface, and libusb will be unable to
+ * perform I/O.
+ *
+ * This functionality is not available on Windows.
+ *
+ * \param dev a device handle
+ * \param interface_number the interface to check
+ * \returns 0 if no kernel driver is active
+ * \returns 1 if a kernel driver is active
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
+ * is not available
+ * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_detach_kernel_driver()
+ */
+int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev,
+ int interface_number)
+{
+ usbi_dbg("interface %d", interface_number);
+
+ if (!dev->dev->attached)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ if (usbi_backend->kernel_driver_active)
+ return usbi_backend->kernel_driver_active(dev, interface_number);
+ else
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+/** \ingroup dev
+ * Detach a kernel driver from an interface. If successful, you will then be
+ * able to claim the interface and perform I/O.
+ *
+ * This functionality is not available on Darwin or Windows.
+ *
+ * Note that libusb itself also talks to the device through a special kernel
+ * driver, if this driver is already attached to the device, this call will
+ * not detach it and return LIBUSB_ERROR_NOT_FOUND.
+ *
+ * \param dev a device handle
+ * \param interface_number the interface to detach the driver from
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active
+ * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
+ * is not available
+ * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_kernel_driver_active()
+ */
+int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev,
+ int interface_number)
+{
+ usbi_dbg("interface %d", interface_number);
+
+ if (!dev->dev->attached)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ if (usbi_backend->detach_kernel_driver)
+ return usbi_backend->detach_kernel_driver(dev, interface_number);
+ else
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+/** \ingroup dev
+ * Re-attach an interface's kernel driver, which was previously detached
+ * using libusb_detach_kernel_driver(). This call is only effective on
+ * Linux and returns LIBUSB_ERROR_NOT_SUPPORTED on all other platforms.
+ *
+ * This functionality is not available on Darwin or Windows.
+ *
+ * \param dev a device handle
+ * \param interface_number the interface to attach the driver from
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active
+ * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
+ * is not available
+ * \returns LIBUSB_ERROR_BUSY if the driver cannot be attached because the
+ * interface is claimed by a program or driver
+ * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_kernel_driver_active()
+ */
+int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev,
+ int interface_number)
+{
+ usbi_dbg("interface %d", interface_number);
+
+ if (!dev->dev->attached)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ if (usbi_backend->attach_kernel_driver)
+ return usbi_backend->attach_kernel_driver(dev, interface_number);
+ else
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+/** \ingroup dev
+ * Enable/disable libusb's automatic kernel driver detachment. When this is
+ * enabled libusb will automatically detach the kernel driver on an interface
+ * when claiming the interface, and attach it when releasing the interface.
+ *
+ * Automatic kernel driver detachment is disabled on newly opened device
+ * handles by default.
+ *
+ * On platforms which do not have LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER
+ * this function will return LIBUSB_ERROR_NOT_SUPPORTED, and libusb will
+ * continue as if this function was never called.
+ *
+ * \param dev a device handle
+ * \param enable whether to enable or disable auto kernel driver detachment
+ *
+ * \returns LIBUSB_SUCCESS on success
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
+ * is not available
+ * \see libusb_claim_interface()
+ * \see libusb_release_interface()
+ * \see libusb_set_configuration()
+ */
+int API_EXPORTED libusb_set_auto_detach_kernel_driver(
+ libusb_device_handle *dev, int enable)
+{
+ if (!(usbi_backend->caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER))
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+
+ dev->auto_detach_kernel_driver = enable;
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup lib
+ * Set log message verbosity.
+ *
+ * The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever
+ * printed. If you choose to increase the message verbosity level, ensure
+ * that your application does not close the stdout/stderr file descriptors.
+ *
+ * You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative
+ * with its message logging and most of the time, will only log messages that
+ * explain error conditions and other oddities. This will help you debug
+ * your software.
+ *
+ * If the LIBUSB_DEBUG environment variable was set when libusb was
+ * initialized, this function does nothing: the message verbosity is fixed
+ * to the value in the environment variable.
+ *
+ * If libusb was compiled without any message logging, this function does
+ * nothing: you'll never get any messages.
+ *
+ * If libusb was compiled with verbose debug message logging, this function
+ * does nothing: you'll always get messages from all levels.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param level debug level to set
+ */
+void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
+{
+ USBI_GET_CONTEXT(ctx);
+ if (!ctx->debug_fixed)
+ ctx->debug = level;
+}
+
+/** \ingroup lib
+ * Initialize libusb. This function must be called before calling any other
+ * libusb function.
+ *
+ * If you do not provide an output location for a context pointer, a default
+ * context will be created. If there was already a default context, it will
+ * be reused (and nothing will be initialized/reinitialized).
+ *
+ * \param context Optional output location for context pointer.
+ * Only valid on return code 0.
+ * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ * \see contexts
+ */
+int API_EXPORTED libusb_init(libusb_context **context)
+{
+ struct libusb_device *dev, *next;
+ char *dbg = getenv("LIBUSB_DEBUG");
+ struct libusb_context *ctx;
+ static int first_init = 1;
+ int r = 0;
+
+ usbi_mutex_static_lock(&default_context_lock);
+
+ if (!timestamp_origin.tv_sec) {
+ usbi_gettimeofday(×tamp_origin, NULL);
+ }
+
+ if (!context && usbi_default_context) {
+ usbi_dbg("reusing default context");
+ default_context_refcnt++;
+ usbi_mutex_static_unlock(&default_context_lock);
+ return 0;
+ }
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err_unlock;
+ }
+
+#ifdef ENABLE_DEBUG_LOGGING
+ ctx->debug = LIBUSB_LOG_LEVEL_DEBUG;
+#endif
+
+ if (dbg) {
+ ctx->debug = atoi(dbg);
+ if (ctx->debug)
+ ctx->debug_fixed = 1;
+ }
+
+ /* default context should be initialized before calling usbi_dbg */
+ if (!usbi_default_context) {
+ usbi_default_context = ctx;
+ default_context_refcnt++;
+ usbi_dbg("created default context");
+ }
+
+ usbi_dbg("libusb v%d.%d.%d.%d", libusb_version_internal.major, libusb_version_internal.minor,
+ libusb_version_internal.micro, libusb_version_internal.nano);
+
+ usbi_mutex_init(&ctx->usb_devs_lock, NULL);
+ usbi_mutex_init(&ctx->open_devs_lock, NULL);
+ usbi_mutex_init(&ctx->hotplug_cbs_lock, NULL);
+ list_init(&ctx->usb_devs);
+ list_init(&ctx->open_devs);
+ list_init(&ctx->hotplug_cbs);
+
+ usbi_mutex_static_lock(&active_contexts_lock);
+ if (first_init) {
+ first_init = 0;
+ list_init (&active_contexts_list);
+ }
+ list_add (&ctx->list, &active_contexts_list);
+ usbi_mutex_static_unlock(&active_contexts_lock);
+
+ if (usbi_backend->init) {
+ r = usbi_backend->init(ctx);
+ if (r)
+ goto err_free_ctx;
+ }
+
+ r = usbi_io_init(ctx);
+ if (r < 0)
+ goto err_backend_exit;
+
+ usbi_mutex_static_unlock(&default_context_lock);
+
+ if (context)
+ *context = ctx;
+
+ return 0;
+
+err_backend_exit:
+ if (usbi_backend->exit)
+ usbi_backend->exit();
+err_free_ctx:
+ if (ctx == usbi_default_context)
+ usbi_default_context = NULL;
+
+ usbi_mutex_static_lock(&active_contexts_lock);
+ list_del (&ctx->list);
+ usbi_mutex_static_unlock(&active_contexts_lock);
+
+ usbi_mutex_lock(&ctx->usb_devs_lock);
+ list_for_each_entry_safe(dev, next, &ctx->usb_devs, list, struct libusb_device) {
+ list_del(&dev->list);
+ libusb_unref_device(dev);
+ }
+ usbi_mutex_unlock(&ctx->usb_devs_lock);
+
+ usbi_mutex_destroy(&ctx->open_devs_lock);
+ usbi_mutex_destroy(&ctx->usb_devs_lock);
+ usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
+
+ free(ctx);
+err_unlock:
+ usbi_mutex_static_unlock(&default_context_lock);
+ return r;
+}
+
+/** \ingroup lib
+ * Deinitialize libusb. Should be called after closing all open devices and
+ * before your application terminates.
+ * \param ctx the context to deinitialize, or NULL for the default context
+ */
+void API_EXPORTED libusb_exit(struct libusb_context *ctx)
+{
+ struct libusb_device *dev, *next;
+ struct timeval tv = { 0, 0 };
+
+ usbi_dbg("");
+ USBI_GET_CONTEXT(ctx);
+
+ /* if working with default context, only actually do the deinitialization
+ * if we're the last user */
+ usbi_mutex_static_lock(&default_context_lock);
+ if (ctx == usbi_default_context) {
+ if (--default_context_refcnt > 0) {
+ usbi_dbg("not destroying default context");
+ usbi_mutex_static_unlock(&default_context_lock);
+ return;
+ }
+ usbi_dbg("destroying default context");
+ usbi_default_context = NULL;
+ }
+ usbi_mutex_static_unlock(&default_context_lock);
+
+ usbi_mutex_static_lock(&active_contexts_lock);
+ list_del (&ctx->list);
+ usbi_mutex_static_unlock(&active_contexts_lock);
+
+ if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ usbi_hotplug_deregister_all(ctx);
+
+ /*
+ * Ensure any pending unplug events are read from the hotplug
+ * pipe. The usb_device-s hold in the events are no longer part
+ * of usb_devs, but the events still hold a reference!
+ *
+ * Note we don't do this if the application has left devices
+ * open (which implies a buggy app) to avoid packet completion
+ * handlers running when the app does not expect them to run.
+ */
+ if (list_empty(&ctx->open_devs))
+ libusb_handle_events_timeout(ctx, &tv);
+
+ usbi_mutex_lock(&ctx->usb_devs_lock);
+ list_for_each_entry_safe(dev, next, &ctx->usb_devs, list, struct libusb_device) {
+ list_del(&dev->list);
+ libusb_unref_device(dev);
+ }
+ usbi_mutex_unlock(&ctx->usb_devs_lock);
+ }
+
+ /* a few sanity checks. don't bother with locking because unless
+ * there is an application bug, nobody will be accessing these. */
+ if (!list_empty(&ctx->usb_devs))
+ usbi_warn(ctx, "some libusb_devices were leaked");
+ if (!list_empty(&ctx->open_devs))
+ usbi_warn(ctx, "application left some devices open");
+
+ usbi_io_exit(ctx);
+ if (usbi_backend->exit)
+ usbi_backend->exit();
+
+ usbi_mutex_destroy(&ctx->open_devs_lock);
+ usbi_mutex_destroy(&ctx->usb_devs_lock);
+ usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
+ free(ctx);
+}
+
+/** \ingroup misc
+ * Check at runtime if the loaded library has a given capability.
+ * This call should be performed after \ref libusb_init(), to ensure the
+ * backend has updated its capability set.
+ *
+ * \param capability the \ref libusb_capability to check for
+ * \returns nonzero if the running library has the capability, 0 otherwise
+ */
+int API_EXPORTED libusb_has_capability(uint32_t capability)
+{
+ switch (capability) {
+ case LIBUSB_CAP_HAS_CAPABILITY:
+ return 1;
+ case LIBUSB_CAP_HAS_HOTPLUG:
+ return !(usbi_backend->get_device_list);
+ case LIBUSB_CAP_HAS_HID_ACCESS:
+ return (usbi_backend->caps & USBI_CAP_HAS_HID_ACCESS);
+ case LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER:
+ return (usbi_backend->caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER);
+ }
+ return 0;
+}
+
+/* this is defined in libusbi.h if needed */
+#ifdef LIBUSB_GETTIMEOFDAY_WIN32
+/*
+ * gettimeofday
+ * Implementation according to:
+ * The Open Group Base Specifications Issue 6
+ * IEEE Std 1003.1, 2004 Edition
+ */
+
+/*
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Contributed by:
+ * Danny Smith
+ */
+
+/* Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */
+#define _W32_FT_OFFSET (116444736000000000)
+
+int usbi_gettimeofday(struct timeval *tp, void *tzp)
+{
+ union {
+ unsigned __int64 ns100; /* Time since 1 Jan 1601, in 100ns units */
+ FILETIME ft;
+ } _now;
+ UNUSED(tzp);
+
+ if(tp) {
+#if defined(OS_WINCE)
+ SYSTEMTIME st;
+ GetSystemTime(&st);
+ SystemTimeToFileTime(&st, &_now.ft);
+#else
+ GetSystemTimeAsFileTime (&_now.ft);
+#endif
+ tp->tv_usec=(long)((_now.ns100 / 10) % 1000000 );
+ tp->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000);
+ }
+ /* Always return 0 as per Open Group Base Specifications Issue 6.
+ Do not set errno on error. */
+ return 0;
+}
+#endif
+
+static void usbi_log_str(struct libusb_context *ctx,
+ enum libusb_log_level level, const char * str)
+{
+#if defined(USE_SYSTEM_LOGGING_FACILITY)
+#if defined(OS_WINDOWS) || defined(OS_WINCE)
+ /* Windows CE only supports the Unicode version of OutputDebugString. */
+ WCHAR wbuf[USBI_MAX_LOG_LEN];
+ MultiByteToWideChar(CP_UTF8, 0, str, -1, wbuf, sizeof(wbuf));
+ OutputDebugStringW(wbuf);
+#elif defined(__ANDROID__)
+ int priority = ANDROID_LOG_UNKNOWN;
+ switch (level) {
+ case LIBUSB_LOG_LEVEL_INFO: priority = ANDROID_LOG_INFO; break;
+ case LIBUSB_LOG_LEVEL_WARNING: priority = ANDROID_LOG_WARN; break;
+ case LIBUSB_LOG_LEVEL_ERROR: priority = ANDROID_LOG_ERROR; break;
+ case LIBUSB_LOG_LEVEL_DEBUG: priority = ANDROID_LOG_DEBUG; break;
+ }
+ __android_log_write(priority, "libusb", str);
+#elif defined(HAVE_SYSLOG_FUNC)
+ int syslog_level = LOG_INFO;
+ switch (level) {
+ case LIBUSB_LOG_LEVEL_INFO: syslog_level = LOG_INFO; break;
+ case LIBUSB_LOG_LEVEL_WARNING: syslog_level = LOG_WARNING; break;
+ case LIBUSB_LOG_LEVEL_ERROR: syslog_level = LOG_ERR; break;
+ case LIBUSB_LOG_LEVEL_DEBUG: syslog_level = LOG_DEBUG; break;
+ }
+ syslog(syslog_level, "%s", str);
+#else /* All of gcc, Clang, XCode seem to use #warning */
+#warning System logging is not supported on this platform. Logging to stderr will be used instead.
+ fputs(str, stderr);
+#endif
+#else
+ fputs(str, stderr);
+#endif /* USE_SYSTEM_LOGGING_FACILITY */
+ UNUSED(ctx);
+ UNUSED(level);
+}
+
+void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
+ const char *function, const char *format, va_list args)
+{
+ const char *prefix = "";
+ char buf[USBI_MAX_LOG_LEN];
+ struct timeval now;
+ int global_debug, header_len, text_len;
+ static int has_debug_header_been_displayed = 0;
+
+#ifdef ENABLE_DEBUG_LOGGING
+ global_debug = 1;
+ UNUSED(ctx);
+#else
+ int ctx_level = 0;
+
+ USBI_GET_CONTEXT(ctx);
+ if (ctx) {
+ ctx_level = ctx->debug;
+ } else {
+ char *dbg = getenv("LIBUSB_DEBUG");
+ if (dbg)
+ ctx_level = atoi(dbg);
+ }
+ global_debug = (ctx_level == LIBUSB_LOG_LEVEL_DEBUG);
+ if (!ctx_level)
+ return;
+ if (level == LIBUSB_LOG_LEVEL_WARNING && ctx_level < LIBUSB_LOG_LEVEL_WARNING)
+ return;
+ if (level == LIBUSB_LOG_LEVEL_INFO && ctx_level < LIBUSB_LOG_LEVEL_INFO)
+ return;
+ if (level == LIBUSB_LOG_LEVEL_DEBUG && ctx_level < LIBUSB_LOG_LEVEL_DEBUG)
+ return;
+#endif
+
+ usbi_gettimeofday(&now, NULL);
+ if ((global_debug) && (!has_debug_header_been_displayed)) {
+ has_debug_header_been_displayed = 1;
+ usbi_log_str(ctx, LIBUSB_LOG_LEVEL_DEBUG, "[timestamp] [threadID] facility level [function call] \n");
+ usbi_log_str(ctx, LIBUSB_LOG_LEVEL_DEBUG, "--------------------------------------------------------------------------------\n");
+ }
+ if (now.tv_usec < timestamp_origin.tv_usec) {
+ now.tv_sec--;
+ now.tv_usec += 1000000;
+ }
+ now.tv_sec -= timestamp_origin.tv_sec;
+ now.tv_usec -= timestamp_origin.tv_usec;
+
+ switch (level) {
+ case LIBUSB_LOG_LEVEL_INFO:
+ prefix = "info";
+ break;
+ case LIBUSB_LOG_LEVEL_WARNING:
+ prefix = "warning";
+ break;
+ case LIBUSB_LOG_LEVEL_ERROR:
+ prefix = "error";
+ break;
+ case LIBUSB_LOG_LEVEL_DEBUG:
+ prefix = "debug";
+ break;
+ case LIBUSB_LOG_LEVEL_NONE:
+ return;
+ default:
+ prefix = "unknown";
+ break;
+ }
+
+ if (global_debug) {
+ header_len = snprintf(buf, sizeof(buf),
+ "[%2d.%06d] [%08x] libusb: %s [%s] ",
+ (int)now.tv_sec, (int)now.tv_usec, usbi_get_tid(), prefix, function);
+ } else {
+ header_len = snprintf(buf, sizeof(buf),
+ "libusb: %s [%s] ", prefix, function);
+ }
+
+ if (header_len < 0 || header_len >= sizeof(buf)) {
+ /* Somehow snprintf failed to write to the buffer,
+ * remove the header so something useful is output. */
+ header_len = 0;
+ }
+ /* Make sure buffer is NUL terminated */
+ buf[header_len] = '\0';
+ text_len = vsnprintf(buf + header_len, sizeof(buf) - header_len,
+ format, args);
+ if (text_len < 0 || text_len + header_len >= sizeof(buf)) {
+ /* Truncated log output. On some platforms a -1 return value means
+ * that the output was truncated. */
+ text_len = sizeof(buf) - header_len;
+ }
+ if (header_len + text_len + sizeof(USBI_LOG_LINE_END) >= sizeof(buf)) {
+ /* Need to truncate the text slightly to fit on the terminator. */
+ text_len -= (header_len + text_len + sizeof(USBI_LOG_LINE_END)) - sizeof(buf);
+ }
+ strcpy(buf + header_len + text_len, USBI_LOG_LINE_END);
+
+ usbi_log_str(ctx, level, buf);
+}
+
+void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
+ const char *function, const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ usbi_log_v(ctx, level, function, format, args);
+ va_end (args);
+}
+
+/** \ingroup misc
+ * Returns a constant NULL-terminated string with the ASCII name of a libusb
+ * error or transfer status code. The caller must not free() the returned
+ * string.
+ *
+ * \param error_code The \ref libusb_error or libusb_transfer_status code to
+ * return the name of.
+ * \returns The error name, or the string **UNKNOWN** if the value of
+ * error_code is not a known error / status code.
+ */
+DEFAULT_VISIBILITY const char * LIBUSB_CALL libusb_error_name(int error_code)
+{
+ switch (error_code) {
+ case LIBUSB_ERROR_IO:
+ return "LIBUSB_ERROR_IO";
+ case LIBUSB_ERROR_INVALID_PARAM:
+ return "LIBUSB_ERROR_INVALID_PARAM";
+ case LIBUSB_ERROR_ACCESS:
+ return "LIBUSB_ERROR_ACCESS";
+ case LIBUSB_ERROR_NO_DEVICE:
+ return "LIBUSB_ERROR_NO_DEVICE";
+ case LIBUSB_ERROR_NOT_FOUND:
+ return "LIBUSB_ERROR_NOT_FOUND";
+ case LIBUSB_ERROR_BUSY:
+ return "LIBUSB_ERROR_BUSY";
+ case LIBUSB_ERROR_TIMEOUT:
+ return "LIBUSB_ERROR_TIMEOUT";
+ case LIBUSB_ERROR_OVERFLOW:
+ return "LIBUSB_ERROR_OVERFLOW";
+ case LIBUSB_ERROR_PIPE:
+ return "LIBUSB_ERROR_PIPE";
+ case LIBUSB_ERROR_INTERRUPTED:
+ return "LIBUSB_ERROR_INTERRUPTED";
+ case LIBUSB_ERROR_NO_MEM:
+ return "LIBUSB_ERROR_NO_MEM";
+ case LIBUSB_ERROR_NOT_SUPPORTED:
+ return "LIBUSB_ERROR_NOT_SUPPORTED";
+ case LIBUSB_ERROR_OTHER:
+ return "LIBUSB_ERROR_OTHER";
+
+ case LIBUSB_TRANSFER_ERROR:
+ return "LIBUSB_TRANSFER_ERROR";
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ return "LIBUSB_TRANSFER_TIMED_OUT";
+ case LIBUSB_TRANSFER_CANCELLED:
+ return "LIBUSB_TRANSFER_CANCELLED";
+ case LIBUSB_TRANSFER_STALL:
+ return "LIBUSB_TRANSFER_STALL";
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ return "LIBUSB_TRANSFER_NO_DEVICE";
+ case LIBUSB_TRANSFER_OVERFLOW:
+ return "LIBUSB_TRANSFER_OVERFLOW";
+
+ case 0:
+ return "LIBUSB_SUCCESS / LIBUSB_TRANSFER_COMPLETED";
+ default:
+ return "**UNKNOWN**";
+ }
+}
+
+/** \ingroup misc
+ * Returns a pointer to const struct libusb_version with the version
+ * (major, minor, micro, nano and rc) of the running library.
+ */
+DEFAULT_VISIBILITY
+const struct libusb_version * LIBUSB_CALL libusb_get_version(void)
+{
+ return &libusb_version_internal;
+}
diff --git a/e502/libusb-1.0/libusb-1.0/descriptor.c b/e502/libusb-1.0/libusb-1.0/descriptor.c
new file mode 100644
index 0000000..93d34ce
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/descriptor.c
@@ -0,0 +1,1199 @@
+/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
+/*
+ * USB descriptor handling functions for libusb
+ * Copyright © 2007 Daniel Drake
+ * Copyright © 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
+ */
+
+#include
+#include
+#include
+#include
+
+#include "libusbi.h"
+
+#define DESC_HEADER_LENGTH 2
+#define DEVICE_DESC_LENGTH 18
+#define CONFIG_DESC_LENGTH 9
+#define INTERFACE_DESC_LENGTH 9
+#define ENDPOINT_DESC_LENGTH 7
+#define ENDPOINT_AUDIO_DESC_LENGTH 9
+
+/** @defgroup desc USB descriptors
+ * This page details how to examine the various standard USB descriptors
+ * for detected devices
+ */
+
+/* set host_endian if the w values are already in host endian format,
+ * as opposed to bus endian. */
+int usbi_parse_descriptor(const unsigned char *source, const char *descriptor,
+ void *dest, int host_endian)
+{
+ const unsigned char *sp = source;
+ unsigned char *dp = dest;
+ uint16_t w;
+ const char *cp;
+ uint32_t d;
+
+ for (cp = descriptor; *cp; cp++) {
+ switch (*cp) {
+ case 'b': /* 8-bit byte */
+ *dp++ = *sp++;
+ break;
+ case 'w': /* 16-bit word, convert from little endian to CPU */
+ dp += ((uintptr_t)dp & 1); /* Align to word boundary */
+
+ if (host_endian) {
+ memcpy(dp, sp, 2);
+ } else {
+ w = (sp[1] << 8) | sp[0];
+ *((uint16_t *)dp) = w;
+ }
+ sp += 2;
+ dp += 2;
+ break;
+ case 'd': /* 32-bit word, convert from little endian to CPU */
+ dp += ((uintptr_t)dp & 1); /* Align to word boundary */
+
+ if (host_endian) {
+ memcpy(dp, sp, 4);
+ } else {
+ d = (sp[3] << 24) | (sp[2] << 16) |
+ (sp[1] << 8) | sp[0];
+ *((uint32_t *)dp) = d;
+ }
+ sp += 4;
+ dp += 4;
+ break;
+ case 'u': /* 16 byte UUID */
+ memcpy(dp, sp, 16);
+ sp += 16;
+ dp += 16;
+ break;
+ }
+ }
+
+ return (int) (sp - source);
+}
+
+static void clear_endpoint(struct libusb_endpoint_descriptor *endpoint)
+{
+ if (endpoint->extra)
+ free((unsigned char *) endpoint->extra);
+}
+
+static int parse_endpoint(struct libusb_context *ctx,
+ struct libusb_endpoint_descriptor *endpoint, unsigned char *buffer,
+ int size, int host_endian)
+{
+ struct usb_descriptor_header header;
+ unsigned char *extra;
+ unsigned char *begin;
+ int parsed = 0;
+ int len;
+
+ if (size < DESC_HEADER_LENGTH) {
+ usbi_err(ctx, "short endpoint descriptor read %d/%d",
+ size, DESC_HEADER_LENGTH);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
+ if (header.bDescriptorType != LIBUSB_DT_ENDPOINT) {
+ usbi_err(ctx, "unexpected descriptor %x (expected %x)",
+ header.bDescriptorType, LIBUSB_DT_ENDPOINT);
+ return parsed;
+ }
+ if (header.bLength > size) {
+ usbi_warn(ctx, "short endpoint descriptor read %d/%d",
+ size, header.bLength);
+ return parsed;
+ }
+ if (header.bLength >= ENDPOINT_AUDIO_DESC_LENGTH)
+ usbi_parse_descriptor(buffer, "bbbbwbbb", endpoint, host_endian);
+ else if (header.bLength >= ENDPOINT_DESC_LENGTH)
+ usbi_parse_descriptor(buffer, "bbbbwb", endpoint, host_endian);
+ else {
+ usbi_err(ctx, "invalid endpoint bLength (%d)", header.bLength);
+ return LIBUSB_ERROR_IO;
+ }
+
+ buffer += header.bLength;
+ size -= header.bLength;
+ parsed += header.bLength;
+
+ /* Skip over the rest of the Class Specific or Vendor Specific */
+ /* descriptors */
+ begin = buffer;
+ while (size >= DESC_HEADER_LENGTH) {
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
+ if (header.bLength < DESC_HEADER_LENGTH) {
+ usbi_err(ctx, "invalid extra ep desc len (%d)",
+ header.bLength);
+ return LIBUSB_ERROR_IO;
+ } else if (header.bLength > size) {
+ usbi_warn(ctx, "short extra ep desc read %d/%d",
+ size, header.bLength);
+ return parsed;
+ }
+
+ /* If we find another "proper" descriptor then we're done */
+ if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
+ (header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
+ (header.bDescriptorType == LIBUSB_DT_CONFIG) ||
+ (header.bDescriptorType == LIBUSB_DT_DEVICE))
+ break;
+
+ usbi_dbg("skipping descriptor %x", header.bDescriptorType);
+ buffer += header.bLength;
+ size -= header.bLength;
+ parsed += header.bLength;
+ }
+
+ /* Copy any unknown descriptors into a storage area for drivers */
+ /* to later parse */
+ len = (int)(buffer - begin);
+ if (!len) {
+ endpoint->extra = NULL;
+ endpoint->extra_length = 0;
+ return parsed;
+ }
+
+ extra = malloc(len);
+ endpoint->extra = extra;
+ if (!extra) {
+ endpoint->extra_length = 0;
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ memcpy(extra, begin, len);
+ endpoint->extra_length = len;
+
+ return parsed;
+}
+
+static void clear_interface(struct libusb_interface *usb_interface)
+{
+ int i;
+ int j;
+
+ if (usb_interface->altsetting) {
+ for (i = 0; i < usb_interface->num_altsetting; i++) {
+ struct libusb_interface_descriptor *ifp =
+ (struct libusb_interface_descriptor *)
+ usb_interface->altsetting + i;
+ if (ifp->extra)
+ free((void *) ifp->extra);
+ if (ifp->endpoint) {
+ for (j = 0; j < ifp->bNumEndpoints; j++)
+ clear_endpoint((struct libusb_endpoint_descriptor *)
+ ifp->endpoint + j);
+ free((void *) ifp->endpoint);
+ }
+ }
+ free((void *) usb_interface->altsetting);
+ usb_interface->altsetting = NULL;
+ }
+
+}
+
+static int parse_interface(libusb_context *ctx,
+ struct libusb_interface *usb_interface, unsigned char *buffer, int size,
+ int host_endian)
+{
+ int i;
+ int len;
+ int r;
+ int parsed = 0;
+ int interface_number = -1;
+ size_t tmp;
+ struct usb_descriptor_header header;
+ struct libusb_interface_descriptor *ifp;
+ unsigned char *begin;
+
+ usb_interface->num_altsetting = 0;
+
+ while (size >= INTERFACE_DESC_LENGTH) {
+ struct libusb_interface_descriptor *altsetting =
+ (struct libusb_interface_descriptor *) usb_interface->altsetting;
+ altsetting = usbi_reallocf(altsetting,
+ sizeof(struct libusb_interface_descriptor) *
+ (usb_interface->num_altsetting + 1));
+ if (!altsetting) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err;
+ }
+ usb_interface->altsetting = altsetting;
+
+ ifp = altsetting + usb_interface->num_altsetting;
+ usbi_parse_descriptor(buffer, "bbbbbbbbb", ifp, 0);
+ if (ifp->bDescriptorType != LIBUSB_DT_INTERFACE) {
+ usbi_err(ctx, "unexpected descriptor %x (expected %x)",
+ ifp->bDescriptorType, LIBUSB_DT_INTERFACE);
+ return parsed;
+ }
+ if (ifp->bLength < INTERFACE_DESC_LENGTH) {
+ usbi_err(ctx, "invalid interface bLength (%d)",
+ ifp->bLength);
+ r = LIBUSB_ERROR_IO;
+ goto err;
+ }
+ if (ifp->bLength > size) {
+ usbi_warn(ctx, "short intf descriptor read %d/%d",
+ size, ifp->bLength);
+ return parsed;
+ }
+ if (ifp->bNumEndpoints > USB_MAXENDPOINTS) {
+ usbi_err(ctx, "too many endpoints (%d)", ifp->bNumEndpoints);
+ r = LIBUSB_ERROR_IO;
+ goto err;
+ }
+
+ usb_interface->num_altsetting++;
+ ifp->extra = NULL;
+ ifp->extra_length = 0;
+ ifp->endpoint = NULL;
+
+ if (interface_number == -1)
+ interface_number = ifp->bInterfaceNumber;
+
+ /* Skip over the interface */
+ buffer += ifp->bLength;
+ parsed += ifp->bLength;
+ size -= ifp->bLength;
+
+ begin = buffer;
+
+ /* Skip over any interface, class or vendor descriptors */
+ while (size >= DESC_HEADER_LENGTH) {
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
+ if (header.bLength < DESC_HEADER_LENGTH) {
+ usbi_err(ctx,
+ "invalid extra intf desc len (%d)",
+ header.bLength);
+ r = LIBUSB_ERROR_IO;
+ goto err;
+ } else if (header.bLength > size) {
+ usbi_warn(ctx,
+ "short extra intf desc read %d/%d",
+ size, header.bLength);
+ return parsed;
+ }
+
+ /* If we find another "proper" descriptor then we're done */
+ if ((header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
+ (header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
+ (header.bDescriptorType == LIBUSB_DT_CONFIG) ||
+ (header.bDescriptorType == LIBUSB_DT_DEVICE))
+ break;
+
+ buffer += header.bLength;
+ parsed += header.bLength;
+ size -= header.bLength;
+ }
+
+ /* Copy any unknown descriptors into a storage area for */
+ /* drivers to later parse */
+ len = (int)(buffer - begin);
+ if (len) {
+ ifp->extra = malloc(len);
+ if (!ifp->extra) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err;
+ }
+ memcpy((unsigned char *) ifp->extra, begin, len);
+ ifp->extra_length = len;
+ }
+
+ if (ifp->bNumEndpoints > 0) {
+ struct libusb_endpoint_descriptor *endpoint;
+ tmp = ifp->bNumEndpoints * sizeof(struct libusb_endpoint_descriptor);
+ endpoint = malloc(tmp);
+ ifp->endpoint = endpoint;
+ if (!endpoint) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err;
+ }
+
+ memset(endpoint, 0, tmp);
+ for (i = 0; i < ifp->bNumEndpoints; i++) {
+ r = parse_endpoint(ctx, endpoint + i, buffer, size,
+ host_endian);
+ if (r < 0)
+ goto err;
+ if (r == 0) {
+ ifp->bNumEndpoints = (uint8_t)i;
+ break;;
+ }
+
+ buffer += r;
+ parsed += r;
+ size -= r;
+ }
+ }
+
+ /* We check to see if it's an alternate to this one */
+ ifp = (struct libusb_interface_descriptor *) buffer;
+ if (size < LIBUSB_DT_INTERFACE_SIZE ||
+ ifp->bDescriptorType != LIBUSB_DT_INTERFACE ||
+ ifp->bInterfaceNumber != interface_number)
+ return parsed;
+ }
+
+ return parsed;
+err:
+ clear_interface(usb_interface);
+ return r;
+}
+
+static void clear_configuration(struct libusb_config_descriptor *config)
+{
+ if (config->interface) {
+ int i;
+ for (i = 0; i < config->bNumInterfaces; i++)
+ clear_interface((struct libusb_interface *)
+ config->interface + i);
+ free((void *) config->interface);
+ }
+ if (config->extra)
+ free((void *) config->extra);
+}
+
+static int parse_configuration(struct libusb_context *ctx,
+ struct libusb_config_descriptor *config, unsigned char *buffer,
+ int size, int host_endian)
+{
+ int i;
+ int r;
+ size_t tmp;
+ struct usb_descriptor_header header;
+ struct libusb_interface *usb_interface;
+
+ if (size < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(ctx, "short config descriptor read %d/%d",
+ size, LIBUSB_DT_CONFIG_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usbi_parse_descriptor(buffer, "bbwbbbbb", config, host_endian);
+ if (config->bDescriptorType != LIBUSB_DT_CONFIG) {
+ usbi_err(ctx, "unexpected descriptor %x (expected %x)",
+ config->bDescriptorType, LIBUSB_DT_CONFIG);
+ return LIBUSB_ERROR_IO;
+ }
+ if (config->bLength < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(ctx, "invalid config bLength (%d)", config->bLength);
+ return LIBUSB_ERROR_IO;
+ }
+ if (config->bLength > size) {
+ usbi_err(ctx, "short config descriptor read %d/%d",
+ size, config->bLength);
+ return LIBUSB_ERROR_IO;
+ }
+ if (config->bNumInterfaces > USB_MAXINTERFACES) {
+ usbi_err(ctx, "too many interfaces (%d)", config->bNumInterfaces);
+ return LIBUSB_ERROR_IO;
+ }
+
+ tmp = config->bNumInterfaces * sizeof(struct libusb_interface);
+ usb_interface = malloc(tmp);
+ config->interface = usb_interface;
+ if (!config->interface)
+ return LIBUSB_ERROR_NO_MEM;
+
+ memset(usb_interface, 0, tmp);
+ buffer += config->bLength;
+ size -= config->bLength;
+
+ config->extra = NULL;
+ config->extra_length = 0;
+
+ for (i = 0; i < config->bNumInterfaces; i++) {
+ int len;
+ unsigned char *begin;
+
+ /* Skip over the rest of the Class Specific or Vendor */
+ /* Specific descriptors */
+ begin = buffer;
+ while (size >= DESC_HEADER_LENGTH) {
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
+
+ if (header.bLength < DESC_HEADER_LENGTH) {
+ usbi_err(ctx,
+ "invalid extra config desc len (%d)",
+ header.bLength);
+ r = LIBUSB_ERROR_IO;
+ goto err;
+ } else if (header.bLength > size) {
+ usbi_warn(ctx,
+ "short extra config desc read %d/%d",
+ size, header.bLength);
+ config->bNumInterfaces = (uint8_t)i;
+ return size;
+ }
+
+ /* If we find another "proper" descriptor then we're done */
+ if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
+ (header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
+ (header.bDescriptorType == LIBUSB_DT_CONFIG) ||
+ (header.bDescriptorType == LIBUSB_DT_DEVICE))
+ break;
+
+ usbi_dbg("skipping descriptor 0x%x\n", header.bDescriptorType);
+ buffer += header.bLength;
+ size -= header.bLength;
+ }
+
+ /* Copy any unknown descriptors into a storage area for */
+ /* drivers to later parse */
+ len = (int)(buffer - begin);
+ if (len) {
+ /* FIXME: We should realloc and append here */
+ if (!config->extra_length) {
+ config->extra = malloc(len);
+ if (!config->extra) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err;
+ }
+
+ memcpy((unsigned char *) config->extra, begin, len);
+ config->extra_length = len;
+ }
+ }
+
+ r = parse_interface(ctx, usb_interface + i, buffer, size, host_endian);
+ if (r < 0)
+ goto err;
+ if (r == 0) {
+ config->bNumInterfaces = (uint8_t)i;
+ break;
+ }
+
+ buffer += r;
+ size -= r;
+ }
+
+ return size;
+
+err:
+ clear_configuration(config);
+ return r;
+}
+
+static int raw_desc_to_config(struct libusb_context *ctx,
+ unsigned char *buf, int size, int host_endian,
+ struct libusb_config_descriptor **config)
+{
+ struct libusb_config_descriptor *_config = malloc(sizeof(*_config));
+ int r;
+
+ if (!_config)
+ return LIBUSB_ERROR_NO_MEM;
+
+ r = parse_configuration(ctx, _config, buf, size, host_endian);
+ if (r < 0) {
+ usbi_err(ctx, "parse_configuration failed with error %d", r);
+ free(_config);
+ return r;
+ } else if (r > 0) {
+ usbi_warn(ctx, "still %d bytes of descriptor data left", r);
+ }
+
+ *config = _config;
+ return LIBUSB_SUCCESS;
+}
+
+int usbi_device_cache_descriptor(libusb_device *dev)
+{
+ int r, host_endian = 0;
+
+ r = usbi_backend->get_device_descriptor(dev, (unsigned char *) &dev->device_descriptor,
+ &host_endian);
+ if (r < 0)
+ return r;
+
+ if (!host_endian) {
+ dev->device_descriptor.bcdUSB = libusb_le16_to_cpu(dev->device_descriptor.bcdUSB);
+ dev->device_descriptor.idVendor = libusb_le16_to_cpu(dev->device_descriptor.idVendor);
+ dev->device_descriptor.idProduct = libusb_le16_to_cpu(dev->device_descriptor.idProduct);
+ dev->device_descriptor.bcdDevice = libusb_le16_to_cpu(dev->device_descriptor.bcdDevice);
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup desc
+ * Get the USB device descriptor for a given device.
+ *
+ * This is a non-blocking function; the device descriptor is cached in memory.
+ *
+ * Note since libusb-1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, this
+ * function always succeeds.
+ *
+ * \param dev the device
+ * \param desc output location for the descriptor data
+ * \returns 0 on success or a LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev,
+ struct libusb_device_descriptor *desc)
+{
+ usbi_dbg("");
+ memcpy((unsigned char *) desc, (unsigned char *) &dev->device_descriptor,
+ sizeof (dev->device_descriptor));
+ return 0;
+}
+
+/** \ingroup desc
+ * Get the USB configuration descriptor for the currently active configuration.
+ * This is a non-blocking function which does not involve any requests being
+ * sent to the device.
+ *
+ * \param dev a device
+ * \param config output location for the USB configuration descriptor. Only
+ * valid if 0 was returned. Must be freed with libusb_free_config_descriptor()
+ * after use.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state
+ * \returns another LIBUSB_ERROR code on error
+ * \see libusb_get_config_descriptor
+ */
+int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev,
+ struct libusb_config_descriptor **config)
+{
+ struct libusb_config_descriptor _config;
+ unsigned char tmp[LIBUSB_DT_CONFIG_SIZE];
+ unsigned char *buf = NULL;
+ int host_endian = 0;
+ int r;
+
+ r = usbi_backend->get_active_config_descriptor(dev, tmp,
+ LIBUSB_DT_CONFIG_SIZE, &host_endian);
+ if (r < 0)
+ return r;
+ if (r < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(dev->ctx, "short config descriptor read %d/%d",
+ r, LIBUSB_DT_CONFIG_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usbi_parse_descriptor(tmp, "bbw", &_config, host_endian);
+ buf = malloc(_config.wTotalLength);
+ if (!buf)
+ return LIBUSB_ERROR_NO_MEM;
+
+ r = usbi_backend->get_active_config_descriptor(dev, buf,
+ _config.wTotalLength, &host_endian);
+ if (r >= 0)
+ r = raw_desc_to_config(dev->ctx, buf, r, host_endian, config);
+
+ free(buf);
+ return r;
+}
+
+/** \ingroup desc
+ * Get a USB configuration descriptor based on its index.
+ * This is a non-blocking function which does not involve any requests being
+ * sent to the device.
+ *
+ * \param dev a device
+ * \param config_index the index of the configuration you wish to retrieve
+ * \param config output location for the USB configuration descriptor. Only
+ * valid if 0 was returned. Must be freed with libusb_free_config_descriptor()
+ * after use.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
+ * \returns another LIBUSB_ERROR code on error
+ * \see libusb_get_active_config_descriptor()
+ * \see libusb_get_config_descriptor_by_value()
+ */
+int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev,
+ uint8_t config_index, struct libusb_config_descriptor **config)
+{
+ struct libusb_config_descriptor _config;
+ unsigned char tmp[LIBUSB_DT_CONFIG_SIZE];
+ unsigned char *buf = NULL;
+ int host_endian = 0;
+ int r;
+
+ usbi_dbg("index %d", config_index);
+ if (config_index >= dev->num_configurations)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ r = usbi_backend->get_config_descriptor(dev, config_index, tmp,
+ LIBUSB_DT_CONFIG_SIZE, &host_endian);
+ if (r < 0)
+ return r;
+ if (r < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(dev->ctx, "short config descriptor read %d/%d",
+ r, LIBUSB_DT_CONFIG_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usbi_parse_descriptor(tmp, "bbw", &_config, host_endian);
+ buf = malloc(_config.wTotalLength);
+ if (!buf)
+ return LIBUSB_ERROR_NO_MEM;
+
+ r = usbi_backend->get_config_descriptor(dev, config_index, buf,
+ _config.wTotalLength, &host_endian);
+ if (r >= 0)
+ r = raw_desc_to_config(dev->ctx, buf, r, host_endian, config);
+
+ free(buf);
+ return r;
+}
+
+/* iterate through all configurations, returning the index of the configuration
+ * matching a specific bConfigurationValue in the idx output parameter, or -1
+ * if the config was not found.
+ * returns 0 on success or a LIBUSB_ERROR code
+ */
+int usbi_get_config_index_by_value(struct libusb_device *dev,
+ uint8_t bConfigurationValue, int *idx)
+{
+ uint8_t i;
+
+ usbi_dbg("value %d", bConfigurationValue);
+ for (i = 0; i < dev->num_configurations; i++) {
+ unsigned char tmp[6];
+ int host_endian;
+ int r = usbi_backend->get_config_descriptor(dev, i, tmp, sizeof(tmp),
+ &host_endian);
+ if (r < 0) {
+ *idx = -1;
+ return r;
+ }
+ if (tmp[5] == bConfigurationValue) {
+ *idx = i;
+ return 0;
+ }
+ }
+
+ *idx = -1;
+ return 0;
+}
+
+/** \ingroup desc
+ * Get a USB configuration descriptor with a specific bConfigurationValue.
+ * This is a non-blocking function which does not involve any requests being
+ * sent to the device.
+ *
+ * \param dev a device
+ * \param bConfigurationValue the bConfigurationValue of the configuration you
+ * wish to retrieve
+ * \param config output location for the USB configuration descriptor. Only
+ * valid if 0 was returned. Must be freed with libusb_free_config_descriptor()
+ * after use.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
+ * \returns another LIBUSB_ERROR code on error
+ * \see libusb_get_active_config_descriptor()
+ * \see libusb_get_config_descriptor()
+ */
+int API_EXPORTED libusb_get_config_descriptor_by_value(libusb_device *dev,
+ uint8_t bConfigurationValue, struct libusb_config_descriptor **config)
+{
+ int r, idx, host_endian;
+ unsigned char *buf = NULL;
+
+ if (usbi_backend->get_config_descriptor_by_value) {
+ r = usbi_backend->get_config_descriptor_by_value(dev,
+ bConfigurationValue, &buf, &host_endian);
+ if (r < 0)
+ return r;
+ return raw_desc_to_config(dev->ctx, buf, r, host_endian, config);
+ }
+
+ r = usbi_get_config_index_by_value(dev, bConfigurationValue, &idx);
+ if (r < 0)
+ return r;
+ else if (idx == -1)
+ return LIBUSB_ERROR_NOT_FOUND;
+ else
+ return libusb_get_config_descriptor(dev, (uint8_t) idx, config);
+}
+
+/** \ingroup desc
+ * Free a configuration descriptor obtained from
+ * libusb_get_active_config_descriptor() or libusb_get_config_descriptor().
+ * It is safe to call this function with a NULL config parameter, in which
+ * case the function simply returns.
+ *
+ * \param config the configuration descriptor to free
+ */
+void API_EXPORTED libusb_free_config_descriptor(
+ struct libusb_config_descriptor *config)
+{
+ if (!config)
+ return;
+
+ clear_configuration(config);
+ free(config);
+}
+
+/** \ingroup desc
+ * Get an endpoints superspeed endpoint companion descriptor (if any)
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param endpoint endpoint descriptor from which to get the superspeed
+ * endpoint companion descriptor
+ * \param ep_comp output location for the superspeed endpoint companion
+ * descriptor. Only valid if 0 was returned. Must be freed with
+ * libusb_free_ss_endpoint_companion_descriptor() after use.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
+ * \returns another LIBUSB_ERROR code on error
+ */
+int API_EXPORTED libusb_get_ss_endpoint_companion_descriptor(
+ struct libusb_context *ctx,
+ const struct libusb_endpoint_descriptor *endpoint,
+ struct libusb_ss_endpoint_companion_descriptor **ep_comp)
+{
+ struct usb_descriptor_header header;
+ int size = endpoint->extra_length;
+ const unsigned char *buffer = endpoint->extra;
+
+ *ep_comp = NULL;
+
+ while (size >= DESC_HEADER_LENGTH) {
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
+ if (header.bLength < 2 || header.bLength > size) {
+ usbi_err(ctx, "invalid descriptor length %d",
+ header.bLength);
+ return LIBUSB_ERROR_IO;
+ }
+ if (header.bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) {
+ buffer += header.bLength;
+ size -= header.bLength;
+ continue;
+ }
+ if (header.bLength < LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) {
+ usbi_err(ctx, "invalid ss-ep-comp-desc length %d",
+ header.bLength);
+ return LIBUSB_ERROR_IO;
+ }
+ *ep_comp = malloc(sizeof(**ep_comp));
+ if (*ep_comp == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+ usbi_parse_descriptor(buffer, "bbbbw", *ep_comp, 0);
+ return LIBUSB_SUCCESS;
+ }
+ return LIBUSB_ERROR_NOT_FOUND;
+}
+
+/** \ingroup desc
+ * Free a superspeed endpoint companion descriptor obtained from
+ * libusb_get_ss_endpoint_companion_descriptor().
+ * It is safe to call this function with a NULL ep_comp parameter, in which
+ * case the function simply returns.
+ *
+ * \param ep_comp the superspeed endpoint companion descriptor to free
+ */
+void API_EXPORTED libusb_free_ss_endpoint_companion_descriptor(
+ struct libusb_ss_endpoint_companion_descriptor *ep_comp)
+{
+ free(ep_comp);
+}
+
+static int parse_bos(struct libusb_context *ctx,
+ struct libusb_bos_descriptor **bos,
+ unsigned char *buffer, int size, int host_endian)
+{
+ struct libusb_bos_descriptor bos_header, *_bos;
+ struct libusb_bos_dev_capability_descriptor dev_cap;
+ int i;
+
+ if (size < LIBUSB_DT_BOS_SIZE) {
+ usbi_err(ctx, "short bos descriptor read %d/%d",
+ size, LIBUSB_DT_BOS_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usbi_parse_descriptor(buffer, "bbwb", &bos_header, host_endian);
+ if (bos_header.bDescriptorType != LIBUSB_DT_BOS) {
+ usbi_err(ctx, "unexpected descriptor %x (expected %x)",
+ bos_header.bDescriptorType, LIBUSB_DT_BOS);
+ return LIBUSB_ERROR_IO;
+ }
+ if (bos_header.bLength < LIBUSB_DT_BOS_SIZE) {
+ usbi_err(ctx, "invalid bos bLength (%d)", bos_header.bLength);
+ return LIBUSB_ERROR_IO;
+ }
+ if (bos_header.bLength > size) {
+ usbi_err(ctx, "short bos descriptor read %d/%d",
+ size, bos_header.bLength);
+ return LIBUSB_ERROR_IO;
+ }
+
+ _bos = calloc (1,
+ sizeof(*_bos) + bos_header.bNumDeviceCaps * sizeof(void *));
+ if (!_bos)
+ return LIBUSB_ERROR_NO_MEM;
+
+ usbi_parse_descriptor(buffer, "bbwb", _bos, host_endian);
+ buffer += bos_header.bLength;
+ size -= bos_header.bLength;
+
+ /* Get the device capability descriptors */
+ for (i = 0; i < bos_header.bNumDeviceCaps; i++) {
+ if (size < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) {
+ usbi_warn(ctx, "short dev-cap descriptor read %d/%d",
+ size, LIBUSB_DT_DEVICE_CAPABILITY_SIZE);
+ break;
+ }
+ usbi_parse_descriptor(buffer, "bbb", &dev_cap, host_endian);
+ if (dev_cap.bDescriptorType != LIBUSB_DT_DEVICE_CAPABILITY) {
+ usbi_warn(ctx, "unexpected descriptor %x (expected %x)",
+ dev_cap.bDescriptorType, LIBUSB_DT_DEVICE_CAPABILITY);
+ break;
+ }
+ if (dev_cap.bLength < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) {
+ usbi_err(ctx, "invalid dev-cap bLength (%d)",
+ dev_cap.bLength);
+ libusb_free_bos_descriptor(_bos);
+ return LIBUSB_ERROR_IO;
+ }
+ if (dev_cap.bLength > size) {
+ usbi_warn(ctx, "short dev-cap descriptor read %d/%d",
+ size, dev_cap.bLength);
+ break;
+ }
+
+ _bos->dev_capability[i] = malloc(dev_cap.bLength);
+ if (!_bos->dev_capability[i]) {
+ libusb_free_bos_descriptor(_bos);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+ memcpy(_bos->dev_capability[i], buffer, dev_cap.bLength);
+ buffer += dev_cap.bLength;
+ size -= dev_cap.bLength;
+ }
+ _bos->bNumDeviceCaps = (uint8_t)i;
+ *bos = _bos;
+
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup desc
+ * Get a Binary Object Store (BOS) descriptor
+ * This is a BLOCKING function, which will send requests to the device.
+ *
+ * \param handle the handle of an open libusb device
+ * \param bos output location for the BOS descriptor. Only valid if 0 was returned.
+ * Must be freed with \ref libusb_free_bos_descriptor() after use.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the device doesn't have a BOS descriptor
+ * \returns another LIBUSB_ERROR code on error
+ */
+int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *handle,
+ struct libusb_bos_descriptor **bos)
+{
+ struct libusb_bos_descriptor _bos;
+ uint8_t bos_header[LIBUSB_DT_BOS_SIZE] = {0};
+ unsigned char *bos_data = NULL;
+ const int host_endian = 0;
+ int r;
+
+ /* Read the BOS. This generates 2 requests on the bus,
+ * one for the header, and one for the full BOS */
+ r = libusb_get_descriptor(handle, LIBUSB_DT_BOS, 0, bos_header,
+ LIBUSB_DT_BOS_SIZE);
+ if (r < 0) {
+ if (r != LIBUSB_ERROR_PIPE)
+ usbi_err(handle->dev->ctx, "failed to read BOS (%d)", r);
+ return r;
+ }
+ if (r < LIBUSB_DT_BOS_SIZE) {
+ usbi_err(handle->dev->ctx, "short BOS read %d/%d",
+ r, LIBUSB_DT_BOS_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usbi_parse_descriptor(bos_header, "bbwb", &_bos, host_endian);
+ usbi_dbg("found BOS descriptor: size %d bytes, %d capabilities",
+ _bos.wTotalLength, _bos.bNumDeviceCaps);
+ bos_data = calloc(_bos.wTotalLength, 1);
+ if (bos_data == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+
+ r = libusb_get_descriptor(handle, LIBUSB_DT_BOS, 0, bos_data,
+ _bos.wTotalLength);
+ if (r >= 0)
+ r = parse_bos(handle->dev->ctx, bos, bos_data, r, host_endian);
+ else
+ usbi_err(handle->dev->ctx, "failed to read BOS (%d)", r);
+
+ free(bos_data);
+ return r;
+}
+
+/** \ingroup desc
+ * Free a BOS descriptor obtained from libusb_get_bos_descriptor().
+ * It is safe to call this function with a NULL bos parameter, in which
+ * case the function simply returns.
+ *
+ * \param bos the BOS descriptor to free
+ */
+void API_EXPORTED libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos)
+{
+ int i;
+
+ if (!bos)
+ return;
+
+ for (i = 0; i < bos->bNumDeviceCaps; i++)
+ free(bos->dev_capability[i]);
+ free(bos);
+}
+
+/** \ingroup desc
+ * Get an USB 2.0 Extension descriptor
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param dev_cap Device Capability descriptor with a bDevCapabilityType of
+ * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION
+ * LIBUSB_BT_USB_2_0_EXTENSION
+ * \param usb_2_0_extension output location for the USB 2.0 Extension
+ * descriptor. Only valid if 0 was returned. Must be freed with
+ * libusb_free_usb_2_0_extension_descriptor() after use.
+ * \returns 0 on success
+ * \returns a LIBUSB_ERROR code on error
+ */
+int API_EXPORTED libusb_get_usb_2_0_extension_descriptor(
+ struct libusb_context *ctx,
+ struct libusb_bos_dev_capability_descriptor *dev_cap,
+ struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension)
+{
+ struct libusb_usb_2_0_extension_descriptor *_usb_2_0_extension;
+ const int host_endian = 0;
+
+ if (dev_cap->bDevCapabilityType != LIBUSB_BT_USB_2_0_EXTENSION) {
+ usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)",
+ dev_cap->bDevCapabilityType,
+ LIBUSB_BT_USB_2_0_EXTENSION);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+ if (dev_cap->bLength < LIBUSB_BT_USB_2_0_EXTENSION_SIZE) {
+ usbi_err(ctx, "short dev-cap descriptor read %d/%d",
+ dev_cap->bLength, LIBUSB_BT_USB_2_0_EXTENSION_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ _usb_2_0_extension = malloc(sizeof(*_usb_2_0_extension));
+ if (!_usb_2_0_extension)
+ return LIBUSB_ERROR_NO_MEM;
+
+ usbi_parse_descriptor((unsigned char *)dev_cap, "bbbd",
+ _usb_2_0_extension, host_endian);
+
+ *usb_2_0_extension = _usb_2_0_extension;
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup desc
+ * Free a USB 2.0 Extension descriptor obtained from
+ * libusb_get_usb_2_0_extension_descriptor().
+ * It is safe to call this function with a NULL usb_2_0_extension parameter,
+ * in which case the function simply returns.
+ *
+ * \param usb_2_0_extension the USB 2.0 Extension descriptor to free
+ */
+void API_EXPORTED libusb_free_usb_2_0_extension_descriptor(
+ struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension)
+{
+ free(usb_2_0_extension);
+}
+
+/** \ingroup desc
+ * Get a SuperSpeed USB Device Capability descriptor
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param dev_cap Device Capability descriptor with a bDevCapabilityType of
+ * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY
+ * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY
+ * \param ss_usb_device_cap output location for the SuperSpeed USB Device
+ * Capability descriptor. Only valid if 0 was returned. Must be freed with
+ * libusb_free_ss_usb_device_capability_descriptor() after use.
+ * \returns 0 on success
+ * \returns a LIBUSB_ERROR code on error
+ */
+int API_EXPORTED libusb_get_ss_usb_device_capability_descriptor(
+ struct libusb_context *ctx,
+ struct libusb_bos_dev_capability_descriptor *dev_cap,
+ struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap)
+{
+ struct libusb_ss_usb_device_capability_descriptor *_ss_usb_device_cap;
+ const int host_endian = 0;
+
+ if (dev_cap->bDevCapabilityType != LIBUSB_BT_SS_USB_DEVICE_CAPABILITY) {
+ usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)",
+ dev_cap->bDevCapabilityType,
+ LIBUSB_BT_SS_USB_DEVICE_CAPABILITY);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+ if (dev_cap->bLength < LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) {
+ usbi_err(ctx, "short dev-cap descriptor read %d/%d",
+ dev_cap->bLength, LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ _ss_usb_device_cap = malloc(sizeof(*_ss_usb_device_cap));
+ if (!_ss_usb_device_cap)
+ return LIBUSB_ERROR_NO_MEM;
+
+ usbi_parse_descriptor((unsigned char *)dev_cap, "bbbbwbbw",
+ _ss_usb_device_cap, host_endian);
+
+ *ss_usb_device_cap = _ss_usb_device_cap;
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup desc
+ * Free a SuperSpeed USB Device Capability descriptor obtained from
+ * libusb_get_ss_usb_device_capability_descriptor().
+ * It is safe to call this function with a NULL ss_usb_device_cap
+ * parameter, in which case the function simply returns.
+ *
+ * \param ss_usb_device_cap the USB 2.0 Extension descriptor to free
+ */
+void API_EXPORTED libusb_free_ss_usb_device_capability_descriptor(
+ struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap)
+{
+ free(ss_usb_device_cap);
+}
+
+/** \ingroup desc
+ * Get a Container ID descriptor
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param dev_cap Device Capability descriptor with a bDevCapabilityType of
+ * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID
+ * LIBUSB_BT_CONTAINER_ID
+ * \param container_id output location for the Container ID descriptor.
+ * Only valid if 0 was returned. Must be freed with
+ * libusb_free_container_id_descriptor() after use.
+ * \returns 0 on success
+ * \returns a LIBUSB_ERROR code on error
+ */
+int API_EXPORTED libusb_get_container_id_descriptor(struct libusb_context *ctx,
+ struct libusb_bos_dev_capability_descriptor *dev_cap,
+ struct libusb_container_id_descriptor **container_id)
+{
+ struct libusb_container_id_descriptor *_container_id;
+ const int host_endian = 0;
+
+ if (dev_cap->bDevCapabilityType != LIBUSB_BT_CONTAINER_ID) {
+ usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)",
+ dev_cap->bDevCapabilityType,
+ LIBUSB_BT_CONTAINER_ID);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+ if (dev_cap->bLength < LIBUSB_BT_CONTAINER_ID_SIZE) {
+ usbi_err(ctx, "short dev-cap descriptor read %d/%d",
+ dev_cap->bLength, LIBUSB_BT_CONTAINER_ID_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ _container_id = malloc(sizeof(*_container_id));
+ if (!_container_id)
+ return LIBUSB_ERROR_NO_MEM;
+
+ usbi_parse_descriptor((unsigned char *)dev_cap, "bbbbu",
+ _container_id, host_endian);
+
+ *container_id = _container_id;
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup desc
+ * Free a Container ID descriptor obtained from
+ * libusb_get_container_id_descriptor().
+ * It is safe to call this function with a NULL container_id parameter,
+ * in which case the function simply returns.
+ *
+ * \param container_id the USB 2.0 Extension descriptor to free
+ */
+void API_EXPORTED libusb_free_container_id_descriptor(
+ struct libusb_container_id_descriptor *container_id)
+{
+ free(container_id);
+}
+
+/** \ingroup desc
+ * Retrieve a string descriptor in C style ASCII.
+ *
+ * Wrapper around libusb_get_string_descriptor(). Uses the first language
+ * supported by the device.
+ *
+ * \param dev a device handle
+ * \param desc_index the index of the descriptor to retrieve
+ * \param data output buffer for ASCII string descriptor
+ * \param length size of data buffer
+ * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev,
+ uint8_t desc_index, unsigned char *data, int length)
+{
+ unsigned char tbuf[255]; /* Some devices choke on size > 255 */
+ int r, si, di;
+ uint16_t langid;
+
+ /* Asking for the zero'th index is special - it returns a string
+ * descriptor that contains all the language IDs supported by the
+ * device. Typically there aren't many - often only one. Language
+ * IDs are 16 bit numbers, and they start at the third byte in the
+ * descriptor. There's also no point in trying to read descriptor 0
+ * with this function. See USB 2.0 specification section 9.6.7 for
+ * more information.
+ */
+
+ if (desc_index == 0)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ r = libusb_get_string_descriptor(dev, 0, 0, tbuf, sizeof(tbuf));
+ if (r < 0)
+ return r;
+
+ if (r < 4)
+ return LIBUSB_ERROR_IO;
+
+ langid = tbuf[2] | (tbuf[3] << 8);
+
+ r = libusb_get_string_descriptor(dev, desc_index, langid, tbuf,
+ sizeof(tbuf));
+ if (r < 0)
+ return r;
+
+ if (tbuf[1] != LIBUSB_DT_STRING)
+ return LIBUSB_ERROR_IO;
+
+ if (tbuf[0] > r)
+ return LIBUSB_ERROR_IO;
+
+ for (di = 0, si = 2; si < tbuf[0]; si += 2) {
+ if (di >= (length - 1))
+ break;
+
+ if ((tbuf[si] & 0x80) || (tbuf[si + 1])) /* non-ASCII */
+ data[di++] = '?';
+ else
+ data[di++] = tbuf[si];
+ }
+
+ data[di] = 0;
+ return di;
+}
diff --git a/e502/libusb-1.0/libusb-1.0/hotplug.c b/e502/libusb-1.0/libusb-1.0/hotplug.c
new file mode 100644
index 0000000..eba8f98
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/hotplug.c
@@ -0,0 +1,327 @@
+/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
+/*
+ * Hotplug functions for libusb
+ * Copyright © 2012-2013 Nathan Hjelm
+ * Copyright © 2012-2013 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
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+#ifdef HAVE_SYS_TYPES_H
+#include
+#endif
+#include
+
+#include "libusbi.h"
+#include "hotplug.h"
+
+/**
+ * @defgroup hotplug Device hotplug event notification
+ * This page details how to use the libusb hotplug interface, where available.
+ *
+ * Be mindful that not all platforms currently implement hotplug notification and
+ * that you should first call on \ref libusb_has_capability() with parameter
+ * \ref LIBUSB_CAP_HAS_HOTPLUG to confirm that hotplug support is available.
+ *
+ * \page hotplug Device hotplug event notification
+ *
+ * \section intro Introduction
+ *
+ * Version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, has added support
+ * for hotplug events on some platforms (you should test if your platform
+ * supports hotplug notification by calling \ref libusb_has_capability() with
+ * parameter \ref LIBUSB_CAP_HAS_HOTPLUG).
+ *
+ * This interface allows you to request notification for the arrival and departure
+ * of matching USB devices.
+ *
+ * To receive hotplug notification you register a callback by calling
+ * \ref libusb_hotplug_register_callback(). This function will optionally return
+ * a handle that can be passed to \ref libusb_hotplug_deregister_callback().
+ *
+ * A callback function must return an int (0 or 1) indicating whether the callback is
+ * expecting additional events. Returning 0 will rearm the callback and 1 will cause
+ * the callback to be deregistered. Note that when callbacks are called from
+ * libusb_hotplug_register_callback() because of the \ref LIBUSB_HOTPLUG_ENUMERATE
+ * flag, the callback return value is ignored, iow you cannot cause a callback
+ * to be deregistered by returning 1 when it is called from
+ * libusb_hotplug_register_callback().
+ *
+ * Callbacks for a particular context are automatically deregistered by libusb_exit().
+ *
+ * As of 1.0.16 there are two supported hotplug events:
+ * - LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: A device has arrived and is ready to use
+ * - LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: A device has left and is no longer available
+ *
+ * A hotplug event can listen for either or both of these events.
+ *
+ * Note: If you receive notification that a device has left and you have any
+ * a libusb_device_handles for the device it is up to you to call libusb_close()
+ * on each handle to free up any remaining resources associated with the device.
+ * Once a device has left any libusb_device_handle associated with the device
+ * are invalid and will remain so even if the device comes back.
+ *
+ * When handling a LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED event it is considered
+ * safe to call any libusb function that takes a libusb_device. On the other hand,
+ * when handling a LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT event the only safe function
+ * is libusb_get_device_descriptor().
+ *
+ * The following code provides an example of the usage of the hotplug interface:
+\code
+#include
+#include
+#include
+
+static int count = 0;
+
+int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev,
+ libusb_hotplug_event event, void *user_data) {
+ static libusb_device_handle *handle = NULL;
+ struct libusb_device_descriptor desc;
+ int rc;
+
+ (void)libusb_get_device_descriptor(dev, &desc);
+
+ if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) {
+ rc = libusb_open(dev, &handle);
+ if (LIBUSB_SUCCESS != rc) {
+ printf("Could not open USB device\n");
+ }
+ } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) {
+ if (handle) {
+ libusb_close(handle);
+ handle = NULL;
+ }
+ } else {
+ printf("Unhandled event %d\n", event);
+ }
+ count++;
+
+ return 0;
+}
+
+int main (void) {
+ libusb_hotplug_callback_handle handle;
+ int rc;
+
+ libusb_init(NULL);
+
+ rc = libusb_hotplug_register_callback(NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
+ LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, 0x045a, 0x5005,
+ LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback, NULL,
+ &handle);
+ if (LIBUSB_SUCCESS != rc) {
+ printf("Error creating a hotplug callback\n");
+ libusb_exit(NULL);
+ return EXIT_FAILURE;
+ }
+
+ while (count < 2) {
+ libusb_handle_events_completed(NULL, NULL);
+ usleep(10000);
+ }
+
+ libusb_hotplug_deregister_callback(NULL, handle);
+ libusb_exit(NULL);
+
+ return 0;
+}
+\endcode
+ */
+
+static int usbi_hotplug_match_cb (struct libusb_context *ctx,
+ struct libusb_device *dev, libusb_hotplug_event event,
+ struct libusb_hotplug_callback *hotplug_cb)
+{
+ /* Handle lazy deregistration of callback */
+ if (hotplug_cb->needs_free) {
+ /* Free callback */
+ return 1;
+ }
+
+ if (!(hotplug_cb->events & event)) {
+ return 0;
+ }
+
+ if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->vendor_id &&
+ hotplug_cb->vendor_id != dev->device_descriptor.idVendor) {
+ return 0;
+ }
+
+ if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->product_id &&
+ hotplug_cb->product_id != dev->device_descriptor.idProduct) {
+ return 0;
+ }
+
+ if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->dev_class &&
+ hotplug_cb->dev_class != dev->device_descriptor.bDeviceClass) {
+ return 0;
+ }
+
+ return hotplug_cb->cb (ctx, dev, event, hotplug_cb->user_data);
+}
+
+void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
+ libusb_hotplug_event event)
+{
+ struct libusb_hotplug_callback *hotplug_cb, *next;
+ int ret;
+
+ usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+
+ list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
+ usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+ ret = usbi_hotplug_match_cb (ctx, dev, event, hotplug_cb);
+ usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+
+ if (ret) {
+ list_del(&hotplug_cb->list);
+ free(hotplug_cb);
+ }
+ }
+
+ usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+
+ /* the backend is expected to call the callback for each active transfer */
+}
+
+int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
+ libusb_hotplug_event events, libusb_hotplug_flag flags,
+ int vendor_id, int product_id, int dev_class,
+ libusb_hotplug_callback_fn cb_fn, void *user_data,
+ libusb_hotplug_callback_handle *handle)
+{
+ libusb_hotplug_callback *new_callback;
+ static int handle_id = 1;
+
+ /* check for hotplug support */
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+
+ /* check for sane values */
+ if ((LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) ||
+ (LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) ||
+ (LIBUSB_HOTPLUG_MATCH_ANY != dev_class && (~0xff & dev_class)) ||
+ !cb_fn) {
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ USBI_GET_CONTEXT(ctx);
+
+ new_callback = (libusb_hotplug_callback *)calloc(1, sizeof (*new_callback));
+ if (!new_callback) {
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ new_callback->ctx = ctx;
+ new_callback->vendor_id = vendor_id;
+ new_callback->product_id = product_id;
+ new_callback->dev_class = dev_class;
+ new_callback->flags = flags;
+ new_callback->events = events;
+ new_callback->cb = cb_fn;
+ new_callback->user_data = user_data;
+ new_callback->needs_free = 0;
+
+ usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+
+ /* protect the handle by the context hotplug lock. it doesn't matter if the same handle
+ * is used for different contexts only that the handle is unique for this context */
+ new_callback->handle = handle_id++;
+
+ list_add(&new_callback->list, &ctx->hotplug_cbs);
+
+ usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+
+
+ if (flags & LIBUSB_HOTPLUG_ENUMERATE) {
+ int i, len;
+ struct libusb_device **devs;
+
+ len = (int) libusb_get_device_list(ctx, &devs);
+ if (len < 0) {
+ libusb_hotplug_deregister_callback(ctx,
+ new_callback->handle);
+ return len;
+ }
+
+ for (i = 0; i < len; i++) {
+ usbi_hotplug_match_cb(ctx, devs[i],
+ LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
+ new_callback);
+ }
+
+ libusb_free_device_list(devs, 1);
+ }
+
+
+ if (handle) {
+ *handle = new_callback->handle;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+void API_EXPORTED libusb_hotplug_deregister_callback (struct libusb_context *ctx,
+ libusb_hotplug_callback_handle handle)
+{
+ struct libusb_hotplug_callback *hotplug_cb;
+ libusb_hotplug_message message;
+ ssize_t ret;
+
+ /* check for hotplug support */
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ return;
+ }
+
+ USBI_GET_CONTEXT(ctx);
+
+ usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+ list_for_each_entry(hotplug_cb, &ctx->hotplug_cbs, list,
+ struct libusb_hotplug_callback) {
+ if (handle == hotplug_cb->handle) {
+ /* Mark this callback for deregistration */
+ hotplug_cb->needs_free = 1;
+ }
+ }
+ usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+
+ /* wakeup handle_events to do the actual free */
+ memset(&message, 0, sizeof(message));
+ ret = usbi_write(ctx->hotplug_pipe[1], &message, sizeof(message));
+ if (sizeof(message) != ret) {
+ usbi_err(ctx, "error writing hotplug message");
+ }
+}
+
+void usbi_hotplug_deregister_all(struct libusb_context *ctx) {
+ struct libusb_hotplug_callback *hotplug_cb, *next;
+
+ usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+ list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list,
+ struct libusb_hotplug_callback) {
+ list_del(&hotplug_cb->list);
+ free(hotplug_cb);
+ }
+
+ usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+}
diff --git a/e502/libusb-1.0/libusb-1.0/hotplug.h b/e502/libusb-1.0/libusb-1.0/hotplug.h
new file mode 100644
index 0000000..321a0a8
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/hotplug.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
+/*
+ * Hotplug support for libusb
+ * Copyright © 2012-2013 Nathan Hjelm
+ * Copyright © 2012-2013 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
+ */
+
+#if !defined(USBI_HOTPLUG_H)
+#define USBI_HOTPLUG_H
+
+#ifndef LIBUSBI_H
+#include "libusbi.h"
+#endif
+
+/** \ingroup hotplug
+ * The hotplug callback structure. The user populates this structure with
+ * libusb_hotplug_prepare_callback() and then calls libusb_hotplug_register_callback()
+ * to receive notification of hotplug events.
+ */
+struct libusb_hotplug_callback {
+ /** Context this callback is associated with */
+ struct libusb_context *ctx;
+
+ /** Vendor ID to match or LIBUSB_HOTPLUG_MATCH_ANY */
+ int vendor_id;
+
+ /** Product ID to match or LIBUSB_HOTPLUG_MATCH_ANY */
+ int product_id;
+
+ /** Device class to match or LIBUSB_HOTPLUG_MATCH_ANY */
+ int dev_class;
+
+ /** Hotplug callback flags */
+ libusb_hotplug_flag flags;
+
+ /** Event(s) that will trigger this callback */
+ libusb_hotplug_event events;
+
+ /** Callback function to invoke for matching event/device */
+ libusb_hotplug_callback_fn cb;
+
+ /** Handle for this callback (used to match on deregister) */
+ libusb_hotplug_callback_handle handle;
+
+ /** User data that will be passed to the callback function */
+ void *user_data;
+
+ /** Callback is marked for deletion */
+ int needs_free;
+
+ /** List this callback is registered in (ctx->hotplug_cbs) */
+ struct list_head list;
+};
+
+typedef struct libusb_hotplug_callback libusb_hotplug_callback;
+
+struct libusb_hotplug_message {
+ libusb_hotplug_event event;
+ struct libusb_device *device;
+};
+
+typedef struct libusb_hotplug_message libusb_hotplug_message;
+
+void usbi_hotplug_deregister_all(struct libusb_context *ctx);
+void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
+ libusb_hotplug_event event);
+
+#endif
diff --git a/e502/libusb-1.0/libusb-1.0/io.c b/e502/libusb-1.0/libusb-1.0/io.c
new file mode 100644
index 0000000..b9ca767
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/io.c
@@ -0,0 +1,2618 @@
+/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
+/*
+ * I/O functions for libusb
+ * Copyright © 2007-2009 Daniel Drake
+ * Copyright © 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
+ */
+
+#include "config.h"
+#include
+#include
+#include
+#include
+#include
+#ifdef HAVE_SIGNAL_H
+#include
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include
+#endif
+#ifdef USBI_TIMERFD_AVAILABLE
+#include
+#endif
+
+#include "libusbi.h"
+#include "hotplug.h"
+
+/**
+ * \page io Synchronous and asynchronous device I/O
+ *
+ * \section intro Introduction
+ *
+ * If you're using libusb in your application, you're probably wanting to
+ * perform I/O with devices - you want to perform USB data transfers.
+ *
+ * libusb offers two separate interfaces for device I/O. This page aims to
+ * introduce the two in order to help you decide which one is more suitable
+ * for your application. You can also choose to use both interfaces in your
+ * application by considering each transfer on a case-by-case basis.
+ *
+ * Once you have read through the following discussion, you should consult the
+ * detailed API documentation pages for the details:
+ * - \ref syncio
+ * - \ref asyncio
+ *
+ * \section theory Transfers at a logical level
+ *
+ * At a logical level, USB transfers typically happen in two parts. For
+ * example, when reading data from a endpoint:
+ * -# A request for data is sent to the device
+ * -# Some time later, the incoming data is received by the host
+ *
+ * or when writing data to an endpoint:
+ *
+ * -# The data is sent to the device
+ * -# Some time later, the host receives acknowledgement from the device that
+ * the data has been transferred.
+ *
+ * There may be an indefinite delay between the two steps. Consider a
+ * fictional USB input device with a button that the user can press. In order
+ * to determine when the button is pressed, you would likely submit a request
+ * to read data on a bulk or interrupt endpoint and wait for data to arrive.
+ * Data will arrive when the button is pressed by the user, which is
+ * potentially hours later.
+ *
+ * libusb offers both a synchronous and an asynchronous interface to performing
+ * USB transfers. The main difference is that the synchronous interface
+ * combines both steps indicated above into a single function call, whereas
+ * the asynchronous interface separates them.
+ *
+ * \section sync The synchronous interface
+ *
+ * The synchronous I/O interface allows you to perform a USB transfer with
+ * a single function call. When the function call returns, the transfer has
+ * completed and you can parse the results.
+ *
+ * If you have used the libusb-0.1 before, this I/O style will seem familar to
+ * you. libusb-0.1 only offered a synchronous interface.
+ *
+ * In our input device example, to read button presses you might write code
+ * in the following style:
+\code
+unsigned char data[4];
+int actual_length;
+int r = libusb_bulk_transfer(handle, LIBUSB_ENDPOINT_IN, data, sizeof(data), &actual_length, 0);
+if (r == 0 && actual_length == sizeof(data)) {
+ // results of the transaction can now be found in the data buffer
+ // parse them here and report button press
+} else {
+ error();
+}
+\endcode
+ *
+ * The main advantage of this model is simplicity: you did everything with
+ * a single simple function call.
+ *
+ * However, this interface has its limitations. Your application will sleep
+ * inside libusb_bulk_transfer() until the transaction has completed. If it
+ * takes the user 3 hours to press the button, your application will be
+ * sleeping for that long. Execution will be tied up inside the library -
+ * the entire thread will be useless for that duration.
+ *
+ * Another issue is that by tieing up the thread with that single transaction
+ * there is no possibility of performing I/O with multiple endpoints and/or
+ * multiple devices simultaneously, unless you resort to creating one thread
+ * per transaction.
+ *
+ * Additionally, there is no opportunity to cancel the transfer after the
+ * request has been submitted.
+ *
+ * For details on how to use the synchronous API, see the
+ * \ref syncio "synchronous I/O API documentation" pages.
+ *
+ * \section async The asynchronous interface
+ *
+ * Asynchronous I/O is the most significant new feature in libusb-1.0.
+ * Although it is a more complex interface, it solves all the issues detailed
+ * above.
+ *
+ * Instead of providing which functions that block until the I/O has complete,
+ * libusb's asynchronous interface presents non-blocking functions which
+ * begin a transfer and then return immediately. Your application passes a
+ * callback function pointer to this non-blocking function, which libusb will
+ * call with the results of the transaction when it has completed.
+ *
+ * Transfers which have been submitted through the non-blocking functions
+ * can be cancelled with a separate function call.
+ *
+ * The non-blocking nature of this interface allows you to be simultaneously
+ * performing I/O to multiple endpoints on multiple devices, without having
+ * to use threads.
+ *
+ * This added flexibility does come with some complications though:
+ * - In the interest of being a lightweight library, libusb does not create
+ * threads and can only operate when your application is calling into it. Your
+ * application must call into libusb from it's main loop when events are ready
+ * to be handled, or you must use some other scheme to allow libusb to
+ * undertake whatever work needs to be done.
+ * - libusb also needs to be called into at certain fixed points in time in
+ * order to accurately handle transfer timeouts.
+ * - Memory handling becomes more complex. You cannot use stack memory unless
+ * the function with that stack is guaranteed not to return until the transfer
+ * callback has finished executing.
+ * - You generally lose some linearity from your code flow because submitting
+ * the transfer request is done in a separate function from where the transfer
+ * results are handled. This becomes particularly obvious when you want to
+ * submit a second transfer based on the results of an earlier transfer.
+ *
+ * Internally, libusb's synchronous interface is expressed in terms of function
+ * calls to the asynchronous interface.
+ *
+ * For details on how to use the asynchronous API, see the
+ * \ref asyncio "asynchronous I/O API" documentation pages.
+ */
+
+
+/**
+ * \page packetoverflow Packets and overflows
+ *
+ * \section packets Packet abstraction
+ *
+ * The USB specifications describe how data is transmitted in packets, with
+ * constraints on packet size defined by endpoint descriptors. The host must
+ * not send data payloads larger than the endpoint's maximum packet size.
+ *
+ * libusb and the underlying OS abstract out the packet concept, allowing you
+ * to request transfers of any size. Internally, the request will be divided
+ * up into correctly-sized packets. You do not have to be concerned with
+ * packet sizes, but there is one exception when considering overflows.
+ *
+ * \section overflow Bulk/interrupt transfer overflows
+ *
+ * When requesting data on a bulk endpoint, libusb requires you to supply a
+ * buffer and the maximum number of bytes of data that libusb can put in that
+ * buffer. However, the size of the buffer is not communicated to the device -
+ * the device is just asked to send any amount of data.
+ *
+ * There is no problem if the device sends an amount of data that is less than
+ * or equal to the buffer size. libusb reports this condition to you through
+ * the \ref libusb_transfer::actual_length "libusb_transfer.actual_length"
+ * field.
+ *
+ * Problems may occur if the device attempts to send more data than can fit in
+ * the buffer. libusb reports LIBUSB_TRANSFER_OVERFLOW for this condition but
+ * other behaviour is largely undefined: actual_length may or may not be
+ * accurate, the chunk of data that can fit in the buffer (before overflow)
+ * may or may not have been transferred.
+ *
+ * Overflows are nasty, but can be avoided. Even though you were told to
+ * ignore packets above, think about the lower level details: each transfer is
+ * split into packets (typically small, with a maximum size of 512 bytes).
+ * Overflows can only happen if the final packet in an incoming data transfer
+ * is smaller than the actual packet that the device wants to transfer.
+ * Therefore, you will never see an overflow if your transfer buffer size is a
+ * multiple of the endpoint's packet size: the final packet will either
+ * fill up completely or will be only partially filled.
+ */
+
+/**
+ * @defgroup asyncio Asynchronous device I/O
+ *
+ * This page details libusb's asynchronous (non-blocking) API for USB device
+ * I/O. This interface is very powerful but is also quite complex - you will
+ * need to read this page carefully to understand the necessary considerations
+ * and issues surrounding use of this interface. Simplistic applications
+ * may wish to consider the \ref syncio "synchronous I/O API" instead.
+ *
+ * The asynchronous interface is built around the idea of separating transfer
+ * submission and handling of transfer completion (the synchronous model
+ * combines both of these into one). There may be a long delay between
+ * submission and completion, however the asynchronous submission function
+ * is non-blocking so will return control to your application during that
+ * potentially long delay.
+ *
+ * \section asyncabstraction Transfer abstraction
+ *
+ * For the asynchronous I/O, libusb implements the concept of a generic
+ * transfer entity for all types of I/O (control, bulk, interrupt,
+ * isochronous). The generic transfer object must be treated slightly
+ * differently depending on which type of I/O you are performing with it.
+ *
+ * This is represented by the public libusb_transfer structure type.
+ *
+ * \section asynctrf Asynchronous transfers
+ *
+ * We can view asynchronous I/O as a 5 step process:
+ * -# Allocation: allocate a libusb_transfer
+ * -# Filling: populate the libusb_transfer instance with information
+ * about the transfer you wish to perform
+ * -# Submission: ask libusb to submit the transfer
+ * -# Completion handling: examine transfer results in the
+ * libusb_transfer structure
+ * -# Deallocation: clean up resources
+ *
+ *
+ * \subsection asyncalloc Allocation
+ *
+ * This step involves allocating memory for a USB transfer. This is the
+ * generic transfer object mentioned above. At this stage, the transfer
+ * is "blank" with no details about what type of I/O it will be used for.
+ *
+ * Allocation is done with the libusb_alloc_transfer() function. You must use
+ * this function rather than allocating your own transfers.
+ *
+ * \subsection asyncfill Filling
+ *
+ * This step is where you take a previously allocated transfer and fill it
+ * with information to determine the message type and direction, data buffer,
+ * callback function, etc.
+ *
+ * You can either fill the required fields yourself or you can use the
+ * helper functions: libusb_fill_control_transfer(), libusb_fill_bulk_transfer()
+ * and libusb_fill_interrupt_transfer().
+ *
+ * \subsection asyncsubmit Submission
+ *
+ * When you have allocated a transfer and filled it, you can submit it using
+ * libusb_submit_transfer(). This function returns immediately but can be
+ * regarded as firing off the I/O request in the background.
+ *
+ * \subsection asynccomplete Completion handling
+ *
+ * After a transfer has been submitted, one of four things can happen to it:
+ *
+ * - The transfer completes (i.e. some data was transferred)
+ * - The transfer has a timeout and the timeout expires before all data is
+ * transferred
+ * - The transfer fails due to an error
+ * - The transfer is cancelled
+ *
+ * Each of these will cause the user-specified transfer callback function to
+ * be invoked. It is up to the callback function to determine which of the
+ * above actually happened and to act accordingly.
+ *
+ * The user-specified callback is passed a pointer to the libusb_transfer
+ * structure which was used to setup and submit the transfer. At completion
+ * time, libusb has populated this structure with results of the transfer:
+ * success or failure reason, number of bytes of data transferred, etc. See
+ * the libusb_transfer structure documentation for more information.
+ *
+ * \subsection Deallocation
+ *
+ * When a transfer has completed (i.e. the callback function has been invoked),
+ * you are advised to free the transfer (unless you wish to resubmit it, see
+ * below). Transfers are deallocated with libusb_free_transfer().
+ *
+ * It is undefined behaviour to free a transfer which has not completed.
+ *
+ * \section asyncresubmit Resubmission
+ *
+ * You may be wondering why allocation, filling, and submission are all
+ * separated above where they could reasonably be combined into a single
+ * operation.
+ *
+ * The reason for separation is to allow you to resubmit transfers without
+ * having to allocate new ones every time. This is especially useful for
+ * common situations dealing with interrupt endpoints - you allocate one
+ * transfer, fill and submit it, and when it returns with results you just
+ * resubmit it for the next interrupt.
+ *
+ * \section asynccancel Cancellation
+ *
+ * Another advantage of using the asynchronous interface is that you have
+ * the ability to cancel transfers which have not yet completed. This is
+ * done by calling the libusb_cancel_transfer() function.
+ *
+ * libusb_cancel_transfer() is asynchronous/non-blocking in itself. When the
+ * cancellation actually completes, the transfer's callback function will
+ * be invoked, and the callback function should check the transfer status to
+ * determine that it was cancelled.
+ *
+ * Freeing the transfer after it has been cancelled but before cancellation
+ * has completed will result in undefined behaviour.
+ *
+ * When a transfer is cancelled, some of the data may have been transferred.
+ * libusb will communicate this to you in the transfer callback. Do not assume
+ * that no data was transferred.
+ *
+ * \section bulk_overflows Overflows on device-to-host bulk/interrupt endpoints
+ *
+ * If your device does not have predictable transfer sizes (or it misbehaves),
+ * your application may submit a request for data on an IN endpoint which is
+ * smaller than the data that the device wishes to send. In some circumstances
+ * this will cause an overflow, which is a nasty condition to deal with. See
+ * the \ref packetoverflow page for discussion.
+ *
+ * \section asyncctrl Considerations for control transfers
+ *
+ * The libusb_transfer structure is generic and hence does not
+ * include specific fields for the control-specific setup packet structure.
+ *
+ * In order to perform a control transfer, you must place the 8-byte setup
+ * packet at the start of the data buffer. To simplify this, you could
+ * cast the buffer pointer to type struct libusb_control_setup, or you can
+ * use the helper function libusb_fill_control_setup().
+ *
+ * The wLength field placed in the setup packet must be the length you would
+ * expect to be sent in the setup packet: the length of the payload that
+ * follows (or the expected maximum number of bytes to receive). However,
+ * the length field of the libusb_transfer object must be the length of
+ * the data buffer - i.e. it should be wLength plus the size of
+ * the setup packet (LIBUSB_CONTROL_SETUP_SIZE).
+ *
+ * If you use the helper functions, this is simplified for you:
+ * -# Allocate a buffer of size LIBUSB_CONTROL_SETUP_SIZE plus the size of the
+ * data you are sending/requesting.
+ * -# Call libusb_fill_control_setup() on the data buffer, using the transfer
+ * request size as the wLength value (i.e. do not include the extra space you
+ * allocated for the control setup).
+ * -# If this is a host-to-device transfer, place the data to be transferred
+ * in the data buffer, starting at offset LIBUSB_CONTROL_SETUP_SIZE.
+ * -# Call libusb_fill_control_transfer() to associate the data buffer with
+ * the transfer (and to set the remaining details such as callback and timeout).
+ * - Note that there is no parameter to set the length field of the transfer.
+ * The length is automatically inferred from the wLength field of the setup
+ * packet.
+ * -# Submit the transfer.
+ *
+ * The multi-byte control setup fields (wValue, wIndex and wLength) must
+ * be given in little-endian byte order (the endianness of the USB bus).
+ * Endianness conversion is transparently handled by
+ * libusb_fill_control_setup() which is documented to accept host-endian
+ * values.
+ *
+ * Further considerations are needed when handling transfer completion in
+ * your callback function:
+ * - As you might expect, the setup packet will still be sitting at the start
+ * of the data buffer.
+ * - If this was a device-to-host transfer, the received data will be sitting
+ * at offset LIBUSB_CONTROL_SETUP_SIZE into the buffer.
+ * - The actual_length field of the transfer structure is relative to the
+ * wLength of the setup packet, rather than the size of the data buffer. So,
+ * if your wLength was 4, your transfer's length was 12, then you
+ * should expect an actual_length of 4 to indicate that the data was
+ * transferred in entirity.
+ *
+ * To simplify parsing of setup packets and obtaining the data from the
+ * correct offset, you may wish to use the libusb_control_transfer_get_data()
+ * and libusb_control_transfer_get_setup() functions within your transfer
+ * callback.
+ *
+ * Even though control endpoints do not halt, a completed control transfer
+ * may have a LIBUSB_TRANSFER_STALL status code. This indicates the control
+ * request was not supported.
+ *
+ * \section asyncintr Considerations for interrupt transfers
+ *
+ * All interrupt transfers are performed using the polling interval presented
+ * by the bInterval value of the endpoint descriptor.
+ *
+ * \section asynciso Considerations for isochronous transfers
+ *
+ * Isochronous transfers are more complicated than transfers to
+ * non-isochronous endpoints.
+ *
+ * To perform I/O to an isochronous endpoint, allocate the transfer by calling
+ * libusb_alloc_transfer() with an appropriate number of isochronous packets.
+ *
+ * During filling, set \ref libusb_transfer::type "type" to
+ * \ref libusb_transfer_type::LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
+ * "LIBUSB_TRANSFER_TYPE_ISOCHRONOUS", and set
+ * \ref libusb_transfer::num_iso_packets "num_iso_packets" to a value less than
+ * or equal to the number of packets you requested during allocation.
+ * libusb_alloc_transfer() does not set either of these fields for you, given
+ * that you might not even use the transfer on an isochronous endpoint.
+ *
+ * Next, populate the length field for the first num_iso_packets entries in
+ * the \ref libusb_transfer::iso_packet_desc "iso_packet_desc" array. Section
+ * 5.6.3 of the USB2 specifications describe how the maximum isochronous
+ * packet length is determined by the wMaxPacketSize field in the endpoint
+ * descriptor.
+ * Two functions can help you here:
+ *
+ * - libusb_get_max_iso_packet_size() is an easy way to determine the max
+ * packet size for an isochronous endpoint. Note that the maximum packet
+ * size is actually the maximum number of bytes that can be transmitted in
+ * a single microframe, therefore this function multiplies the maximum number
+ * of bytes per transaction by the number of transaction opportunities per
+ * microframe.
+ * - libusb_set_iso_packet_lengths() assigns the same length to all packets
+ * within a transfer, which is usually what you want.
+ *
+ * For outgoing transfers, you'll obviously fill the buffer and populate the
+ * packet descriptors in hope that all the data gets transferred. For incoming
+ * transfers, you must ensure the buffer has sufficient capacity for
+ * the situation where all packets transfer the full amount of requested data.
+ *
+ * Completion handling requires some extra consideration. The
+ * \ref libusb_transfer::actual_length "actual_length" field of the transfer
+ * is meaningless and should not be examined; instead you must refer to the
+ * \ref libusb_iso_packet_descriptor::actual_length "actual_length" field of
+ * each individual packet.
+ *
+ * The \ref libusb_transfer::status "status" field of the transfer is also a
+ * little misleading:
+ * - If the packets were submitted and the isochronous data microframes
+ * completed normally, status will have value
+ * \ref libusb_transfer_status::LIBUSB_TRANSFER_COMPLETED
+ * "LIBUSB_TRANSFER_COMPLETED". Note that bus errors and software-incurred
+ * delays are not counted as transfer errors; the transfer.status field may
+ * indicate COMPLETED even if some or all of the packets failed. Refer to
+ * the \ref libusb_iso_packet_descriptor::status "status" field of each
+ * individual packet to determine packet failures.
+ * - The status field will have value
+ * \ref libusb_transfer_status::LIBUSB_TRANSFER_ERROR
+ * "LIBUSB_TRANSFER_ERROR" only when serious errors were encountered.
+ * - Other transfer status codes occur with normal behaviour.
+ *
+ * The data for each packet will be found at an offset into the buffer that
+ * can be calculated as if each prior packet completed in full. The
+ * libusb_get_iso_packet_buffer() and libusb_get_iso_packet_buffer_simple()
+ * functions may help you here.
+ *
+ * \section asyncmem Memory caveats
+ *
+ * In most circumstances, it is not safe to use stack memory for transfer
+ * buffers. This is because the function that fired off the asynchronous
+ * transfer may return before libusb has finished using the buffer, and when
+ * the function returns it's stack gets destroyed. This is true for both
+ * host-to-device and device-to-host transfers.
+ *
+ * The only case in which it is safe to use stack memory is where you can
+ * guarantee that the function owning the stack space for the buffer does not
+ * return until after the transfer's callback function has completed. In every
+ * other case, you need to use heap memory instead.
+ *
+ * \section asyncflags Fine control
+ *
+ * Through using this asynchronous interface, you may find yourself repeating
+ * a few simple operations many times. You can apply a bitwise OR of certain
+ * flags to a transfer to simplify certain things:
+ * - \ref libusb_transfer_flags::LIBUSB_TRANSFER_SHORT_NOT_OK
+ * "LIBUSB_TRANSFER_SHORT_NOT_OK" results in transfers which transferred
+ * less than the requested amount of data being marked with status
+ * \ref libusb_transfer_status::LIBUSB_TRANSFER_ERROR "LIBUSB_TRANSFER_ERROR"
+ * (they would normally be regarded as COMPLETED)
+ * - \ref libusb_transfer_flags::LIBUSB_TRANSFER_FREE_BUFFER
+ * "LIBUSB_TRANSFER_FREE_BUFFER" allows you to ask libusb to free the transfer
+ * buffer when freeing the transfer.
+ * - \ref libusb_transfer_flags::LIBUSB_TRANSFER_FREE_TRANSFER
+ * "LIBUSB_TRANSFER_FREE_TRANSFER" causes libusb to automatically free the
+ * transfer after the transfer callback returns.
+ *
+ * \section asyncevent Event handling
+ *
+ * An asynchronous model requires that libusb perform work at various
+ * points in time - namely processing the results of previously-submitted
+ * transfers and invoking the user-supplied callback function.
+ *
+ * This gives rise to the libusb_handle_events() function which your
+ * application must call into when libusb has work do to. This gives libusb
+ * the opportunity to reap pending transfers, invoke callbacks, etc.
+ *
+ * There are 2 different approaches to dealing with libusb_handle_events:
+ *
+ * -# Repeatedly call libusb_handle_events() in blocking mode from a dedicated
+ * thread.
+ * -# Integrate libusb with your application's main event loop. libusb
+ * exposes a set of file descriptors which allow you to do this.
+ *
+ * The first approach has the big advantage that it will also work on Windows
+ * were libusb' poll API for select / poll integration is not available. So
+ * if you want to support Windows and use the async API, you must use this
+ * approach, see the \ref eventthread "Using an event handling thread" section
+ * below for details.
+ *
+ * If you prefer a single threaded approach with a single central event loop,
+ * see the \ref poll "polling and timing" section for how to integrate libusb
+ * into your application's main event loop.
+ *
+ * \section eventthread Using an event handling thread
+ *
+ * Lets begin with stating the obvious: If you're going to use a separate
+ * thread for libusb event handling, your callback functions MUST be
+ * threadsafe.
+ *
+ * Other then that doing event handling from a separate thread, is mostly
+ * simple. You can use an event thread function as follows:
+\code
+void *event_thread_func(void *ctx)
+{
+ while (event_thread_run)
+ libusb_handle_events(ctx);
+
+ return NULL;
+}
+\endcode
+ *
+ * There is one caveat though, stopping this thread requires setting the
+ * event_thread_run variable to 0, and after that libusb_handle_events() needs
+ * to return control to event_thread_func. But unless some event happens,
+ * libusb_handle_events() will not return.
+ *
+ * There are 2 different ways of dealing with this, depending on if your
+ * application uses libusb' \ref hotplug "hotplug" support or not.
+ *
+ * Applications which do not use hotplug support, should not start the event
+ * thread until after their first call to libusb_open(), and should stop the
+ * thread when closing the last open device as follows:
+\code
+void my_close_handle(libusb_device_handle *handle)
+{
+ if (open_devs == 1)
+ event_thread_run = 0;
+
+ libusb_close(handle); // This wakes up libusb_handle_events()
+
+ if (open_devs == 1)
+ pthread_join(event_thread);
+
+ open_devs--;
+}
+\endcode
+ *
+ * Applications using hotplug support should start the thread at program init,
+ * after having successfully called libusb_hotplug_register_callback(), and
+ * should stop the thread at program exit as follows:
+\code
+void my_libusb_exit(void)
+{
+ event_thread_run = 0;
+ libusb_hotplug_deregister_callback(ctx, hotplug_cb_handle); // This wakes up libusb_handle_events()
+ pthread_join(event_thread);
+ libusb_exit(ctx);
+}
+\endcode
+ */
+
+/**
+ * @defgroup poll Polling and timing
+ *
+ * This page documents libusb's functions for polling events and timing.
+ * These functions are only necessary for users of the
+ * \ref asyncio "asynchronous API". If you are only using the simpler
+ * \ref syncio "synchronous API" then you do not need to ever call these
+ * functions.
+ *
+ * The justification for the functionality described here has already been
+ * discussed in the \ref asyncevent "event handling" section of the
+ * asynchronous API documentation. In summary, libusb does not create internal
+ * threads for event processing and hence relies on your application calling
+ * into libusb at certain points in time so that pending events can be handled.
+ *
+ * Your main loop is probably already calling poll() or select() or a
+ * variant on a set of file descriptors for other event sources (e.g. keyboard
+ * button presses, mouse movements, network sockets, etc). You then add
+ * libusb's file descriptors to your poll()/select() calls, and when activity
+ * is detected on such descriptors you know it is time to call
+ * libusb_handle_events().
+ *
+ * There is one final event handling complication. libusb supports
+ * asynchronous transfers which time out after a specified time period.
+ *
+ * On some platforms a timerfd is used, so the timeout handling is just another
+ * fd, on other platforms this requires that libusb is called into at or after
+ * the timeout to handle it. So, in addition to considering libusb's file
+ * descriptors in your main event loop, you must also consider that libusb
+ * sometimes needs to be called into at fixed points in time even when there
+ * is no file descriptor activity, see \ref polltime details.
+ *
+ * In order to know precisely when libusb needs to be called into, libusb
+ * offers you a set of pollable file descriptors and information about when
+ * the next timeout expires.
+ *
+ * If you are using the asynchronous I/O API, you must take one of the two
+ * following options, otherwise your I/O will not complete.
+ *
+ * \section pollsimple The simple option
+ *
+ * If your application revolves solely around libusb and does not need to
+ * handle other event sources, you can have a program structure as follows:
+\code
+// initialize libusb
+// find and open device
+// maybe fire off some initial async I/O
+
+while (user_has_not_requested_exit)
+ libusb_handle_events(ctx);
+
+// clean up and exit
+\endcode
+ *
+ * With such a simple main loop, you do not have to worry about managing
+ * sets of file descriptors or handling timeouts. libusb_handle_events() will
+ * handle those details internally.
+ *
+ * \section pollmain The more advanced option
+ *
+ * \note This functionality is currently only available on Unix-like platforms.
+ * On Windows, libusb_get_pollfds() simply returns NULL. Applications which
+ * want to support Windows are advised to use an \ref eventthread
+ * "event handling thread" instead.
+ *
+ * In more advanced applications, you will already have a main loop which
+ * is monitoring other event sources: network sockets, X11 events, mouse
+ * movements, etc. Through exposing a set of file descriptors, libusb is
+ * designed to cleanly integrate into such main loops.
+ *
+ * In addition to polling file descriptors for the other event sources, you
+ * take a set of file descriptors from libusb and monitor those too. When you
+ * detect activity on libusb's file descriptors, you call
+ * libusb_handle_events_timeout() in non-blocking mode.
+ *
+ * What's more, libusb may also need to handle events at specific moments in
+ * time. No file descriptor activity is generated at these times, so your
+ * own application needs to be continually aware of when the next one of these
+ * moments occurs (through calling libusb_get_next_timeout()), and then it
+ * needs to call libusb_handle_events_timeout() in non-blocking mode when
+ * these moments occur. This means that you need to adjust your
+ * poll()/select() timeout accordingly.
+ *
+ * libusb provides you with a set of file descriptors to poll and expects you
+ * to poll all of them, treating them as a single entity. The meaning of each
+ * file descriptor in the set is an internal implementation detail,
+ * platform-dependent and may vary from release to release. Don't try and
+ * interpret the meaning of the file descriptors, just do as libusb indicates,
+ * polling all of them at once.
+ *
+ * In pseudo-code, you want something that looks like:
+\code
+// initialise libusb
+
+libusb_get_pollfds(ctx)
+while (user has not requested application exit) {
+ libusb_get_next_timeout(ctx);
+ poll(on libusb file descriptors plus any other event sources of interest,
+ using a timeout no larger than the value libusb just suggested)
+ if (poll() indicated activity on libusb file descriptors)
+ libusb_handle_events_timeout(ctx, &zero_tv);
+ if (time has elapsed to or beyond the libusb timeout)
+ libusb_handle_events_timeout(ctx, &zero_tv);
+ // handle events from other sources here
+}
+
+// clean up and exit
+\endcode
+ *
+ * \subsection polltime Notes on time-based events
+ *
+ * The above complication with having to track time and call into libusb at
+ * specific moments is a bit of a headache. For maximum compatibility, you do
+ * need to write your main loop as above, but you may decide that you can
+ * restrict the supported platforms of your application and get away with
+ * a more simplistic scheme.
+ *
+ * These time-based event complications are \b not required on the following
+ * platforms:
+ * - Darwin
+ * - Linux, provided that the following version requirements are satisfied:
+ * - Linux v2.6.27 or newer, compiled with timerfd support
+ * - glibc v2.9 or newer
+ * - libusb v1.0.5 or newer
+ *
+ * Under these configurations, libusb_get_next_timeout() will \em always return
+ * 0, so your main loop can be simplified to:
+\code
+// initialise libusb
+
+libusb_get_pollfds(ctx)
+while (user has not requested application exit) {
+ poll(on libusb file descriptors plus any other event sources of interest,
+ using any timeout that you like)
+ if (poll() indicated activity on libusb file descriptors)
+ libusb_handle_events_timeout(ctx, &zero_tv);
+ // handle events from other sources here
+}
+
+// clean up and exit
+\endcode
+ *
+ * Do remember that if you simplify your main loop to the above, you will
+ * lose compatibility with some platforms (including legacy Linux platforms,
+ * and any future platforms supported by libusb which may have time-based
+ * event requirements). The resultant problems will likely appear as
+ * strange bugs in your application.
+ *
+ * You can use the libusb_pollfds_handle_timeouts() function to do a runtime
+ * check to see if it is safe to ignore the time-based event complications.
+ * If your application has taken the shortcut of ignoring libusb's next timeout
+ * in your main loop, then you are advised to check the return value of
+ * libusb_pollfds_handle_timeouts() during application startup, and to abort
+ * if the platform does suffer from these timing complications.
+ *
+ * \subsection fdsetchange Changes in the file descriptor set
+ *
+ * The set of file descriptors that libusb uses as event sources may change
+ * during the life of your application. Rather than having to repeatedly
+ * call libusb_get_pollfds(), you can set up notification functions for when
+ * the file descriptor set changes using libusb_set_pollfd_notifiers().
+ *
+ * \subsection mtissues Multi-threaded considerations
+ *
+ * Unfortunately, the situation is complicated further when multiple threads
+ * come into play. If two threads are monitoring the same file descriptors,
+ * the fact that only one thread will be woken up when an event occurs causes
+ * some headaches.
+ *
+ * The events lock, event waiters lock, and libusb_handle_events_locked()
+ * entities are added to solve these problems. You do not need to be concerned
+ * with these entities otherwise.
+ *
+ * See the extra documentation: \ref mtasync
+ */
+
+/** \page mtasync Multi-threaded applications and asynchronous I/O
+ *
+ * libusb is a thread-safe library, but extra considerations must be applied
+ * to applications which interact with libusb from multiple threads.
+ *
+ * The underlying issue that must be addressed is that all libusb I/O
+ * revolves around monitoring file descriptors through the poll()/select()
+ * system calls. This is directly exposed at the
+ * \ref asyncio "asynchronous interface" but it is important to note that the
+ * \ref syncio "synchronous interface" is implemented on top of the
+ * asynchonrous interface, therefore the same considerations apply.
+ *
+ * The issue is that if two or more threads are concurrently calling poll()
+ * or select() on libusb's file descriptors then only one of those threads
+ * will be woken up when an event arrives. The others will be completely
+ * oblivious that anything has happened.
+ *
+ * Consider the following pseudo-code, which submits an asynchronous transfer
+ * then waits for its completion. This style is one way you could implement a
+ * synchronous interface on top of the asynchronous interface (and libusb
+ * does something similar, albeit more advanced due to the complications
+ * explained on this page).
+ *
+\code
+void cb(struct libusb_transfer *transfer)
+{
+ int *completed = transfer->user_data;
+ *completed = 1;
+}
+
+void myfunc() {
+ struct libusb_transfer *transfer;
+ unsigned char buffer[LIBUSB_CONTROL_SETUP_SIZE] __attribute__ ((aligned (2)));
+ int completed = 0;
+
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_control_setup(buffer,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, 0x04, 0x01, 0, 0);
+ libusb_fill_control_transfer(transfer, dev, buffer, cb, &completed, 1000);
+ libusb_submit_transfer(transfer);
+
+ while (!completed) {
+ poll(libusb file descriptors, 120*1000);
+ if (poll indicates activity)
+ libusb_handle_events_timeout(ctx, &zero_tv);
+ }
+ printf("completed!");
+ // other code here
+}
+\endcode
+ *
+ * Here we are serializing completion of an asynchronous event
+ * against a condition - the condition being completion of a specific transfer.
+ * The poll() loop has a long timeout to minimize CPU usage during situations
+ * when nothing is happening (it could reasonably be unlimited).
+ *
+ * If this is the only thread that is polling libusb's file descriptors, there
+ * is no problem: there is no danger that another thread will swallow up the
+ * event that we are interested in. On the other hand, if there is another
+ * thread polling the same descriptors, there is a chance that it will receive
+ * the event that we were interested in. In this situation, myfunc()
+ * will only realise that the transfer has completed on the next iteration of
+ * the loop, up to 120 seconds later. Clearly a two-minute delay is
+ * undesirable, and don't even think about using short timeouts to circumvent
+ * this issue!
+ *
+ * The solution here is to ensure that no two threads are ever polling the
+ * file descriptors at the same time. A naive implementation of this would
+ * impact the capabilities of the library, so libusb offers the scheme
+ * documented below to ensure no loss of functionality.
+ *
+ * Before we go any further, it is worth mentioning that all libusb-wrapped
+ * event handling procedures fully adhere to the scheme documented below.
+ * This includes libusb_handle_events() and its variants, and all the
+ * synchronous I/O functions - libusb hides this headache from you.
+ *
+ * \section Using libusb_handle_events() from multiple threads
+ *
+ * Even when only using libusb_handle_events() and synchronous I/O functions,
+ * you can still have a race condition. You might be tempted to solve the
+ * above with libusb_handle_events() like so:
+ *
+\code
+ libusb_submit_transfer(transfer);
+
+ while (!completed) {
+ libusb_handle_events(ctx);
+ }
+ printf("completed!");
+\endcode
+ *
+ * This however has a race between the checking of completed and
+ * libusb_handle_events() acquiring the events lock, so another thread
+ * could have completed the transfer, resulting in this thread hanging
+ * until either a timeout or another event occurs. See also commit
+ * 6696512aade99bb15d6792af90ae329af270eba6 which fixes this in the
+ * synchronous API implementation of libusb.
+ *
+ * Fixing this race requires checking the variable completed only after
+ * taking the event lock, which defeats the concept of just calling
+ * libusb_handle_events() without worrying about locking. This is why
+ * libusb-1.0.9 introduces the new libusb_handle_events_timeout_completed()
+ * and libusb_handle_events_completed() functions, which handles doing the
+ * completion check for you after they have acquired the lock:
+ *
+\code
+ libusb_submit_transfer(transfer);
+
+ while (!completed) {
+ libusb_handle_events_completed(ctx, &completed);
+ }
+ printf("completed!");
+\endcode
+ *
+ * This nicely fixes the race in our example. Note that if all you want to
+ * do is submit a single transfer and wait for its completion, then using
+ * one of the synchronous I/O functions is much easier.
+ *
+ * \section eventlock The events lock
+ *
+ * The problem is when we consider the fact that libusb exposes file
+ * descriptors to allow for you to integrate asynchronous USB I/O into
+ * existing main loops, effectively allowing you to do some work behind
+ * libusb's back. If you do take libusb's file descriptors and pass them to
+ * poll()/select() yourself, you need to be aware of the associated issues.
+ *
+ * The first concept to be introduced is the events lock. The events lock
+ * is used to serialize threads that want to handle events, such that only
+ * one thread is handling events at any one time.
+ *
+ * You must take the events lock before polling libusb file descriptors,
+ * using libusb_lock_events(). You must release the lock as soon as you have
+ * aborted your poll()/select() loop, using libusb_unlock_events().
+ *
+ * \section threadwait Letting other threads do the work for you
+ *
+ * Although the events lock is a critical part of the solution, it is not
+ * enough on it's own. You might wonder if the following is sufficient...
+\code
+ libusb_lock_events(ctx);
+ while (!completed) {
+ poll(libusb file descriptors, 120*1000);
+ if (poll indicates activity)
+ libusb_handle_events_timeout(ctx, &zero_tv);
+ }
+ libusb_unlock_events(ctx);
+\endcode
+ * ...and the answer is that it is not. This is because the transfer in the
+ * code shown above may take a long time (say 30 seconds) to complete, and
+ * the lock is not released until the transfer is completed.
+ *
+ * Another thread with similar code that wants to do event handling may be
+ * working with a transfer that completes after a few milliseconds. Despite
+ * having such a quick completion time, the other thread cannot check that
+ * status of its transfer until the code above has finished (30 seconds later)
+ * due to contention on the lock.
+ *
+ * To solve this, libusb offers you a mechanism to determine when another
+ * thread is handling events. It also offers a mechanism to block your thread
+ * until the event handling thread has completed an event (and this mechanism
+ * does not involve polling of file descriptors).
+ *
+ * After determining that another thread is currently handling events, you
+ * obtain the event waiters lock using libusb_lock_event_waiters().
+ * You then re-check that some other thread is still handling events, and if
+ * so, you call libusb_wait_for_event().
+ *
+ * libusb_wait_for_event() puts your application to sleep until an event
+ * occurs, or until a thread releases the events lock. When either of these
+ * things happen, your thread is woken up, and should re-check the condition
+ * it was waiting on. It should also re-check that another thread is handling
+ * events, and if not, it should start handling events itself.
+ *
+ * This looks like the following, as pseudo-code:
+\code
+retry:
+if (libusb_try_lock_events(ctx) == 0) {
+ // we obtained the event lock: do our own event handling
+ while (!completed) {
+ if (!libusb_event_handling_ok(ctx)) {
+ libusb_unlock_events(ctx);
+ goto retry;
+ }
+ poll(libusb file descriptors, 120*1000);
+ if (poll indicates activity)
+ libusb_handle_events_locked(ctx, 0);
+ }
+ libusb_unlock_events(ctx);
+} else {
+ // another thread is doing event handling. wait for it to signal us that
+ // an event has completed
+ libusb_lock_event_waiters(ctx);
+
+ while (!completed) {
+ // now that we have the event waiters lock, double check that another
+ // thread is still handling events for us. (it may have ceased handling
+ // events in the time it took us to reach this point)
+ if (!libusb_event_handler_active(ctx)) {
+ // whoever was handling events is no longer doing so, try again
+ libusb_unlock_event_waiters(ctx);
+ goto retry;
+ }
+
+ libusb_wait_for_event(ctx, NULL);
+ }
+ libusb_unlock_event_waiters(ctx);
+}
+printf("completed!\n");
+\endcode
+ *
+ * A naive look at the above code may suggest that this can only support
+ * one event waiter (hence a total of 2 competing threads, the other doing
+ * event handling), because the event waiter seems to have taken the event
+ * waiters lock while waiting for an event. However, the system does support
+ * multiple event waiters, because libusb_wait_for_event() actually drops
+ * the lock while waiting, and reaquires it before continuing.
+ *
+ * We have now implemented code which can dynamically handle situations where
+ * nobody is handling events (so we should do it ourselves), and it can also
+ * handle situations where another thread is doing event handling (so we can
+ * piggyback onto them). It is also equipped to handle a combination of
+ * the two, for example, another thread is doing event handling, but for
+ * whatever reason it stops doing so before our condition is met, so we take
+ * over the event handling.
+ *
+ * Four functions were introduced in the above pseudo-code. Their importance
+ * should be apparent from the code shown above.
+ * -# libusb_try_lock_events() is a non-blocking function which attempts
+ * to acquire the events lock but returns a failure code if it is contended.
+ * -# libusb_event_handling_ok() checks that libusb is still happy for your
+ * thread to be performing event handling. Sometimes, libusb needs to
+ * interrupt the event handler, and this is how you can check if you have
+ * been interrupted. If this function returns 0, the correct behaviour is
+ * for you to give up the event handling lock, and then to repeat the cycle.
+ * The following libusb_try_lock_events() will fail, so you will become an
+ * events waiter. For more information on this, read \ref fullstory below.
+ * -# libusb_handle_events_locked() is a variant of
+ * libusb_handle_events_timeout() that you can call while holding the
+ * events lock. libusb_handle_events_timeout() itself implements similar
+ * logic to the above, so be sure not to call it when you are
+ * "working behind libusb's back", as is the case here.
+ * -# libusb_event_handler_active() determines if someone is currently
+ * holding the events lock
+ *
+ * You might be wondering why there is no function to wake up all threads
+ * blocked on libusb_wait_for_event(). This is because libusb can do this
+ * internally: it will wake up all such threads when someone calls
+ * libusb_unlock_events() or when a transfer completes (at the point after its
+ * callback has returned).
+ *
+ * \subsection fullstory The full story
+ *
+ * The above explanation should be enough to get you going, but if you're
+ * really thinking through the issues then you may be left with some more
+ * questions regarding libusb's internals. If you're curious, read on, and if
+ * not, skip to the next section to avoid confusing yourself!
+ *
+ * The immediate question that may spring to mind is: what if one thread
+ * modifies the set of file descriptors that need to be polled while another
+ * thread is doing event handling?
+ *
+ * There are 2 situations in which this may happen.
+ * -# libusb_open() will add another file descriptor to the poll set,
+ * therefore it is desirable to interrupt the event handler so that it
+ * restarts, picking up the new descriptor.
+ * -# libusb_close() will remove a file descriptor from the poll set. There
+ * are all kinds of race conditions that could arise here, so it is
+ * important that nobody is doing event handling at this time.
+ *
+ * libusb handles these issues internally, so application developers do not
+ * have to stop their event handlers while opening/closing devices. Here's how
+ * it works, focusing on the libusb_close() situation first:
+ *
+ * -# During initialization, libusb opens an internal pipe, and it adds the read
+ * end of this pipe to the set of file descriptors to be polled.
+ * -# During libusb_close(), libusb writes some dummy data on this control pipe.
+ * This immediately interrupts the event handler. libusb also records
+ * internally that it is trying to interrupt event handlers for this
+ * high-priority event.
+ * -# At this point, some of the functions described above start behaving
+ * differently:
+ * - libusb_event_handling_ok() starts returning 1, indicating that it is NOT
+ * OK for event handling to continue.
+ * - libusb_try_lock_events() starts returning 1, indicating that another
+ * thread holds the event handling lock, even if the lock is uncontended.
+ * - libusb_event_handler_active() starts returning 1, indicating that
+ * another thread is doing event handling, even if that is not true.
+ * -# The above changes in behaviour result in the event handler stopping and
+ * giving up the events lock very quickly, giving the high-priority
+ * libusb_close() operation a "free ride" to acquire the events lock. All
+ * threads that are competing to do event handling become event waiters.
+ * -# With the events lock held inside libusb_close(), libusb can safely remove
+ * a file descriptor from the poll set, in the safety of knowledge that
+ * nobody is polling those descriptors or trying to access the poll set.
+ * -# After obtaining the events lock, the close operation completes very
+ * quickly (usually a matter of milliseconds) and then immediately releases
+ * the events lock.
+ * -# At the same time, the behaviour of libusb_event_handling_ok() and friends
+ * reverts to the original, documented behaviour.
+ * -# The release of the events lock causes the threads that are waiting for
+ * events to be woken up and to start competing to become event handlers
+ * again. One of them will succeed; it will then re-obtain the list of poll
+ * descriptors, and USB I/O will then continue as normal.
+ *
+ * libusb_open() is similar, and is actually a more simplistic case. Upon a
+ * call to libusb_open():
+ *
+ * -# The device is opened and a file descriptor is added to the poll set.
+ * -# libusb sends some dummy data on the control pipe, and records that it
+ * is trying to modify the poll descriptor set.
+ * -# The event handler is interrupted, and the same behaviour change as for
+ * libusb_close() takes effect, causing all event handling threads to become
+ * event waiters.
+ * -# The libusb_open() implementation takes its free ride to the events lock.
+ * -# Happy that it has successfully paused the events handler, libusb_open()
+ * releases the events lock.
+ * -# The event waiter threads are all woken up and compete to become event
+ * handlers again. The one that succeeds will obtain the list of poll
+ * descriptors again, which will include the addition of the new device.
+ *
+ * \subsection concl Closing remarks
+ *
+ * The above may seem a little complicated, but hopefully I have made it clear
+ * why such complications are necessary. Also, do not forget that this only
+ * applies to applications that take libusb's file descriptors and integrate
+ * them into their own polling loops.
+ *
+ * You may decide that it is OK for your multi-threaded application to ignore
+ * some of the rules and locks detailed above, because you don't think that
+ * two threads can ever be polling the descriptors at the same time. If that
+ * is the case, then that's good news for you because you don't have to worry.
+ * But be careful here; remember that the synchronous I/O functions do event
+ * handling internally. If you have one thread doing event handling in a loop
+ * (without implementing the rules and locking semantics documented above)
+ * and another trying to send a synchronous USB transfer, you will end up with
+ * two threads monitoring the same descriptors, and the above-described
+ * undesirable behaviour occuring. The solution is for your polling thread to
+ * play by the rules; the synchronous I/O functions do so, and this will result
+ * in them getting along in perfect harmony.
+ *
+ * If you do have a dedicated thread doing event handling, it is perfectly
+ * legal for it to take the event handling lock for long periods of time. Any
+ * synchronous I/O functions you call from other threads will transparently
+ * fall back to the "event waiters" mechanism detailed above. The only
+ * consideration that your event handling thread must apply is the one related
+ * to libusb_event_handling_ok(): you must call this before every poll(), and
+ * give up the events lock if instructed.
+ */
+
+int usbi_io_init(struct libusb_context *ctx)
+{
+ int r;
+
+ usbi_mutex_init(&ctx->flying_transfers_lock, NULL);
+ usbi_mutex_init(&ctx->pollfds_lock, NULL);
+ usbi_mutex_init(&ctx->pollfd_modify_lock, NULL);
+ usbi_mutex_init_recursive(&ctx->events_lock, NULL);
+ usbi_mutex_init(&ctx->event_waiters_lock, NULL);
+ usbi_cond_init(&ctx->event_waiters_cond, NULL);
+ list_init(&ctx->flying_transfers);
+ list_init(&ctx->pollfds);
+
+ /* FIXME should use an eventfd on kernels that support it */
+ r = usbi_pipe(ctx->ctrl_pipe);
+ if (r < 0) {
+ r = LIBUSB_ERROR_OTHER;
+ goto err;
+ }
+
+ r = usbi_add_pollfd(ctx, ctx->ctrl_pipe[0], POLLIN);
+ if (r < 0)
+ goto err_close_pipe;
+
+ /* create hotplug pipe */
+ r = usbi_pipe(ctx->hotplug_pipe);
+ if (r < 0) {
+ r = LIBUSB_ERROR_OTHER;
+ goto err;
+ }
+
+ r = usbi_add_pollfd(ctx, ctx->hotplug_pipe[0], POLLIN);
+ if (r < 0)
+ goto err_close_hp_pipe;
+
+#ifdef USBI_TIMERFD_AVAILABLE
+ ctx->timerfd = timerfd_create(usbi_backend->get_timerfd_clockid(),
+ TFD_NONBLOCK);
+ if (ctx->timerfd >= 0) {
+ usbi_dbg("using timerfd for timeouts");
+ r = usbi_add_pollfd(ctx, ctx->timerfd, POLLIN);
+ if (r < 0) {
+ usbi_remove_pollfd(ctx, ctx->ctrl_pipe[0]);
+ close(ctx->timerfd);
+ goto err_close_hp_pipe;
+ }
+ } else {
+ usbi_dbg("timerfd not available (code %d error %d)", ctx->timerfd, errno);
+ ctx->timerfd = -1;
+ }
+#endif
+
+ return 0;
+
+err_close_hp_pipe:
+ usbi_close(ctx->hotplug_pipe[0]);
+ usbi_close(ctx->hotplug_pipe[1]);
+err_close_pipe:
+ usbi_close(ctx->ctrl_pipe[0]);
+ usbi_close(ctx->ctrl_pipe[1]);
+err:
+ usbi_mutex_destroy(&ctx->flying_transfers_lock);
+ usbi_mutex_destroy(&ctx->pollfds_lock);
+ usbi_mutex_destroy(&ctx->pollfd_modify_lock);
+ usbi_mutex_destroy(&ctx->events_lock);
+ usbi_mutex_destroy(&ctx->event_waiters_lock);
+ usbi_cond_destroy(&ctx->event_waiters_cond);
+ return r;
+}
+
+void usbi_io_exit(struct libusb_context *ctx)
+{
+ usbi_remove_pollfd(ctx, ctx->ctrl_pipe[0]);
+ usbi_close(ctx->ctrl_pipe[0]);
+ usbi_close(ctx->ctrl_pipe[1]);
+ usbi_remove_pollfd(ctx, ctx->hotplug_pipe[0]);
+ usbi_close(ctx->hotplug_pipe[0]);
+ usbi_close(ctx->hotplug_pipe[1]);
+#ifdef USBI_TIMERFD_AVAILABLE
+ if (usbi_using_timerfd(ctx)) {
+ usbi_remove_pollfd(ctx, ctx->timerfd);
+ close(ctx->timerfd);
+ }
+#endif
+ usbi_mutex_destroy(&ctx->flying_transfers_lock);
+ usbi_mutex_destroy(&ctx->pollfds_lock);
+ usbi_mutex_destroy(&ctx->pollfd_modify_lock);
+ usbi_mutex_destroy(&ctx->events_lock);
+ usbi_mutex_destroy(&ctx->event_waiters_lock);
+ usbi_cond_destroy(&ctx->event_waiters_cond);
+}
+
+static int calculate_timeout(struct usbi_transfer *transfer)
+{
+ int r;
+ struct timespec current_time;
+ unsigned int timeout =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout;
+
+ if (!timeout)
+ return 0;
+
+ r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, ¤t_time);
+ if (r < 0) {
+ usbi_err(ITRANSFER_CTX(transfer),
+ "failed to read monotonic clock, errno=%d", errno);
+ return r;
+ }
+
+ current_time.tv_sec += timeout / 1000;
+ current_time.tv_nsec += (timeout % 1000) * 1000000;
+
+ while (current_time.tv_nsec >= 1000000000) {
+ current_time.tv_nsec -= 1000000000;
+ current_time.tv_sec++;
+ }
+
+ TIMESPEC_TO_TIMEVAL(&transfer->timeout, ¤t_time);
+ return 0;
+}
+
+/* add a transfer to the (timeout-sorted) active transfers list.
+ * Callers of this function must hold the flying_transfers_lock.
+ * This function *always* adds the transfer to the flying_transfers list,
+ * it will return non 0 if it fails to update the timer, but even then the
+ * transfer is added to the flying_transfers list. */
+static int add_to_flying_list(struct usbi_transfer *transfer)
+{
+ struct usbi_transfer *cur;
+ struct timeval *timeout = &transfer->timeout;
+ struct libusb_context *ctx = ITRANSFER_CTX(transfer);
+ int r = 0;
+ int first = 1;
+
+ /* if we have no other flying transfers, start the list with this one */
+ if (list_empty(&ctx->flying_transfers)) {
+ list_add(&transfer->list, &ctx->flying_transfers);
+ goto out;
+ }
+
+ /* if we have infinite timeout, append to end of list */
+ if (!timerisset(timeout)) {
+ list_add_tail(&transfer->list, &ctx->flying_transfers);
+ /* first is irrelevant in this case */
+ goto out;
+ }
+
+ /* otherwise, find appropriate place in list */
+ list_for_each_entry(cur, &ctx->flying_transfers, list, struct usbi_transfer) {
+ /* find first timeout that occurs after the transfer in question */
+ struct timeval *cur_tv = &cur->timeout;
+
+ if (!timerisset(cur_tv) || (cur_tv->tv_sec > timeout->tv_sec) ||
+ (cur_tv->tv_sec == timeout->tv_sec &&
+ cur_tv->tv_usec > timeout->tv_usec)) {
+ list_add_tail(&transfer->list, &cur->list);
+ goto out;
+ }
+ first = 0;
+ }
+ /* first is 0 at this stage (list not empty) */
+
+ /* otherwise we need to be inserted at the end */
+ list_add_tail(&transfer->list, &ctx->flying_transfers);
+out:
+#ifdef USBI_TIMERFD_AVAILABLE
+ if (first && usbi_using_timerfd(ctx) && timerisset(timeout)) {
+ /* if this transfer has the lowest timeout of all active transfers,
+ * rearm the timerfd with this transfer's timeout */
+ const struct itimerspec it = { {0, 0},
+ { timeout->tv_sec, timeout->tv_usec * 1000 } };
+ usbi_dbg("arm timerfd for timeout in %dms (first in line)",
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout);
+ r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL);
+ if (r < 0) {
+ usbi_warn(ctx, "failed to arm first timerfd (errno %d)", errno);
+ r = LIBUSB_ERROR_OTHER;
+ }
+ }
+#else
+ UNUSED(first);
+#endif
+
+ return r;
+}
+
+/** \ingroup asyncio
+ * Allocate a libusb transfer with a specified number of isochronous packet
+ * descriptors. The returned transfer is pre-initialized for you. When the new
+ * transfer is no longer needed, it should be freed with
+ * libusb_free_transfer().
+ *
+ * Transfers intended for non-isochronous endpoints (e.g. control, bulk,
+ * interrupt) should specify an iso_packets count of zero.
+ *
+ * For transfers intended for isochronous endpoints, specify an appropriate
+ * number of packet descriptors to be allocated as part of the transfer.
+ * The returned transfer is not specially initialized for isochronous I/O;
+ * you are still required to set the
+ * \ref libusb_transfer::num_iso_packets "num_iso_packets" and
+ * \ref libusb_transfer::type "type" fields accordingly.
+ *
+ * It is safe to allocate a transfer with some isochronous packets and then
+ * use it on a non-isochronous endpoint. If you do this, ensure that at time
+ * of submission, num_iso_packets is 0 and that type is set appropriately.
+ *
+ * \param iso_packets number of isochronous packet descriptors to allocate
+ * \returns a newly allocated transfer, or NULL on error
+ */
+DEFAULT_VISIBILITY
+struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(
+ int iso_packets)
+{
+ size_t os_alloc_size = usbi_backend->transfer_priv_size
+ + (usbi_backend->add_iso_packet_size * iso_packets);
+ size_t alloc_size = sizeof(struct usbi_transfer)
+ + sizeof(struct libusb_transfer)
+ + (sizeof(struct libusb_iso_packet_descriptor) * iso_packets)
+ + os_alloc_size;
+ struct usbi_transfer *itransfer = calloc(1, alloc_size);
+ if (!itransfer)
+ return NULL;
+
+ itransfer->num_iso_packets = iso_packets;
+ usbi_mutex_init(&itransfer->lock, NULL);
+ return USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+}
+
+/** \ingroup asyncio
+ * Free a transfer structure. This should be called for all transfers
+ * allocated with libusb_alloc_transfer().
+ *
+ * If the \ref libusb_transfer_flags::LIBUSB_TRANSFER_FREE_BUFFER
+ * "LIBUSB_TRANSFER_FREE_BUFFER" flag is set and the transfer buffer is
+ * non-NULL, this function will also free the transfer buffer using the
+ * standard system memory allocator (e.g. free()).
+ *
+ * It is legal to call this function with a NULL transfer. In this case,
+ * the function will simply return safely.
+ *
+ * It is not legal to free an active transfer (one which has been submitted
+ * and has not yet completed).
+ *
+ * \param transfer the transfer to free
+ */
+void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer)
+{
+ struct usbi_transfer *itransfer;
+ if (!transfer)
+ return;
+
+ if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER && transfer->buffer)
+ free(transfer->buffer);
+
+ itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+ usbi_mutex_destroy(&itransfer->lock);
+ free(itransfer);
+}
+
+#ifdef USBI_TIMERFD_AVAILABLE
+static int disarm_timerfd(struct libusb_context *ctx)
+{
+ const struct itimerspec disarm_timer = { { 0, 0 }, { 0, 0 } };
+ int r;
+
+ usbi_dbg("");
+ r = timerfd_settime(ctx->timerfd, 0, &disarm_timer, NULL);
+ if (r < 0)
+ return LIBUSB_ERROR_OTHER;
+ else
+ return 0;
+}
+
+/* iterates through the flying transfers, and rearms the timerfd based on the
+ * next upcoming timeout.
+ * must be called with flying_list locked.
+ * returns 0 if there was no timeout to arm, 1 if the next timeout was armed,
+ * or a LIBUSB_ERROR code on failure.
+ */
+static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
+{
+ struct usbi_transfer *transfer;
+
+ list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
+ struct timeval *cur_tv = &transfer->timeout;
+
+ /* if we've reached transfers of infinite timeout, then we have no
+ * arming to do */
+ if (!timerisset(cur_tv))
+ goto disarm;
+
+ /* act on first transfer that is not already cancelled */
+ if (!(transfer->flags & USBI_TRANSFER_TIMED_OUT)) {
+ int r;
+ const struct itimerspec it = { {0, 0},
+ { cur_tv->tv_sec, cur_tv->tv_usec * 1000 } };
+ usbi_dbg("next timeout originally %dms", USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout);
+ r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL);
+ if (r < 0)
+ return LIBUSB_ERROR_OTHER;
+ return 1;
+ }
+ }
+
+disarm:
+ return disarm_timerfd(ctx);
+}
+#else
+static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
+{
+ (void)ctx;
+ return 0;
+}
+#endif
+
+/** \ingroup asyncio
+ * Submit a transfer. This function will fire off the USB transfer and then
+ * return immediately.
+ *
+ * \param transfer the transfer to submit
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_BUSY if the transfer has already been submitted.
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported
+ * by the operating system.
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer)
+{
+ struct libusb_context *ctx = TRANSFER_CTX(transfer);
+ struct usbi_transfer *itransfer =
+ LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+ int r;
+ int updated_fds;
+
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+ usbi_mutex_lock(&itransfer->lock);
+ itransfer->transferred = 0;
+ itransfer->flags = 0;
+ r = calculate_timeout(itransfer);
+ if (r < 0) {
+ r = LIBUSB_ERROR_OTHER;
+ goto out;
+ }
+
+ r = add_to_flying_list(itransfer);
+ if (r == LIBUSB_SUCCESS) {
+ r = usbi_backend->submit_transfer(itransfer);
+ }
+ if (r != LIBUSB_SUCCESS) {
+ list_del(&itransfer->list);
+ arm_timerfd_for_next_timeout(ctx);
+ } else {
+ /* keep a reference to this device */
+ libusb_ref_device(transfer->dev_handle->dev);
+ }
+out:
+ updated_fds = (itransfer->flags & USBI_TRANSFER_UPDATED_FDS);
+ usbi_mutex_unlock(&itransfer->lock);
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+ if (updated_fds)
+ usbi_fd_notification(ctx);
+ return r;
+}
+
+/** \ingroup asyncio
+ * Asynchronously cancel a previously submitted transfer.
+ * This function returns immediately, but this does not indicate cancellation
+ * is complete. Your callback function will be invoked at some later time
+ * with a transfer status of
+ * \ref libusb_transfer_status::LIBUSB_TRANSFER_CANCELLED
+ * "LIBUSB_TRANSFER_CANCELLED."
+ *
+ * \param transfer the transfer to cancel
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the transfer is already complete or
+ * cancelled.
+ * \returns a LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer)
+{
+ struct usbi_transfer *itransfer =
+ LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+ int r;
+
+ usbi_dbg("");
+ usbi_mutex_lock(&itransfer->lock);
+ r = usbi_backend->cancel_transfer(itransfer);
+ if (r < 0) {
+ if (r != LIBUSB_ERROR_NOT_FOUND &&
+ r != LIBUSB_ERROR_NO_DEVICE)
+ usbi_err(TRANSFER_CTX(transfer),
+ "cancel transfer failed error %d", r);
+ else
+ usbi_dbg("cancel transfer failed error %d", r);
+
+ if (r == LIBUSB_ERROR_NO_DEVICE)
+ itransfer->flags |= USBI_TRANSFER_DEVICE_DISAPPEARED;
+ }
+
+ itransfer->flags |= USBI_TRANSFER_CANCELLING;
+
+ usbi_mutex_unlock(&itransfer->lock);
+ return r;
+}
+
+/** \ingroup asyncio
+ * Set a transfers bulk stream id. Note users are advised to use
+ * libusb_fill_bulk_stream_transfer() instead of calling this function
+ * directly.
+ *
+ * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103
+ *
+ * \param transfer the transfer to set the stream id for
+ * \param stream_id the stream id to set
+ * \see libusb_alloc_streams()
+ */
+void API_EXPORTED libusb_transfer_set_stream_id(
+ struct libusb_transfer *transfer, uint32_t stream_id)
+{
+ struct usbi_transfer *itransfer =
+ LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+
+ itransfer->stream_id = stream_id;
+}
+
+/** \ingroup asyncio
+ * Get a transfers bulk stream id.
+ *
+ * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103
+ *
+ * \param transfer the transfer to get the stream id for
+ * \returns the stream id for the transfer
+ */
+uint32_t API_EXPORTED libusb_transfer_get_stream_id(
+ struct libusb_transfer *transfer)
+{
+ struct usbi_transfer *itransfer =
+ LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+
+ return itransfer->stream_id;
+}
+
+/* Handle completion of a transfer (completion might be an error condition).
+ * This will invoke the user-supplied callback function, which may end up
+ * freeing the transfer. Therefore you cannot use the transfer structure
+ * after calling this function, and you should free all backend-specific
+ * data before calling it.
+ * Do not call this function with the usbi_transfer lock held. User-specified
+ * callback functions may attempt to directly resubmit the transfer, which
+ * will attempt to take the lock. */
+int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
+ enum libusb_transfer_status status)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = TRANSFER_CTX(transfer);
+ struct libusb_device_handle *handle = transfer->dev_handle;
+ uint8_t flags;
+ int r = 0;
+
+ /* FIXME: could be more intelligent with the timerfd here. we don't need
+ * to disarm the timerfd if there was no timer running, and we only need
+ * to rearm the timerfd if the transfer that expired was the one with
+ * the shortest timeout. */
+
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+ list_del(&itransfer->list);
+ if (usbi_using_timerfd(ctx))
+ r = arm_timerfd_for_next_timeout(ctx);
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+ if (usbi_using_timerfd(ctx) && (r < 0))
+ return r;
+
+ if (status == LIBUSB_TRANSFER_COMPLETED
+ && transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) {
+ int rqlen = transfer->length;
+ if (transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL)
+ rqlen -= LIBUSB_CONTROL_SETUP_SIZE;
+ if (rqlen != itransfer->transferred) {
+ usbi_dbg("interpreting short transfer as error");
+ status = LIBUSB_TRANSFER_ERROR;
+ }
+ }
+
+ flags = transfer->flags;
+ transfer->status = status;
+ transfer->actual_length = itransfer->transferred;
+ usbi_dbg("transfer %p has callback %p", transfer, transfer->callback);
+ if (transfer->callback)
+ transfer->callback(transfer);
+ /* transfer might have been freed by the above call, do not use from
+ * this point. */
+ if (flags & LIBUSB_TRANSFER_FREE_TRANSFER)
+ libusb_free_transfer(transfer);
+ usbi_mutex_lock(&ctx->event_waiters_lock);
+ usbi_cond_broadcast(&ctx->event_waiters_cond);
+ usbi_mutex_unlock(&ctx->event_waiters_lock);
+ libusb_unref_device(handle->dev);
+ return 0;
+}
+
+/* Similar to usbi_handle_transfer_completion() but exclusively for transfers
+ * that were asynchronously cancelled. The same concerns w.r.t. freeing of
+ * transfers exist here.
+ * Do not call this function with the usbi_transfer lock held. User-specified
+ * callback functions may attempt to directly resubmit the transfer, which
+ * will attempt to take the lock. */
+int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer)
+{
+ /* if the URB was cancelled due to timeout, report timeout to the user */
+ if (transfer->flags & USBI_TRANSFER_TIMED_OUT) {
+ usbi_dbg("detected timeout cancellation");
+ return usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_TIMED_OUT);
+ }
+
+ /* otherwise its a normal async cancel */
+ return usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_CANCELLED);
+}
+
+/** \ingroup poll
+ * Attempt to acquire the event handling lock. This lock is used to ensure that
+ * only one thread is monitoring libusb event sources at any one time.
+ *
+ * You only need to use this lock if you are developing an application
+ * which calls poll() or select() on libusb's file descriptors directly.
+ * If you stick to libusb's event handling loop functions (e.g.
+ * libusb_handle_events()) then you do not need to be concerned with this
+ * locking.
+ *
+ * While holding this lock, you are trusted to actually be handling events.
+ * If you are no longer handling events, you must call libusb_unlock_events()
+ * as soon as possible.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \returns 0 if the lock was obtained successfully
+ * \returns 1 if the lock was not obtained (i.e. another thread holds the lock)
+ * \ref mtasync
+ */
+int API_EXPORTED libusb_try_lock_events(libusb_context *ctx)
+{
+ int r;
+ unsigned int ru;
+ USBI_GET_CONTEXT(ctx);
+
+ /* is someone else waiting to modify poll fds? if so, don't let this thread
+ * start event handling */
+ usbi_mutex_lock(&ctx->pollfd_modify_lock);
+ ru = ctx->pollfd_modify;
+ usbi_mutex_unlock(&ctx->pollfd_modify_lock);
+ if (ru) {
+ usbi_dbg("someone else is modifying poll fds");
+ return 1;
+ }
+
+ r = usbi_mutex_trylock(&ctx->events_lock);
+ if (r)
+ return 1;
+
+ ctx->event_handler_active = 1;
+ return 0;
+}
+
+/** \ingroup poll
+ * Acquire the event handling lock, blocking until successful acquisition if
+ * it is contended. This lock is used to ensure that only one thread is
+ * monitoring libusb event sources at any one time.
+ *
+ * You only need to use this lock if you are developing an application
+ * which calls poll() or select() on libusb's file descriptors directly.
+ * If you stick to libusb's event handling loop functions (e.g.
+ * libusb_handle_events()) then you do not need to be concerned with this
+ * locking.
+ *
+ * While holding this lock, you are trusted to actually be handling events.
+ * If you are no longer handling events, you must call libusb_unlock_events()
+ * as soon as possible.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \ref mtasync
+ */
+void API_EXPORTED libusb_lock_events(libusb_context *ctx)
+{
+ USBI_GET_CONTEXT(ctx);
+ usbi_mutex_lock(&ctx->events_lock);
+ ctx->event_handler_active = 1;
+}
+
+/** \ingroup poll
+ * Release the lock previously acquired with libusb_try_lock_events() or
+ * libusb_lock_events(). Releasing this lock will wake up any threads blocked
+ * on libusb_wait_for_event().
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \ref mtasync
+ */
+void API_EXPORTED libusb_unlock_events(libusb_context *ctx)
+{
+ USBI_GET_CONTEXT(ctx);
+ ctx->event_handler_active = 0;
+ usbi_mutex_unlock(&ctx->events_lock);
+
+ /* FIXME: perhaps we should be a bit more efficient by not broadcasting
+ * the availability of the events lock when we are modifying pollfds
+ * (check ctx->pollfd_modify)? */
+ usbi_mutex_lock(&ctx->event_waiters_lock);
+ usbi_cond_broadcast(&ctx->event_waiters_cond);
+ usbi_mutex_unlock(&ctx->event_waiters_lock);
+}
+
+/** \ingroup poll
+ * Determine if it is still OK for this thread to be doing event handling.
+ *
+ * Sometimes, libusb needs to temporarily pause all event handlers, and this
+ * is the function you should use before polling file descriptors to see if
+ * this is the case.
+ *
+ * If this function instructs your thread to give up the events lock, you
+ * should just continue the usual logic that is documented in \ref mtasync.
+ * On the next iteration, your thread will fail to obtain the events lock,
+ * and will hence become an event waiter.
+ *
+ * This function should be called while the events lock is held: you don't
+ * need to worry about the results of this function if your thread is not
+ * the current event handler.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \returns 1 if event handling can start or continue
+ * \returns 0 if this thread must give up the events lock
+ * \ref fullstory "Multi-threaded I/O: the full story"
+ */
+int API_EXPORTED libusb_event_handling_ok(libusb_context *ctx)
+{
+ unsigned int r;
+ USBI_GET_CONTEXT(ctx);
+
+ /* is someone else waiting to modify poll fds? if so, don't let this thread
+ * continue event handling */
+ usbi_mutex_lock(&ctx->pollfd_modify_lock);
+ r = ctx->pollfd_modify;
+ usbi_mutex_unlock(&ctx->pollfd_modify_lock);
+ if (r) {
+ usbi_dbg("someone else is modifying poll fds");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/** \ingroup poll
+ * Determine if an active thread is handling events (i.e. if anyone is holding
+ * the event handling lock).
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \returns 1 if a thread is handling events
+ * \returns 0 if there are no threads currently handling events
+ * \ref mtasync
+ */
+int API_EXPORTED libusb_event_handler_active(libusb_context *ctx)
+{
+ unsigned int r;
+ USBI_GET_CONTEXT(ctx);
+
+ /* is someone else waiting to modify poll fds? if so, don't let this thread
+ * start event handling -- indicate that event handling is happening */
+ usbi_mutex_lock(&ctx->pollfd_modify_lock);
+ r = ctx->pollfd_modify;
+ usbi_mutex_unlock(&ctx->pollfd_modify_lock);
+ if (r) {
+ usbi_dbg("someone else is modifying poll fds");
+ return 1;
+ }
+
+ return ctx->event_handler_active;
+}
+
+/** \ingroup poll
+ * Acquire the event waiters lock. This lock is designed to be obtained under
+ * the situation where you want to be aware when events are completed, but
+ * some other thread is event handling so calling libusb_handle_events() is not
+ * allowed.
+ *
+ * You then obtain this lock, re-check that another thread is still handling
+ * events, then call libusb_wait_for_event().
+ *
+ * You only need to use this lock if you are developing an application
+ * which calls poll() or select() on libusb's file descriptors directly,
+ * and may potentially be handling events from 2 threads simultaenously.
+ * If you stick to libusb's event handling loop functions (e.g.
+ * libusb_handle_events()) then you do not need to be concerned with this
+ * locking.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \ref mtasync
+ */
+void API_EXPORTED libusb_lock_event_waiters(libusb_context *ctx)
+{
+ USBI_GET_CONTEXT(ctx);
+ usbi_mutex_lock(&ctx->event_waiters_lock);
+}
+
+/** \ingroup poll
+ * Release the event waiters lock.
+ * \param ctx the context to operate on, or NULL for the default context
+ * \ref mtasync
+ */
+void API_EXPORTED libusb_unlock_event_waiters(libusb_context *ctx)
+{
+ USBI_GET_CONTEXT(ctx);
+ usbi_mutex_unlock(&ctx->event_waiters_lock);
+}
+
+/** \ingroup poll
+ * Wait for another thread to signal completion of an event. Must be called
+ * with the event waiters lock held, see libusb_lock_event_waiters().
+ *
+ * This function will block until any of the following conditions are met:
+ * -# The timeout expires
+ * -# A transfer completes
+ * -# A thread releases the event handling lock through libusb_unlock_events()
+ *
+ * Condition 1 is obvious. Condition 2 unblocks your thread after
+ * the callback for the transfer has completed. Condition 3 is important
+ * because it means that the thread that was previously handling events is no
+ * longer doing so, so if any events are to complete, another thread needs to
+ * step up and start event handling.
+ *
+ * This function releases the event waiters lock before putting your thread
+ * to sleep, and reacquires the lock as it is being woken up.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param tv maximum timeout for this blocking function. A NULL value
+ * indicates unlimited timeout.
+ * \returns 0 after a transfer completes or another thread stops event handling
+ * \returns 1 if the timeout expired
+ * \ref mtasync
+ */
+int API_EXPORTED libusb_wait_for_event(libusb_context *ctx, struct timeval *tv)
+{
+ struct timespec timeout;
+ int r;
+
+ USBI_GET_CONTEXT(ctx);
+ if (tv == NULL) {
+ usbi_cond_wait(&ctx->event_waiters_cond, &ctx->event_waiters_lock);
+ return 0;
+ }
+
+ r = usbi_backend->clock_gettime(USBI_CLOCK_REALTIME, &timeout);
+ if (r < 0) {
+ usbi_err(ctx, "failed to read realtime clock, error %d", errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ timeout.tv_sec += tv->tv_sec;
+ timeout.tv_nsec += tv->tv_usec * 1000;
+ while (timeout.tv_nsec >= 1000000000) {
+ timeout.tv_nsec -= 1000000000;
+ timeout.tv_sec++;
+ }
+
+ r = usbi_cond_timedwait(&ctx->event_waiters_cond,
+ &ctx->event_waiters_lock, &timeout);
+ return (r == ETIMEDOUT);
+}
+
+static void handle_timeout(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ int r;
+
+ itransfer->flags |= USBI_TRANSFER_TIMED_OUT;
+ r = libusb_cancel_transfer(transfer);
+ if (r < 0)
+ usbi_warn(TRANSFER_CTX(transfer),
+ "async cancel failed %d errno=%d", r, errno);
+}
+
+static int handle_timeouts_locked(struct libusb_context *ctx)
+{
+ int r;
+ struct timespec systime_ts;
+ struct timeval systime;
+ struct usbi_transfer *transfer;
+
+ if (list_empty(&ctx->flying_transfers))
+ return 0;
+
+ /* get current time */
+ r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, &systime_ts);
+ if (r < 0)
+ return r;
+
+ TIMESPEC_TO_TIMEVAL(&systime, &systime_ts);
+
+ /* iterate through flying transfers list, finding all transfers that
+ * have expired timeouts */
+ list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
+ struct timeval *cur_tv = &transfer->timeout;
+
+ /* if we've reached transfers of infinite timeout, we're all done */
+ if (!timerisset(cur_tv))
+ return 0;
+
+ /* ignore timeouts we've already handled */
+ if (transfer->flags & (USBI_TRANSFER_TIMED_OUT | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
+ continue;
+
+ /* if transfer has non-expired timeout, nothing more to do */
+ if ((cur_tv->tv_sec > systime.tv_sec) ||
+ (cur_tv->tv_sec == systime.tv_sec &&
+ cur_tv->tv_usec > systime.tv_usec))
+ return 0;
+
+ /* otherwise, we've got an expired timeout to handle */
+ handle_timeout(transfer);
+ }
+ return 0;
+}
+
+static int handle_timeouts(struct libusb_context *ctx)
+{
+ int r;
+ USBI_GET_CONTEXT(ctx);
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+ r = handle_timeouts_locked(ctx);
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+ return r;
+}
+
+#ifdef USBI_TIMERFD_AVAILABLE
+static int handle_timerfd_trigger(struct libusb_context *ctx)
+{
+ int r;
+
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+
+ /* process the timeout that just happened */
+ r = handle_timeouts_locked(ctx);
+ if (r < 0)
+ goto out;
+
+ /* arm for next timeout*/
+ r = arm_timerfd_for_next_timeout(ctx);
+
+out:
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+ return r;
+}
+#endif
+
+/* do the actual event handling. assumes that no other thread is concurrently
+ * doing the same thing. */
+static int handle_events(struct libusb_context *ctx, struct timeval *tv)
+{
+ int r;
+ struct usbi_pollfd *ipollfd;
+ POLL_NFDS_TYPE nfds = 0;
+ struct pollfd *fds = NULL;
+ int i = -1;
+ int timeout_ms;
+ int special_event;
+
+ usbi_mutex_lock(&ctx->pollfds_lock);
+ list_for_each_entry(ipollfd, &ctx->pollfds, list, struct usbi_pollfd)
+ nfds++;
+
+ /* TODO: malloc when number of fd's changes, not on every poll */
+ if (nfds != 0)
+ fds = malloc(sizeof(*fds) * nfds);
+ if (!fds) {
+ usbi_mutex_unlock(&ctx->pollfds_lock);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ list_for_each_entry(ipollfd, &ctx->pollfds, list, struct usbi_pollfd) {
+ struct libusb_pollfd *pollfd = &ipollfd->pollfd;
+ int fd = pollfd->fd;
+ i++;
+ fds[i].fd = fd;
+ fds[i].events = pollfd->events;
+ fds[i].revents = 0;
+ }
+ usbi_mutex_unlock(&ctx->pollfds_lock);
+
+ timeout_ms = (int)(tv->tv_sec * 1000) + (tv->tv_usec / 1000);
+
+ /* round up to next millisecond */
+ if (tv->tv_usec % 1000)
+ timeout_ms++;
+
+redo_poll:
+ usbi_dbg("poll() %d fds with timeout in %dms", nfds, timeout_ms);
+ r = usbi_poll(fds, nfds, timeout_ms);
+ usbi_dbg("poll() returned %d", r);
+ if (r == 0) {
+ free(fds);
+ return handle_timeouts(ctx);
+ } else if (r == -1 && errno == EINTR) {
+ free(fds);
+ return LIBUSB_ERROR_INTERRUPTED;
+ } else if (r < 0) {
+ free(fds);
+ usbi_err(ctx, "poll failed %d err=%d\n", r, errno);
+ return LIBUSB_ERROR_IO;
+ }
+
+ special_event = 0;
+
+ /* fd[0] is always the ctrl pipe */
+ if (fds[0].revents) {
+ /* another thread wanted to interrupt event handling, and it succeeded!
+ * handle any other events that cropped up at the same time, and
+ * simply return */
+ usbi_dbg("caught a fish on the control pipe");
+
+ if (r == 1) {
+ r = 0;
+ goto handled;
+ } else {
+ /* prevent OS backend from trying to handle events on ctrl pipe */
+ fds[0].revents = 0;
+ r--;
+ }
+ }
+
+ /* fd[1] is always the hotplug pipe */
+ if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && fds[1].revents) {
+ libusb_hotplug_message message;
+ ssize_t ret;
+
+ usbi_dbg("caught a fish on the hotplug pipe");
+ special_event = 1;
+
+ /* read the message from the hotplug thread */
+ ret = usbi_read(ctx->hotplug_pipe[0], &message, sizeof (message));
+ if (ret != sizeof(message)) {
+ usbi_err(ctx, "hotplug pipe read error %d != %u",
+ ret, sizeof(message));
+ r = LIBUSB_ERROR_OTHER;
+ goto handled;
+ }
+
+ usbi_hotplug_match(ctx, message.device, message.event);
+
+ /* the device left. dereference the device */
+ if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == message.event)
+ libusb_unref_device(message.device);
+
+ fds[1].revents = 0;
+ if (1 == r--)
+ goto handled;
+ } /* else there shouldn't be anything on this pipe */
+
+#ifdef USBI_TIMERFD_AVAILABLE
+ /* on timerfd configurations, fds[2] is the timerfd */
+ if (usbi_using_timerfd(ctx) && fds[2].revents) {
+ /* timerfd indicates that a timeout has expired */
+ int ret;
+ usbi_dbg("timerfd triggered");
+ special_event = 1;
+
+ ret = handle_timerfd_trigger(ctx);
+ if (ret < 0) {
+ /* return error code */
+ r = ret;
+ goto handled;
+ } else if (r == 1) {
+ /* no more active file descriptors, nothing more to do */
+ r = 0;
+ goto handled;
+ } else {
+ /* more events pending...
+ * prevent OS backend from trying to handle events on timerfd */
+ fds[2].revents = 0;
+ r--;
+ }
+ }
+#endif
+
+ r = usbi_backend->handle_events(ctx, fds, nfds, r);
+ if (r)
+ usbi_err(ctx, "backend handle_events failed with error %d", r);
+
+handled:
+ if (r == 0 && special_event) {
+ timeout_ms = 0;
+ goto redo_poll;
+ }
+
+ free(fds);
+ return r;
+}
+
+/* returns the smallest of:
+ * 1. timeout of next URB
+ * 2. user-supplied timeout
+ * returns 1 if there is an already-expired timeout, otherwise returns 0
+ * and populates out
+ */
+static int get_next_timeout(libusb_context *ctx, struct timeval *tv,
+ struct timeval *out)
+{
+ struct timeval timeout;
+ int r = libusb_get_next_timeout(ctx, &timeout);
+ if (r) {
+ /* timeout already expired? */
+ if (!timerisset(&timeout))
+ return 1;
+
+ /* choose the smallest of next URB timeout or user specified timeout */
+ if (timercmp(&timeout, tv, <))
+ *out = timeout;
+ else
+ *out = *tv;
+ } else {
+ *out = *tv;
+ }
+ return 0;
+}
+
+/** \ingroup poll
+ * Handle any pending events.
+ *
+ * libusb determines "pending events" by checking if any timeouts have expired
+ * and by checking the set of file descriptors for activity.
+ *
+ * If a zero timeval is passed, this function will handle any already-pending
+ * events and then immediately return in non-blocking style.
+ *
+ * If a non-zero timeval is passed and no events are currently pending, this
+ * function will block waiting for events to handle up until the specified
+ * timeout. If an event arrives or a signal is raised, this function will
+ * return early.
+ *
+ * If the parameter completed is not NULL then after obtaining the event
+ * handling lock this function will return immediately if the integer
+ * pointed to is not 0. This allows for race free waiting for the completion
+ * of a specific transfer.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param tv the maximum time to block waiting for events, or an all zero
+ * timeval struct for non-blocking mode
+ * \param completed pointer to completion integer to check, or NULL
+ * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ * \ref mtasync
+ */
+int API_EXPORTED libusb_handle_events_timeout_completed(libusb_context *ctx,
+ struct timeval *tv, int *completed)
+{
+ int r;
+ struct timeval poll_timeout;
+
+ USBI_GET_CONTEXT(ctx);
+ r = get_next_timeout(ctx, tv, &poll_timeout);
+ if (r) {
+ /* timeout already expired */
+ return handle_timeouts(ctx);
+ }
+
+retry:
+ if (libusb_try_lock_events(ctx) == 0) {
+ if (completed == NULL || !*completed) {
+ /* we obtained the event lock: do our own event handling */
+ usbi_dbg("doing our own event handling");
+ r = handle_events(ctx, &poll_timeout);
+ }
+ libusb_unlock_events(ctx);
+ return r;
+ }
+
+ /* another thread is doing event handling. wait for thread events that
+ * notify event completion. */
+ libusb_lock_event_waiters(ctx);
+
+ if (completed && *completed)
+ goto already_done;
+
+ if (!libusb_event_handler_active(ctx)) {
+ /* we hit a race: whoever was event handling earlier finished in the
+ * time it took us to reach this point. try the cycle again. */
+ libusb_unlock_event_waiters(ctx);
+ usbi_dbg("event handler was active but went away, retrying");
+ goto retry;
+ }
+
+ usbi_dbg("another thread is doing event handling");
+ r = libusb_wait_for_event(ctx, &poll_timeout);
+
+already_done:
+ libusb_unlock_event_waiters(ctx);
+
+ if (r < 0)
+ return r;
+ else if (r == 1)
+ return handle_timeouts(ctx);
+ else
+ return 0;
+}
+
+/** \ingroup poll
+ * Handle any pending events
+ *
+ * Like libusb_handle_events_timeout_completed(), but without the completed
+ * parameter, calling this function is equivalent to calling
+ * libusb_handle_events_timeout_completed() with a NULL completed parameter.
+ *
+ * This function is kept primarily for backwards compatibility.
+ * All new code should call libusb_handle_events_completed() or
+ * libusb_handle_events_timeout_completed() to avoid race conditions.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param tv the maximum time to block waiting for events, or an all zero
+ * timeval struct for non-blocking mode
+ * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_handle_events_timeout(libusb_context *ctx,
+ struct timeval *tv)
+{
+ return libusb_handle_events_timeout_completed(ctx, tv, NULL);
+}
+
+/** \ingroup poll
+ * Handle any pending events in blocking mode. There is currently a timeout
+ * hardcoded at 60 seconds but we plan to make it unlimited in future. For
+ * finer control over whether this function is blocking or non-blocking, or
+ * for control over the timeout, use libusb_handle_events_timeout_completed()
+ * instead.
+ *
+ * This function is kept primarily for backwards compatibility.
+ * All new code should call libusb_handle_events_completed() or
+ * libusb_handle_events_timeout_completed() to avoid race conditions.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_handle_events(libusb_context *ctx)
+{
+ struct timeval tv;
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+ return libusb_handle_events_timeout_completed(ctx, &tv, NULL);
+}
+
+/** \ingroup poll
+ * Handle any pending events in blocking mode.
+ *
+ * Like libusb_handle_events(), with the addition of a completed parameter
+ * to allow for race free waiting for the completion of a specific transfer.
+ *
+ * See libusb_handle_events_timeout_completed() for details on the completed
+ * parameter.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param completed pointer to completion integer to check, or NULL
+ * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ * \ref mtasync
+ */
+int API_EXPORTED libusb_handle_events_completed(libusb_context *ctx,
+ int *completed)
+{
+ struct timeval tv;
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+ return libusb_handle_events_timeout_completed(ctx, &tv, completed);
+}
+
+/** \ingroup poll
+ * Handle any pending events by polling file descriptors, without checking if
+ * any other threads are already doing so. Must be called with the event lock
+ * held, see libusb_lock_events().
+ *
+ * This function is designed to be called under the situation where you have
+ * taken the event lock and are calling poll()/select() directly on libusb's
+ * file descriptors (as opposed to using libusb_handle_events() or similar).
+ * You detect events on libusb's descriptors, so you then call this function
+ * with a zero timeout value (while still holding the event lock).
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param tv the maximum time to block waiting for events, or zero for
+ * non-blocking mode
+ * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ * \ref mtasync
+ */
+int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx,
+ struct timeval *tv)
+{
+ int r;
+ struct timeval poll_timeout;
+
+ USBI_GET_CONTEXT(ctx);
+ r = get_next_timeout(ctx, tv, &poll_timeout);
+ if (r) {
+ /* timeout already expired */
+ return handle_timeouts(ctx);
+ }
+
+ return handle_events(ctx, &poll_timeout);
+}
+
+/** \ingroup poll
+ * Determines whether your application must apply special timing considerations
+ * when monitoring libusb's file descriptors.
+ *
+ * This function is only useful for applications which retrieve and poll
+ * libusb's file descriptors in their own main loop (\ref pollmain).
+ *
+ * Ordinarily, libusb's event handler needs to be called into at specific
+ * moments in time (in addition to times when there is activity on the file
+ * descriptor set). The usual approach is to use libusb_get_next_timeout()
+ * to learn about when the next timeout occurs, and to adjust your
+ * poll()/select() timeout accordingly so that you can make a call into the
+ * library at that time.
+ *
+ * Some platforms supported by libusb do not come with this baggage - any
+ * events relevant to timing will be represented by activity on the file
+ * descriptor set, and libusb_get_next_timeout() will always return 0.
+ * This function allows you to detect whether you are running on such a
+ * platform.
+ *
+ * Since v1.0.5.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \returns 0 if you must call into libusb at times determined by
+ * libusb_get_next_timeout(), or 1 if all timeout events are handled internally
+ * or through regular activity on the file descriptors.
+ * \ref pollmain "Polling libusb file descriptors for event handling"
+ */
+int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx)
+{
+#if defined(USBI_TIMERFD_AVAILABLE)
+ USBI_GET_CONTEXT(ctx);
+ return usbi_using_timerfd(ctx);
+#else
+ (void)ctx;
+ return 0;
+#endif
+}
+
+/** \ingroup poll
+ * Determine the next internal timeout that libusb needs to handle. You only
+ * need to use this function if you are calling poll() or select() or similar
+ * on libusb's file descriptors yourself - you do not need to use it if you
+ * are calling libusb_handle_events() or a variant directly.
+ *
+ * You should call this function in your main loop in order to determine how
+ * long to wait for select() or poll() to return results. libusb needs to be
+ * called into at this timeout, so you should use it as an upper bound on
+ * your select() or poll() call.
+ *
+ * When the timeout has expired, call into libusb_handle_events_timeout()
+ * (perhaps in non-blocking mode) so that libusb can handle the timeout.
+ *
+ * This function may return 1 (success) and an all-zero timeval. If this is
+ * the case, it indicates that libusb has a timeout that has already expired
+ * so you should call libusb_handle_events_timeout() or similar immediately.
+ * A return code of 0 indicates that there are no pending timeouts.
+ *
+ * On some platforms, this function will always returns 0 (no pending
+ * timeouts). See \ref polltime.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param tv output location for a relative time against the current
+ * clock in which libusb must be called into in order to process timeout events
+ * \returns 0 if there are no pending timeouts, 1 if a timeout was returned,
+ * or LIBUSB_ERROR_OTHER on failure
+ */
+int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
+ struct timeval *tv)
+{
+ struct usbi_transfer *transfer;
+ struct timespec cur_ts;
+ struct timeval cur_tv;
+ struct timeval *next_timeout;
+ int r;
+ int found = 0;
+
+ USBI_GET_CONTEXT(ctx);
+ if (usbi_using_timerfd(ctx))
+ return 0;
+
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+ if (list_empty(&ctx->flying_transfers)) {
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+ usbi_dbg("no URBs, no timeout!");
+ return 0;
+ }
+
+ /* find next transfer which hasn't already been processed as timed out */
+ list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
+ if (transfer->flags & (USBI_TRANSFER_TIMED_OUT | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
+ continue;
+
+ /* no timeout for this transfer? */
+ if (!timerisset(&transfer->timeout))
+ continue;
+
+ found = 1;
+ break;
+ }
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
+ if (!found) {
+ usbi_dbg("no URB with timeout or all handled by OS; no timeout!");
+ return 0;
+ }
+
+ next_timeout = &transfer->timeout;
+
+ r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, &cur_ts);
+ if (r < 0) {
+ usbi_err(ctx, "failed to read monotonic clock, errno=%d", errno);
+ return 0;
+ }
+ TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts);
+
+ if (!timercmp(&cur_tv, next_timeout, <)) {
+ usbi_dbg("first timeout already expired");
+ timerclear(tv);
+ } else {
+ timersub(next_timeout, &cur_tv, tv);
+ usbi_dbg("next timeout in %d.%06ds", tv->tv_sec, tv->tv_usec);
+ }
+
+ return 1;
+}
+
+/** \ingroup poll
+ * Register notification functions for file descriptor additions/removals.
+ * These functions will be invoked for every new or removed file descriptor
+ * that libusb uses as an event source.
+ *
+ * To remove notifiers, pass NULL values for the function pointers.
+ *
+ * Note that file descriptors may have been added even before you register
+ * these notifiers (e.g. at libusb_init() time).
+ *
+ * Additionally, note that the removal notifier may be called during
+ * libusb_exit() (e.g. when it is closing file descriptors that were opened
+ * and added to the poll set at libusb_init() time). If you don't want this,
+ * remove the notifiers immediately before calling libusb_exit().
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param added_cb pointer to function for addition notifications
+ * \param removed_cb pointer to function for removal notifications
+ * \param user_data User data to be passed back to callbacks (useful for
+ * passing context information)
+ */
+void API_EXPORTED libusb_set_pollfd_notifiers(libusb_context *ctx,
+ libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb,
+ void *user_data)
+{
+ USBI_GET_CONTEXT(ctx);
+ ctx->fd_added_cb = added_cb;
+ ctx->fd_removed_cb = removed_cb;
+ ctx->fd_cb_user_data = user_data;
+}
+
+/* Add a file descriptor to the list of file descriptors to be monitored.
+ * events should be specified as a bitmask of events passed to poll(), e.g.
+ * POLLIN and/or POLLOUT. */
+int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events)
+{
+ struct usbi_pollfd *ipollfd = malloc(sizeof(*ipollfd));
+ if (!ipollfd)
+ return LIBUSB_ERROR_NO_MEM;
+
+ usbi_dbg("add fd %d events %d", fd, events);
+ ipollfd->pollfd.fd = fd;
+ ipollfd->pollfd.events = events;
+ usbi_mutex_lock(&ctx->pollfds_lock);
+ list_add_tail(&ipollfd->list, &ctx->pollfds);
+ usbi_mutex_unlock(&ctx->pollfds_lock);
+
+ if (ctx->fd_added_cb)
+ ctx->fd_added_cb(fd, events, ctx->fd_cb_user_data);
+ return 0;
+}
+
+/* Remove a file descriptor from the list of file descriptors to be polled. */
+void usbi_remove_pollfd(struct libusb_context *ctx, int fd)
+{
+ struct usbi_pollfd *ipollfd;
+ int found = 0;
+
+ usbi_dbg("remove fd %d", fd);
+ usbi_mutex_lock(&ctx->pollfds_lock);
+ list_for_each_entry(ipollfd, &ctx->pollfds, list, struct usbi_pollfd)
+ if (ipollfd->pollfd.fd == fd) {
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ usbi_dbg("couldn't find fd %d to remove", fd);
+ usbi_mutex_unlock(&ctx->pollfds_lock);
+ return;
+ }
+
+ list_del(&ipollfd->list);
+ usbi_mutex_unlock(&ctx->pollfds_lock);
+ free(ipollfd);
+ if (ctx->fd_removed_cb)
+ ctx->fd_removed_cb(fd, ctx->fd_cb_user_data);
+}
+
+/** \ingroup poll
+ * Retrieve a list of file descriptors that should be polled by your main loop
+ * as libusb event sources.
+ *
+ * The returned list is NULL-terminated and should be freed with free() when
+ * done. The actual list contents must not be touched.
+ *
+ * As file descriptors are a Unix-specific concept, this function is not
+ * available on Windows and will always return NULL.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \returns a NULL-terminated list of libusb_pollfd structures
+ * \returns NULL on error
+ * \returns NULL on platforms where the functionality is not available
+ */
+DEFAULT_VISIBILITY
+const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds(
+ libusb_context *ctx)
+{
+#ifndef OS_WINDOWS
+ struct libusb_pollfd **ret = NULL;
+ struct usbi_pollfd *ipollfd;
+ size_t i = 0;
+ size_t cnt = 0;
+ USBI_GET_CONTEXT(ctx);
+
+ usbi_mutex_lock(&ctx->pollfds_lock);
+ list_for_each_entry(ipollfd, &ctx->pollfds, list, struct usbi_pollfd)
+ cnt++;
+
+ ret = calloc(cnt + 1, sizeof(struct libusb_pollfd *));
+ if (!ret)
+ goto out;
+
+ list_for_each_entry(ipollfd, &ctx->pollfds, list, struct usbi_pollfd)
+ ret[i++] = (struct libusb_pollfd *) ipollfd;
+ ret[cnt] = NULL;
+
+out:
+ usbi_mutex_unlock(&ctx->pollfds_lock);
+ return (const struct libusb_pollfd **) ret;
+#else
+ usbi_err(ctx, "external polling of libusb's internal descriptors "\
+ "is not yet supported on Windows platforms");
+ return NULL;
+#endif
+}
+
+/* Backends may call this from handle_events to report disconnection of a
+ * device. This function ensures transfers get cancelled appropriately.
+ * Callers of this function must hold the events_lock.
+ */
+void usbi_handle_disconnect(struct libusb_device_handle *handle)
+{
+ struct usbi_transfer *cur;
+ struct usbi_transfer *to_cancel;
+
+ usbi_dbg("device %d.%d",
+ handle->dev->bus_number, handle->dev->device_address);
+
+ /* terminate all pending transfers with the LIBUSB_TRANSFER_NO_DEVICE
+ * status code.
+ *
+ * this is a bit tricky because:
+ * 1. we can't do transfer completion while holding flying_transfers_lock
+ * because the completion handler may try to re-submit the transfer
+ * 2. the transfers list can change underneath us - if we were to build a
+ * list of transfers to complete (while holding lock), the situation
+ * might be different by the time we come to free them
+ *
+ * so we resort to a loop-based approach as below
+ *
+ * This is safe because transfers are only removed from the
+ * flying_transfer list by usbi_handle_transfer_completion and
+ * libusb_close, both of which hold the events_lock while doing so,
+ * so usbi_handle_disconnect cannot be running at the same time.
+ *
+ * Note that libusb_submit_transfer also removes the transfer from
+ * the flying_transfer list on submission failure, but it keeps the
+ * flying_transfer list locked between addition and removal, so
+ * usbi_handle_disconnect never sees such transfers.
+ */
+
+ while (1) {
+ usbi_mutex_lock(&HANDLE_CTX(handle)->flying_transfers_lock);
+ to_cancel = NULL;
+ list_for_each_entry(cur, &HANDLE_CTX(handle)->flying_transfers, list, struct usbi_transfer)
+ if (USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur)->dev_handle == handle) {
+ to_cancel = cur;
+ break;
+ }
+ usbi_mutex_unlock(&HANDLE_CTX(handle)->flying_transfers_lock);
+
+ if (!to_cancel)
+ break;
+
+ usbi_dbg("cancelling transfer %p from disconnect",
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel));
+
+ usbi_backend->clear_transfer_priv(to_cancel);
+ usbi_handle_transfer_completion(to_cancel, LIBUSB_TRANSFER_NO_DEVICE);
+ }
+
+}
diff --git a/e502/libusb-1.0/libusb-1.0/libusb-1.0.def b/e502/libusb-1.0/libusb-1.0/libusb-1.0.def
new file mode 100644
index 0000000..d45cfc5
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/libusb-1.0.def
@@ -0,0 +1,166 @@
+LIBRARY "libusb-1.0.dll"
+EXPORTS
+ libusb_alloc_streams
+ libusb_alloc_streams@16 = libusb_alloc_streams
+ libusb_alloc_transfer
+ libusb_alloc_transfer@4 = libusb_alloc_transfer
+ libusb_attach_kernel_driver
+ libusb_attach_kernel_driver@8 = libusb_attach_kernel_driver
+ libusb_bulk_transfer
+ libusb_bulk_transfer@24 = libusb_bulk_transfer
+ libusb_cancel_transfer
+ libusb_cancel_transfer@4 = libusb_cancel_transfer
+ libusb_claim_interface
+ libusb_claim_interface@8 = libusb_claim_interface
+ libusb_clear_halt
+ libusb_clear_halt@8 = libusb_clear_halt
+ libusb_close
+ libusb_close@4 = libusb_close
+ libusb_control_transfer
+ libusb_control_transfer@32 = libusb_control_transfer
+ libusb_detach_kernel_driver
+ libusb_detach_kernel_driver@8 = libusb_detach_kernel_driver
+ libusb_error_name
+ libusb_error_name@4 = libusb_error_name
+ libusb_event_handler_active
+ libusb_event_handler_active@4 = libusb_event_handler_active
+ libusb_event_handling_ok
+ libusb_event_handling_ok@4 = libusb_event_handling_ok
+ libusb_exit
+ libusb_exit@4 = libusb_exit
+ libusb_free_bos_descriptor
+ libusb_free_bos_descriptor@4 = libusb_free_bos_descriptor
+ libusb_free_config_descriptor
+ libusb_free_config_descriptor@4 = libusb_free_config_descriptor
+ libusb_free_container_id_descriptor
+ libusb_free_container_id_descriptor@4 = libusb_free_container_id_descriptor
+ libusb_free_device_list
+ libusb_free_device_list@8 = libusb_free_device_list
+ libusb_free_ss_endpoint_companion_descriptor
+ libusb_free_ss_endpoint_companion_descriptor@4 = libusb_free_ss_endpoint_companion_descriptor
+ libusb_free_ss_usb_device_capability_descriptor
+ libusb_free_ss_usb_device_capability_descriptor@4 = libusb_free_ss_usb_device_capability_descriptor
+ libusb_free_streams
+ libusb_free_streams@12 = libusb_free_streams
+ libusb_free_transfer
+ libusb_free_transfer@4 = libusb_free_transfer
+ libusb_free_usb_2_0_extension_descriptor
+ libusb_free_usb_2_0_extension_descriptor@4 = libusb_free_usb_2_0_extension_descriptor
+ libusb_get_active_config_descriptor
+ libusb_get_active_config_descriptor@8 = libusb_get_active_config_descriptor
+ libusb_get_bos_descriptor
+ libusb_get_bos_descriptor@8 = libusb_get_bos_descriptor
+ libusb_get_bus_number
+ libusb_get_bus_number@4 = libusb_get_bus_number
+ libusb_get_config_descriptor
+ libusb_get_config_descriptor@12 = libusb_get_config_descriptor
+ libusb_get_config_descriptor_by_value
+ libusb_get_config_descriptor_by_value@12 = libusb_get_config_descriptor_by_value
+ libusb_get_configuration
+ libusb_get_configuration@8 = libusb_get_configuration
+ libusb_get_container_id_descriptor
+ libusb_get_container_id_descriptor@12 = libusb_get_container_id_descriptor
+ libusb_get_device
+ libusb_get_device@4 = libusb_get_device
+ libusb_get_device_address
+ libusb_get_device_address@4 = libusb_get_device_address
+ libusb_get_device_descriptor
+ libusb_get_device_descriptor@8 = libusb_get_device_descriptor
+ libusb_get_device_list
+ libusb_get_device_list@8 = libusb_get_device_list
+ libusb_get_device_speed
+ libusb_get_device_speed@4 = libusb_get_device_speed
+ libusb_get_max_iso_packet_size
+ libusb_get_max_iso_packet_size@8 = libusb_get_max_iso_packet_size
+ libusb_get_max_packet_size
+ libusb_get_max_packet_size@8 = libusb_get_max_packet_size
+ libusb_get_next_timeout
+ libusb_get_next_timeout@8 = libusb_get_next_timeout
+ libusb_get_parent
+ libusb_get_parent@4 = libusb_get_parent
+ libusb_get_pollfds
+ libusb_get_pollfds@4 = libusb_get_pollfds
+ libusb_get_port_number
+ libusb_get_port_number@4 = libusb_get_port_number
+ libusb_get_port_numbers
+ libusb_get_port_numbers@12 = libusb_get_port_numbers
+ libusb_get_port_path
+ libusb_get_port_path@16 = libusb_get_port_path
+ libusb_get_ss_endpoint_companion_descriptor
+ libusb_get_ss_endpoint_companion_descriptor@12 = libusb_get_ss_endpoint_companion_descriptor
+ libusb_get_ss_usb_device_capability_descriptor
+ libusb_get_ss_usb_device_capability_descriptor@12 = libusb_get_ss_usb_device_capability_descriptor
+ libusb_get_string_descriptor_ascii
+ libusb_get_string_descriptor_ascii@16 = libusb_get_string_descriptor_ascii
+ libusb_get_usb_2_0_extension_descriptor
+ libusb_get_usb_2_0_extension_descriptor@12 = libusb_get_usb_2_0_extension_descriptor
+ libusb_get_version
+ libusb_get_version@0 = libusb_get_version
+ libusb_handle_events
+ libusb_handle_events@4 = libusb_handle_events
+ libusb_handle_events_completed
+ libusb_handle_events_completed@8 = libusb_handle_events_completed
+ libusb_handle_events_locked
+ libusb_handle_events_locked@8 = libusb_handle_events_locked
+ libusb_handle_events_timeout
+ libusb_handle_events_timeout@8 = libusb_handle_events_timeout
+ libusb_handle_events_timeout_completed
+ libusb_handle_events_timeout_completed@12 = libusb_handle_events_timeout_completed
+ libusb_has_capability
+ libusb_has_capability@4 = libusb_has_capability
+ libusb_hotplug_deregister_callback
+ libusb_hotplug_deregister_callback@8 = libusb_hotplug_deregister_callback
+ libusb_hotplug_register_callback
+ libusb_hotplug_register_callback@36 = libusb_hotplug_register_callback
+ libusb_init
+ libusb_init@4 = libusb_init
+ libusb_interrupt_transfer
+ libusb_interrupt_transfer@24 = libusb_interrupt_transfer
+ libusb_kernel_driver_active
+ libusb_kernel_driver_active@8 = libusb_kernel_driver_active
+ libusb_lock_event_waiters
+ libusb_lock_event_waiters@4 = libusb_lock_event_waiters
+ libusb_lock_events
+ libusb_lock_events@4 = libusb_lock_events
+ libusb_open
+ libusb_open@8 = libusb_open
+ libusb_open_device_with_vid_pid
+ libusb_open_device_with_vid_pid@12 = libusb_open_device_with_vid_pid
+ libusb_pollfds_handle_timeouts
+ libusb_pollfds_handle_timeouts@4 = libusb_pollfds_handle_timeouts
+ libusb_ref_device
+ libusb_ref_device@4 = libusb_ref_device
+ libusb_release_interface
+ libusb_release_interface@8 = libusb_release_interface
+ libusb_reset_device
+ libusb_reset_device@4 = libusb_reset_device
+ libusb_set_auto_detach_kernel_driver
+ libusb_set_auto_detach_kernel_driver@8 = libusb_set_auto_detach_kernel_driver
+ libusb_set_configuration
+ libusb_set_configuration@8 = libusb_set_configuration
+ libusb_set_debug
+ libusb_set_debug@8 = libusb_set_debug
+ libusb_set_interface_alt_setting
+ libusb_set_interface_alt_setting@12 = libusb_set_interface_alt_setting
+ libusb_set_pollfd_notifiers
+ libusb_set_pollfd_notifiers@16 = libusb_set_pollfd_notifiers
+ libusb_setlocale
+ libusb_setlocale@4 = libusb_setlocale
+ libusb_strerror
+ libusb_strerror@4 = libusb_strerror
+ libusb_submit_transfer
+ libusb_submit_transfer@4 = libusb_submit_transfer
+ libusb_transfer_get_stream_id
+ libusb_transfer_get_stream_id@4 = libusb_transfer_get_stream_id
+ libusb_transfer_set_stream_id
+ libusb_transfer_set_stream_id@8 = libusb_transfer_set_stream_id
+ libusb_try_lock_events
+ libusb_try_lock_events@4 = libusb_try_lock_events
+ libusb_unlock_event_waiters
+ libusb_unlock_event_waiters@4 = libusb_unlock_event_waiters
+ libusb_unlock_events
+ libusb_unlock_events@4 = libusb_unlock_events
+ libusb_unref_device
+ libusb_unref_device@4 = libusb_unref_device
+ libusb_wait_for_event
+ libusb_wait_for_event@8 = libusb_wait_for_event
diff --git a/e502/libusb-1.0/libusb-1.0/libusb-1.0.rc b/e502/libusb-1.0/libusb-1.0/libusb-1.0.rc
new file mode 100644
index 0000000..3dce6d5
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/libusb-1.0.rc
@@ -0,0 +1,61 @@
+/*
+ * For Windows: input this file to the Resoure Compiler to produce a binary
+ * .res file. This is then embedded in the resultant library (like any other
+ * compilation object).
+ * The information can then be queried using standard APIs and can also be
+ * viewed with utilities such as Windows Explorer.
+ */
+#ifndef _WIN32_WCE
+#include "winresrc.h"
+#endif
+
+#include "version.h"
+#ifndef LIBUSB_VERSIONSTRING
+#define LU_STR(s) #s
+#define LU_XSTR(s) LU_STR(s)
+#if LIBUSB_NANO > 0
+#define LIBUSB_VERSIONSTRING \
+ LU_XSTR(LIBUSB_MAJOR) "." LU_XSTR(LIBUSB_MINOR) "." \
+ LU_XSTR(LIBUSB_MICRO) "." LU_XSTR(LIBUSB_NANO) LIBUSB_RC "\0"
+#else
+#define LIBUSB_VERSIONSTRING \
+ LU_XSTR(LIBUSB_MAJOR) "." LU_XSTR(LIBUSB_MINOR) "." \
+ LU_XSTR(LIBUSB_MICRO) LIBUSB_RC "\0"
+#endif
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION LIBUSB_MAJOR,LIBUSB_MINOR,LIBUSB_MICRO,LIBUSB_NANO
+ PRODUCTVERSION LIBUSB_MAJOR,LIBUSB_MINOR,LIBUSB_MICRO,LIBUSB_NANO
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "libusb.info\0"
+ VALUE "FileDescription", "C library for writing portable USB drivers in userspace\0"
+ VALUE "FileVersion", LIBUSB_VERSIONSTRING
+ VALUE "InternalName", "libusb\0"
+ VALUE "LegalCopyright", "See individual source files, GNU LGPL v2.1 or later.\0"
+ VALUE "LegalTrademarks", "http://www.gnu.org/licenses/lgpl-2.1.html\0"
+ VALUE "OriginalFilename", "libusb-1.0.dll\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "libusb-1.0\0"
+ VALUE "ProductVersion", LIBUSB_VERSIONSTRING
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/e502/libusb-1.0/libusb-1.0/libusb.h b/e502/libusb-1.0/libusb-1.0/libusb.h
new file mode 100644
index 0000000..53083ec
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/libusb.h
@@ -0,0 +1,2015 @@
+/*
+ * Public libusb header file
+ * Copyright © 2001 Johannes Erdfelt
+ * Copyright © 2007-2008 Daniel Drake
+ * Copyright © 2012 Pete Batard
+ * Copyright © 2012 Nathan Hjelm
+ * For more information, please visit: http://libusb.info
+ *
+ * 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 */
+#if !defined(__cplusplus)
+#define inline __inline
+#endif
+/* 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 not available on older MSVC */
+#if defined(_MSC_VER) && (_MSC_VER < 1600) && (!defined(_STDINT)) && (!defined(_STDINT_H))
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+#else
+#include
+#endif
+
+#if !defined(_WIN32_WCE)
+#include
+#endif
+
+#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__)
+#include
+#endif
+
+#include
+#include
+
+/* '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__) || defined(_WIN32_WCE)
+#include
+#if defined(interface)
+#undef interface
+#endif
+#if !defined(__CYGWIN__)
+#include
+#endif
+#endif
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+#define LIBUSB_DEPRECATED_FOR(f) \
+ __attribute__((deprecated("Use " #f " instead")))
+#else
+#define LIBUSB_DEPRECATED_FOR(f)
+#endif /* __GNUC__ */
+
+/** \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__) || defined(_WIN32_WCE)
+#define LIBUSB_CALL WINAPI
+#else
+#define LIBUSB_CALL
+#endif
+
+/** \def LIBUSB_API_VERSION
+ * \ingroup misc
+ * libusb's API version.
+ *
+ * Since version 1.0.13, to help with feature detection, libusb defines
+ * a LIBUSB_API_VERSION macro that gets increased every time there is a
+ * significant change to the API, such as the introduction of a new call,
+ * the definition of a new macro/enum member, or any other element that
+ * libusb applications may want to detect at compilation time.
+ *
+ * The macro is typically used in an application as follows:
+ * \code
+ * #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01001234)
+ * // Use one of the newer features from the libusb API
+ * #endif
+ * \endcode
+ *
+ * Another feature of LIBUSB_API_VERSION is that it can be used to detect
+ * whether you are compiling against the libusb or the libusb library.
+ *
+ * Internally, LIBUSB_API_VERSION is defined as follows:
+ * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental)
+ */
+#define LIBUSB_API_VERSION 0x01000103
+
+/* The following is kept for compatibility, but will be deprecated in the future */
+#define LIBUSBX_API_VERSION LIBUSB_API_VERSION
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \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] = (uint8_t) (x >> 8);
+ _tmp.b8[0] = (uint8_t) (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,
+
+ /** BOS descriptor */
+ LIBUSB_DT_BOS = 0x0f,
+
+ /** Device Capability descriptor */
+ LIBUSB_DT_DEVICE_CAPABILITY = 0x10,
+
+ /** 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,
+
+ /** SuperSpeed Hub descriptor */
+ LIBUSB_DT_SUPERSPEED_HUB = 0x2a,
+
+ /** SuperSpeed Endpoint Companion descriptor */
+ LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30
+};
+
+/* 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_DT_SS_ENDPOINT_COMPANION_SIZE 6
+#define LIBUSB_DT_BOS_SIZE 5
+#define LIBUSB_DT_DEVICE_CAPABILITY_SIZE 3
+
+/* BOS descriptor sizes */
+#define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7
+#define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10
+#define LIBUSB_BT_CONTAINER_ID_SIZE 20
+
+/* We unwrap the BOS => define its max size */
+#define LIBUSB_DT_BOS_MAX_SIZE ((LIBUSB_DT_BOS_SIZE) +\
+ (LIBUSB_BT_USB_2_0_EXTENSION_SIZE) +\
+ (LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) +\
+ (LIBUSB_BT_CONTAINER_ID_SIZE))
+
+#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_endpoint_transfer_type {
+ /** Control endpoint */
+ LIBUSB_ENDPOINT_TRANSFER_TYPE_CONTROL = 0x0,
+
+ /** Isochronous endpoint */
+ LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS = 0x1,
+
+ /** Bulk endpoint */
+ LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK = 0x2,
+
+ /** Interrupt endpoint */
+ LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT = 0x3,
+};
+
+/** \ingroup libusb_asyncio
+ * Transfer type
+ */
+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,
+
+ /** Stream endpoint */
+ LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4,
+};
+
+/** \ingroup misc
+ * Standard requests, as defined in table 9-5 of the USB 3.0 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,
+
+ /** Sets both the U1 and U2 Exit Latency */
+ LIBUSB_REQUEST_SET_SEL = 0x30,
+
+ /** Delay from the time a host transmits a packet to the time it is
+ * received by the device. */
+ LIBUSB_SET_ISOCH_DELAY = 0x31,
+};
+
+/** \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 3.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.6 of the USB 3.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 3.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 3.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 desc
+ * A structure representing the superspeed endpoint companion
+ * descriptor. This descriptor is documented in section 9.6.7 of
+ * the USB 3.0 specification. All multiple-byte fields are represented in
+ * host-endian format.
+ */
+struct libusb_ss_endpoint_companion_descriptor {
+
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_SS_ENDPOINT_COMPANION in
+ * this context. */
+ uint8_t bDescriptorType;
+
+
+ /** The maximum number of packets the endpoint can send or
+ * recieve as part of a burst. */
+ uint8_t bMaxBurst;
+
+ /** In bulk EP: bits 4:0 represents the maximum number of
+ * streams the EP supports. In isochronous EP: bits 1:0
+ * represents the Mult - a zero based value that determines
+ * the maximum number of packets within a service interval */
+ uint8_t bmAttributes;
+
+ /** The total number of bytes this EP will transfer every
+ * service interval. valid only for periodic EPs. */
+ uint16_t wBytesPerInterval;
+};
+
+/** \ingroup desc
+ * A generic representation of a BOS Device Capability descriptor. It is
+ * advised to check bDevCapabilityType and call the matching
+ * libusb_get_*_descriptor function to get a structure fully matching the type.
+ */
+struct libusb_bos_dev_capability_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ * LIBUSB_DT_DEVICE_CAPABILITY in this context. */
+ uint8_t bDescriptorType;
+ /** Device Capability type */
+ uint8_t bDevCapabilityType;
+ /** Device Capability data (bLength - 3 bytes) */
+ uint8_t dev_capability_data
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+ [] /* valid C99 code */
+#else
+ [0] /* non-standard, but usually working code */
+#endif
+ ;
+};
+
+/** \ingroup desc
+ * A structure representing the Binary Device Object Store (BOS) descriptor.
+ * This descriptor is documented in section 9.6.2 of the USB 3.0 specification.
+ * All multiple-byte fields are represented in host-endian format.
+ */
+struct libusb_bos_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_BOS LIBUSB_DT_BOS
+ * in this context. */
+ uint8_t bDescriptorType;
+
+ /** Length of this descriptor and all of its sub descriptors */
+ uint16_t wTotalLength;
+
+ /** The number of separate device capability descriptors in
+ * the BOS */
+ uint8_t bNumDeviceCaps;
+
+ /** bNumDeviceCap Device Capability Descriptors */
+ struct libusb_bos_dev_capability_descriptor *dev_capability
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+ [] /* valid C99 code */
+#else
+ [0] /* non-standard, but usually working code */
+#endif
+ ;
+};
+
+/** \ingroup desc
+ * A structure representing the USB 2.0 Extension descriptor
+ * This descriptor is documented in section 9.6.2.1 of the USB 3.0 specification.
+ * All multiple-byte fields are represented in host-endian format.
+ */
+struct libusb_usb_2_0_extension_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ * LIBUSB_DT_DEVICE_CAPABILITY in this context. */
+ uint8_t bDescriptorType;
+
+ /** Capability type. Will have value
+ * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION
+ * LIBUSB_BT_USB_2_0_EXTENSION in this context. */
+ uint8_t bDevCapabilityType;
+
+ /** Bitmap encoding of supported device level features.
+ * A value of one in a bit location indicates a feature is
+ * supported; a value of zero indicates it is not supported.
+ * See \ref libusb_usb_2_0_extension_attributes. */
+ uint32_t bmAttributes;
+};
+
+/** \ingroup desc
+ * A structure representing the SuperSpeed USB Device Capability descriptor
+ * This descriptor is documented in section 9.6.2.2 of the USB 3.0 specification.
+ * All multiple-byte fields are represented in host-endian format.
+ */
+struct libusb_ss_usb_device_capability_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ * LIBUSB_DT_DEVICE_CAPABILITY in this context. */
+ uint8_t bDescriptorType;
+
+ /** Capability type. Will have value
+ * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY
+ * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY in this context. */
+ uint8_t bDevCapabilityType;
+
+ /** Bitmap encoding of supported device level features.
+ * A value of one in a bit location indicates a feature is
+ * supported; a value of zero indicates it is not supported.
+ * See \ref libusb_ss_usb_device_capability_attributes. */
+ uint8_t bmAttributes;
+
+ /** Bitmap encoding of the speed supported by this device when
+ * operating in SuperSpeed mode. See \ref libusb_supported_speed. */
+ uint16_t wSpeedSupported;
+
+ /** The lowest speed at which all the functionality supported
+ * by the device is available to the user. For example if the
+ * device supports all its functionality when connected at
+ * full speed and above then it sets this value to 1. */
+ uint8_t bFunctionalitySupport;
+
+ /** U1 Device Exit Latency. */
+ uint8_t bU1DevExitLat;
+
+ /** U2 Device Exit Latency. */
+ uint16_t bU2DevExitLat;
+};
+
+/** \ingroup desc
+ * A structure representing the Container ID descriptor.
+ * This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification.
+ * All multiple-byte fields, except UUIDs, are represented in host-endian format.
+ */
+struct libusb_container_id_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ * LIBUSB_DT_DEVICE_CAPABILITY in this context. */
+ uint8_t bDescriptorType;
+
+ /** Capability type. Will have value
+ * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID
+ * LIBUSB_BT_CONTAINER_ID in this context. */
+ uint8_t bDevCapabilityType;
+
+ /** Reserved field */
+ uint8_t bReserved;
+
+ /** 128 bit UUID */
+ uint8_t ContainerID[16];
+};
+
+/** \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;
+struct libusb_hotplug_callback;
+
+/** \ingroup lib
+ * Structure providing the version of the libusb runtime
+ */
+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. */
+ const uint16_t nano;
+
+ /** Library release candidate suffix string, e.g. "-rc4". */
+ const char *rc;
+
+ /** For ABI compatibility only. */
+ 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_ref_device() and
+ * libusb_unref_device(), 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 dev
+ * Supported speeds (wSpeedSupported) bitfield. Indicates what
+ * speeds the device supports.
+ */
+enum libusb_supported_speed {
+ /** Low speed operation supported (1.5MBit/s). */
+ LIBUSB_LOW_SPEED_OPERATION = 1,
+
+ /** Full speed operation supported (12MBit/s). */
+ LIBUSB_FULL_SPEED_OPERATION = 2,
+
+ /** High speed operation supported (480MBit/s). */
+ LIBUSB_HIGH_SPEED_OPERATION = 4,
+
+ /** Superspeed operation supported (5000MBit/s). */
+ LIBUSB_SUPER_SPEED_OPERATION = 8,
+};
+
+/** \ingroup dev
+ * Masks for the bits of the
+ * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field
+ * of the USB 2.0 Extension descriptor.
+ */
+enum libusb_usb_2_0_extension_attributes {
+ /** Supports Link Power Management (LPM) */
+ LIBUSB_BM_LPM_SUPPORT = 2,
+};
+
+/** \ingroup dev
+ * Masks for the bits of the
+ * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field
+ * field of the SuperSpeed USB Device Capability descriptor.
+ */
+enum libusb_ss_usb_device_capability_attributes {
+ /** Supports Latency Tolerance Messages (LTM) */
+ LIBUSB_BM_LTM_SUPPORT = 2,
+};
+
+/** \ingroup dev
+ * USB capability types
+ */
+enum libusb_bos_type {
+ /** Wireless USB device capability */
+ LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 1,
+
+ /** USB 2.0 extensions */
+ LIBUSB_BT_USB_2_0_EXTENSION = 2,
+
+ /** SuperSpeed USB device capability */
+ LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 3,
+
+ /** Container ID type */
+ LIBUSB_BT_CONTAINER_ID = 4,
+};
+
+/** \ingroup misc
+ * Error codes. Most libusb functions return 0 on success or one of these
+ * codes on failure.
+ * You can call libusb_error_name() to retrieve a string representation of an
+ * error code or libusb_strerror() to get an end-user suitable description 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_COUNT below as well as the
+ message strings in strerror.c when adding new error codes here. */
+
+ /** Other error */
+ LIBUSB_ERROR_OTHER = -99,
+};
+
+/* Total number of error codes in enum libusb_error */
+#define LIBUSB_ERROR_COUNT 14
+
+/** \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,
+
+ /* NB! Remember to update libusb_error_name()
+ when adding new status codes here. */
+};
+
+/** \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 an instance of libusb on the current running
+ * platform. 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 = 0x0000,
+ /** Hotplug support is available on this platform. */
+ LIBUSB_CAP_HAS_HOTPLUG = 0x0001,
+ /** The library can access HID devices without requiring user intervention.
+ * Note that before being able to actually access an HID device, you may
+ * still have to call additional libusb functions such as
+ * \ref libusb_detach_kernel_driver(). */
+ LIBUSB_CAP_HAS_HID_ACCESS = 0x0100,
+ /** The library supports detaching of the default USB driver, using
+ * \ref libusb_detach_kernel_driver(), if one is set by the OS kernel */
+ LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101
+};
+
+/** \ingroup lib
+ * Log message levels.
+ * - LIBUSB_LOG_LEVEL_NONE (0) : no messages ever printed by the library (default)
+ * - LIBUSB_LOG_LEVEL_ERROR (1) : error messages are printed to stderr
+ * - LIBUSB_LOG_LEVEL_WARNING (2) : warning and error messages are printed to stderr
+ * - LIBUSB_LOG_LEVEL_INFO (3) : informational messages are printed to stdout, warning
+ * and error messages are printed to stderr
+ * - LIBUSB_LOG_LEVEL_DEBUG (4) : debug and informational messages are printed to stdout,
+ * warnings and errors to stderr
+ */
+enum libusb_log_level {
+ LIBUSB_LOG_LEVEL_NONE = 0,
+ LIBUSB_LOG_LEVEL_ERROR,
+ LIBUSB_LOG_LEVEL_WARNING,
+ LIBUSB_LOG_LEVEL_INFO,
+ LIBUSB_LOG_LEVEL_DEBUG,
+};
+
+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);
+int LIBUSB_CALL libusb_setlocale(const char *locale);
+const char * LIBUSB_CALL libusb_strerror(enum libusb_error 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);
+int LIBUSB_CALL libusb_get_ss_endpoint_companion_descriptor(
+ struct libusb_context *ctx,
+ const struct libusb_endpoint_descriptor *endpoint,
+ struct libusb_ss_endpoint_companion_descriptor **ep_comp);
+void LIBUSB_CALL libusb_free_ss_endpoint_companion_descriptor(
+ struct libusb_ss_endpoint_companion_descriptor *ep_comp);
+int LIBUSB_CALL libusb_get_bos_descriptor(libusb_device_handle *handle,
+ struct libusb_bos_descriptor **bos);
+void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos);
+int LIBUSB_CALL libusb_get_usb_2_0_extension_descriptor(
+ struct libusb_context *ctx,
+ struct libusb_bos_dev_capability_descriptor *dev_cap,
+ struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension);
+void LIBUSB_CALL libusb_free_usb_2_0_extension_descriptor(
+ struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension);
+int LIBUSB_CALL libusb_get_ss_usb_device_capability_descriptor(
+ struct libusb_context *ctx,
+ struct libusb_bos_dev_capability_descriptor *dev_cap,
+ struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap);
+void LIBUSB_CALL libusb_free_ss_usb_device_capability_descriptor(
+ struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap);
+int LIBUSB_CALL libusb_get_container_id_descriptor(struct libusb_context *ctx,
+ struct libusb_bos_dev_capability_descriptor *dev_cap,
+ struct libusb_container_id_descriptor **container_id);
+void LIBUSB_CALL libusb_free_container_id_descriptor(
+ struct libusb_container_id_descriptor *container_id);
+uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev);
+uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev);
+int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t* port_numbers, int port_numbers_len);
+LIBUSB_DEPRECATED_FOR(libusb_get_port_numbers)
+int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t* path, uint8_t path_length);
+libusb_device * LIBUSB_CALL libusb_get_parent(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_alloc_streams(libusb_device_handle *dev,
+ uint32_t num_streams, unsigned char *endpoints, int num_endpoints);
+int LIBUSB_CALL libusb_free_streams(libusb_device_handle *dev,
+ unsigned char *endpoints, int num_endpoints);
+
+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);
+int LIBUSB_CALL libusb_set_auto_detach_kernel_driver(
+ libusb_device_handle *dev, int enable);
+
+/* 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 *)(void *) 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
+ * This pointer must be aligned to at least 2 bytes boundary.
+ * \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 *)(void *) 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);
+void LIBUSB_CALL libusb_transfer_set_stream_id(
+ struct libusb_transfer *transfer, uint32_t stream_id);
+uint32_t LIBUSB_CALL libusb_transfer_get_stream_id(
+ 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.
+ * This pointer must be aligned to at least 2 bytes boundary.
+ * \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 *)(void *) 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 = (int) (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 a bulk transfer using bulk streams.
+ *
+ * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103
+ *
+ * \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 stream_id bulk stream id for this transfer
+ * \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_stream_transfer(
+ struct libusb_transfer *transfer, libusb_device_handle *dev_handle,
+ unsigned char endpoint, uint32_t stream_id,
+ unsigned char *buffer, int length, libusb_transfer_cb_fn callback,
+ void *user_data, unsigned int timeout)
+{
+ libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer,
+ length, callback, user_data, timeout);
+ transfer->type = LIBUSB_TRANSFER_TYPE_BULK_STREAM;
+ libusb_transfer_set_stream_id(transfer, stream_id);
+}
+
+/** \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 = (int) 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 = (int) packet;
+
+ if (_packet >= transfer->num_iso_packets)
+ return NULL;
+
+ return transfer->buffer + ((int) 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, (uint16_t) ((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);
+
+/** \ingroup hotplug
+ * Callback handle.
+ *
+ * Callbacks handles are generated by libusb_hotplug_register_callback()
+ * and can be used to deregister callbacks. Callback handles are unique
+ * per libusb_context and it is safe to call libusb_hotplug_deregister_callback()
+ * on an already deregisted callback.
+ *
+ * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ *
+ * For more information, see \ref hotplug.
+ */
+typedef int libusb_hotplug_callback_handle;
+
+/** \ingroup hotplug
+ *
+ * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ *
+ * Flags for hotplug events */
+typedef enum {
+ /** Arm the callback and fire it for all matching currently attached devices. */
+ LIBUSB_HOTPLUG_ENUMERATE = 1,
+} libusb_hotplug_flag;
+
+/** \ingroup hotplug
+ *
+ * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ *
+ * Hotplug events */
+typedef enum {
+ /** A device has been plugged in and is ready to use */
+ LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = 0x01,
+
+ /** A device has left and is no longer available.
+ * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device.
+ * It is safe to call libusb_get_device_descriptor on a device that has left */
+ LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = 0x02,
+} libusb_hotplug_event;
+
+/** \ingroup hotplug
+ * Wildcard matching for hotplug events */
+#define LIBUSB_HOTPLUG_MATCH_ANY -1
+
+/** \ingroup hotplug
+ * Hotplug callback function type. When requesting hotplug event notifications,
+ * you pass a pointer to a callback function of this type.
+ *
+ * This callback may be called by an internal event thread and as such it is
+ * recommended the callback do minimal processing before returning.
+ *
+ * libusb will call this function later, when a matching event had happened on
+ * a matching device. See \ref hotplug for more information.
+ *
+ * It is safe to call either libusb_hotplug_register_callback() or
+ * libusb_hotplug_deregister_callback() from within a callback function.
+ *
+ * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ *
+ * \param ctx context of this notification
+ * \param device libusb_device this event occurred on
+ * \param event event that occurred
+ * \param user_data user data provided when this callback was registered
+ * \returns bool whether this callback is finished processing events.
+ * returning 1 will cause this callback to be deregistered
+ */
+typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx,
+ libusb_device *device,
+ libusb_hotplug_event event,
+ void *user_data);
+
+/** \ingroup hotplug
+ * Register a hotplug callback function
+ *
+ * Register a callback with the libusb_context. The callback will fire
+ * when a matching event occurs on a matching device. The callback is
+ * armed until either it is deregistered with libusb_hotplug_deregister_callback()
+ * or the supplied callback returns 1 to indicate it is finished processing events.
+ *
+ * If the \ref LIBUSB_HOTPLUG_ENUMERATE is passed the callback will be
+ * called with a \ref LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED for all devices
+ * already plugged into the machine. Note that libusb modifies its internal
+ * device list from a separate thread, while calling hotplug callbacks from
+ * libusb_handle_events(), so it is possible for a device to already be present
+ * on, or removed from, its internal device list, while the hotplug callbacks
+ * still need to be dispatched. This means that when using \ref
+ * LIBUSB_HOTPLUG_ENUMERATE, your callback may be called twice for the arrival
+ * of the same device, once from libusb_hotplug_register_callback() and once
+ * from libusb_handle_events(); and/or your callback may be called for the
+ * removal of a device for which an arrived call was never made.
+ *
+ * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ *
+ * \param[in] ctx context to register this callback with
+ * \param[in] events bitwise or of events that will trigger this callback. See \ref
+ * libusb_hotplug_event
+ * \param[in] flags hotplug callback flags. See \ref libusb_hotplug_flag
+ * \param[in] vendor_id the vendor id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
+ * \param[in] product_id the product id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
+ * \param[in] dev_class the device class to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
+ * \param[in] cb_fn the function to be invoked on a matching event/device
+ * \param[in] user_data user data to pass to the callback function
+ * \param[out] handle pointer to store the handle of the allocated callback (can be NULL)
+ * \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure
+ */
+int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx,
+ libusb_hotplug_event events,
+ libusb_hotplug_flag flags,
+ int vendor_id, int product_id,
+ int dev_class,
+ libusb_hotplug_callback_fn cb_fn,
+ void *user_data,
+ libusb_hotplug_callback_handle *handle);
+
+/** \ingroup hotplug
+ * Deregisters a hotplug callback.
+ *
+ * Deregister a callback from a libusb_context. This function is safe to call from within
+ * a hotplug callback.
+ *
+ * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ *
+ * \param[in] ctx context this callback is registered with
+ * \param[in] handle the handle of the callback to deregister
+ */
+void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx,
+ libusb_hotplug_callback_handle handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/e502/libusb-1.0/libusb-1.0/libusbi.h b/e502/libusb-1.0/libusb-1.0/libusbi.h
new file mode 100644
index 0000000..d59ec30
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/libusbi.h
@@ -0,0 +1,1027 @@
+/*
+ * Internal header for libusb
+ * Copyright © 2007-2009 Daniel Drake
+ * Copyright © 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 "config.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#ifdef HAVE_POLL_H
+#include
+#endif
+
+#ifdef HAVE_MISSING_H
+#include "missing.h"
+#endif
+#include "libusb.h"
+#include "version.h"
+
+/* 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
+
+/* Backend specific capabilities */
+#define USBI_CAP_HAS_HID_ACCESS 0x00010000
+#define USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER 0x00020000
+
+/* Maximum number of bytes in a log line */
+#define USBI_MAX_LOG_LEN 1024
+/* Terminator for log lines */
+#define USBI_LOG_LINE_END "\n"
+
+/* The following is used to silence warnings for unused variables */
+#define UNUSED(var) do { (void)(var); } while(0)
+
+#if !defined(ARRAYSIZE)
+#define ARRAYSIZE(array) (sizeof(array)/sizeof(array[0]))
+#endif
+
+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)offsetof(type, 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)
+{
+ if (entry->next != NULL)
+ entry->next->prev = entry->prev;
+ if (entry->prev != NULL)
+ entry->prev->next = entry->next;
+ entry->next = entry->prev = NULL;
+}
+
+static inline void *usbi_reallocf(void *ptr, size_t size)
+{
+ void *ret = realloc(ptr, size);
+ if (!ret)
+ free(ptr);
+ return ret;
+}
+
+#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)
+
+/* Some platforms don't have this define */
+#ifndef TIMESPEC_TO_TIMEVAL
+#define TIMESPEC_TO_TIMEVAL(tv, ts) \
+ do { \
+ (tv)->tv_sec = (long)(ts)->tv_sec; \
+ (tv)->tv_usec = (ts)->tv_nsec / 1000; \
+ } while (0)
+#endif
+
+void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
+ const char *function, const char *format, ...);
+
+void usbi_log_v(struct libusb_context *ctx, enum libusb_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__)
+#define usbi_dbg(...) _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
+#else
+#define _usbi_log(ctx, level, ...) do { (void)(ctx); } while(0)
+#define usbi_dbg(...) do {} while(0)
+#endif
+
+#define usbi_info(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__)
+#define usbi_warn(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__)
+#define usbi_err(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_ERROR, __VA_ARGS__)
+
+#else /* !defined(_MSC_VER) || _MSC_VER >= 1400 */
+
+#ifdef ENABLE_LOGGING
+#define LOG_BODY(ctxt, level) \
+{ \
+ va_list args; \
+ va_start (args, format); \
+ usbi_log_v(ctxt, level, "", format, args); \
+ va_end(args); \
+}
+#else
+#define LOG_BODY(ctxt, level) do { (void)(ctxt); } while(0)
+#endif
+
+static inline void usbi_info(struct libusb_context *ctx, const char *format,
+ ...)
+ LOG_BODY(ctx,LIBUSB_LOG_LEVEL_INFO)
+static inline void usbi_warn(struct libusb_context *ctx, const char *format,
+ ...)
+ LOG_BODY(ctx,LIBUSB_LOG_LEVEL_WARNING)
+static inline void usbi_err( struct libusb_context *ctx, const char *format,
+ ...)
+ LOG_BODY(ctx,LIBUSB_LOG_LEVEL_ERROR)
+
+static inline void usbi_dbg(const char *format, ...)
+ LOG_BODY(NULL,LIBUSB_LOG_LEVEL_DEBUG)
+
+#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 abstraction for thread synchronization */
+#if defined(THREADS_POSIX)
+#include "os/threads_posix.h"
+#elif defined(OS_WINDOWS) || defined(OS_WINCE)
+#include "libusb-1.0/os/threads_windows.h"
+#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;
+
+ /* A list of registered hotplug callbacks */
+ struct list_head hotplug_cbs;
+ usbi_mutex_t hotplug_cbs_lock;
+ int hotplug_pipe[2];
+
+ /* 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
+
+ struct list_head list;
+};
+
+#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 port_number;
+ struct libusb_device* parent_dev;
+ uint8_t device_address;
+ uint8_t num_configurations;
+ enum libusb_speed speed;
+
+ struct list_head list;
+ unsigned long session_data;
+
+ struct libusb_device_descriptor device_descriptor;
+ int attached;
+
+ unsigned char os_priv
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+ [] /* valid C99 code */
+#else
+ [0] /* non-standard, but usually working code */
+#endif
+ ;
+};
+
+struct libusb_device_handle {
+ /* lock protects claimed_interfaces */
+ usbi_mutex_t lock;
+ unsigned long claimed_interfaces;
+
+ struct list_head list;
+ struct libusb_device *dev;
+ int auto_detach_kernel_driver;
+ unsigned char os_priv
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+ [] /* valid C99 code */
+#else
+ [0] /* non-standard, but usually working code */
+#endif
+ ;
+};
+
+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;
+ uint32_t stream_id;
+ 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,
+
+ /* Set by backend submit_transfer() if the fds in use have been updated */
+ USBI_TRANSFER_UPDATED_FDS = 1 << 4,
+};
+
+#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(const unsigned char *source, const char *descriptor,
+ void *dest, int host_endian);
+int usbi_device_cache_descriptor(libusb_device *dev);
+int usbi_get_config_index_by_value(struct libusb_device *dev,
+ uint8_t bConfigurationValue, int *idx);
+
+void usbi_connect_device (struct libusb_device *dev);
+void usbi_disconnect_device (struct libusb_device *dev);
+
+/* Internal abstraction for poll (needs struct usbi_transfer on Windows) */
+#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD) || defined(OS_NETBSD)
+#include
+#include "os/poll_posix.h"
+#elif defined(OS_WINDOWS) || defined(OS_WINCE)
+#include "os/poll_windows.h"
+#endif
+
+#if (defined(OS_WINDOWS) || defined(OS_WINCE)) && !defined(__GNUC__)
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+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
+
+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
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+ [] /* valid C99 code */
+#else
+ [0] /* non-standard, but usually working code */
+#endif
+ ;
+};
+
+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;
+
+ /* Binary mask for backend specific capabilities */
+ uint32_t caps;
+
+ /* 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 reference
+ * to 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.
+ *
+ * If the backend has hotplug support, this function is not used!
+ *
+ * Return 0 on success, or a LIBUSB_ERROR code on failure.
+ */
+ int (*get_device_list)(struct libusb_context *ctx,
+ struct discovered_devs **discdevs);
+
+ /* Apps which were written before hotplug support, may listen for
+ * hotplug events on their own and call libusb_get_device_list on
+ * device addition. In this case libusb_get_device_list will likely
+ * return a list without the new device in there, as the hotplug
+ * event thread will still be busy enumerating the device, which may
+ * take a while, or may not even have seen the event yet.
+ *
+ * To avoid this libusb_get_device_list will call this optional
+ * function for backends with hotplug support before copying
+ * ctx->usb_devs to the user. In this function the backend should
+ * ensure any pending hotplug events are fully processed before
+ * returning.
+ *
+ * Optional, should be implemented by backends with hotplug support.
+ */
+ void (*hotplug_poll)(void);
+
+ /* 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 the length read 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);
+
+ /* Like get_config_descriptor but then by bConfigurationValue instead
+ * of by index.
+ *
+ * Optional, if not present the core will call get_config_descriptor
+ * for all configs until it finds the desired bConfigurationValue.
+ *
+ * Returns a pointer to the raw-descriptor in *buffer, this memory
+ * is valid as long as device is valid.
+ *
+ * Returns the length of the returned raw-descriptor on success,
+ * or a LIBUSB_ERROR code on failure.
+ */
+ int (*get_config_descriptor_by_value)(struct libusb_device *device,
+ uint8_t bConfigurationValue, unsigned char **buffer,
+ 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);
+
+ /* Alloc num_streams usb3 bulk streams on the passed in endpoints */
+ int (*alloc_streams)(struct libusb_device_handle *handle,
+ uint32_t num_streams, unsigned char *endpoints, int num_endpoints);
+
+ /* Free usb3 bulk streams allocated with alloc_streams */
+ int (*free_streams)(struct libusb_device_handle *handle,
+ unsigned char *endpoints, int num_endpoints);
+
+ /* 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.
+ *
+ * This function gets called with the flying_transfers_lock locked!
+ *
+ * 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 netbsd_backend;
+extern const struct usbi_os_backend windows_backend;
+extern const struct usbi_os_backend wince_backend;
+
+extern struct list_head active_contexts_list;
+extern usbi_mutex_static_t active_contexts_lock;
+
+#endif
diff --git a/e502/libusb-1.0/libusb-1.0/msvc/config.h b/e502/libusb-1.0/libusb-1.0/msvc/config.h
new file mode 100644
index 0000000..4b418db
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/msvc/config.h
@@ -0,0 +1,42 @@
+/* config.h. Manual config for MSVC. */
+
+#ifndef _MSC_VER
+#warn "msvc/config.h shouldn't be included for your development environment."
+#error "Please make sure the msvc/ directory is removed from your build path."
+#endif
+
+/* Disable: warning C4200: nonstandard extension used : zero-sized array in struct/union */
+#pragma warning(disable:4200)
+/* Disable: warning C6258: Using TerminateThread does not allow proper thread clean up */
+#pragma warning(disable: 6258)
+#if defined(_PREFAST_)
+/* Disable "Banned API" errors when using the MS's WDK OACR/Prefast */
+#pragma warning(disable:28719)
+/* Disable "The function 'InitializeCriticalSection' must be called from within a try/except block" */
+#pragma warning(disable:28125)
+#endif
+
+/* Default visibility */
+#define DEFAULT_VISIBILITY /**/
+
+/* Enable global message logging */
+#define ENABLE_LOGGING 1
+
+/* Uncomment to start with debug message logging enabled */
+// #define ENABLE_DEBUG_LOGGING 1
+
+/* Uncomment to enabling logging to system log */
+// #define USE_SYSTEM_LOGGING_FACILITY
+
+/* type of second poll() argument */
+#define POLL_NFDS_TYPE unsigned int
+
+/* Windows/WinCE backend */
+#if defined(_WIN32_WCE)
+#define OS_WINCE 1
+#define HAVE_MISSING_H
+#else
+#define OS_WINDOWS 1
+#define HAVE_SIGNAL_H 1
+#define HAVE_SYS_TYPES_H 1
+#endif
diff --git a/e502/libusb-1.0/libusb-1.0/msvc/errno/errno.h b/e502/libusb-1.0/libusb-1.0/msvc/errno/errno.h
new file mode 100644
index 0000000..07d15e3
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/msvc/errno/errno.h
@@ -0,0 +1,102 @@
+/*
+ * errno.h
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is a part of the mingw-runtime package.
+ * No warranty is given; refer to the file DISCLAIMER within the package.
+ *
+ * Error numbers and access to error reporting.
+ *
+ */
+
+#ifndef _ERRNO_H_
+#define _ERRNO_H_
+
+#include
+
+/*
+ * Error numbers.
+ * TODO: Can't be sure of some of these assignments, I guessed from the
+ * names given by strerror and the defines in the Cygnus errno.h. A lot
+ * of the names from the Cygnus errno.h are not represented, and a few
+ * of the descriptions returned by strerror do not obviously match
+ * their error naming.
+ */
+#define EPERM 1 /* Operation not permitted */
+#define ENOFILE 2 /* No such file or directory */
+#define ENOENT 2
+#define ESRCH 3 /* No such process */
+#define EINTR 4 /* Interrupted function call */
+#define EIO 5 /* Input/output error */
+#define ENXIO 6 /* No such device or address */
+#define E2BIG 7 /* Arg list too long */
+#define ENOEXEC 8 /* Exec format error */
+#define EBADF 9 /* Bad file descriptor */
+#define ECHILD 10 /* No child processes */
+#define EAGAIN 11 /* Resource temporarily unavailable */
+#define ENOMEM 12 /* Not enough space */
+#define EACCES 13 /* Permission denied */
+#define EFAULT 14 /* Bad address */
+/* 15 - Unknown Error */
+#define EBUSY 16 /* strerror reports "Resource device" */
+#define EEXIST 17 /* File exists */
+#define EXDEV 18 /* Improper link (cross-device link?) */
+#define ENODEV 19 /* No such device */
+#define ENOTDIR 20 /* Not a directory */
+#define EISDIR 21 /* Is a directory */
+#define EINVAL 22 /* Invalid argument */
+#define ENFILE 23 /* Too many open files in system */
+#define EMFILE 24 /* Too many open files */
+#define ENOTTY 25 /* Inappropriate I/O control operation */
+/* 26 - Unknown Error */
+#define EFBIG 27 /* File too large */
+#define ENOSPC 28 /* No space left on device */
+#define ESPIPE 29 /* Invalid seek (seek on a pipe?) */
+#define EROFS 30 /* Read-only file system */
+#define EMLINK 31 /* Too many links */
+#define EPIPE 32 /* Broken pipe */
+#define EDOM 33 /* Domain error (math functions) */
+#define ERANGE 34 /* Result too large (possibly too small) */
+/* 35 - Unknown Error */
+#define EDEADLOCK 36 /* Resource deadlock avoided (non-Cyg) */
+#define EDEADLK 36
+#if 0
+/* 37 - Unknown Error */
+#define ENAMETOOLONG 38 /* Filename too long (91 in Cyg?) */
+#define ENOLCK 39 /* No locks available (46 in Cyg?) */
+#define ENOSYS 40 /* Function not implemented (88 in Cyg?) */
+#define ENOTEMPTY 41 /* Directory not empty (90 in Cyg?) */
+#define EILSEQ 42 /* Illegal byte sequence */
+#endif
+
+/*
+ * NOTE: ENAMETOOLONG and ENOTEMPTY conflict with definitions in the
+ * sockets.h header provided with windows32api-0.1.2.
+ * You should go and put an #if 0 ... #endif around the whole block
+ * of errors (look at the comment above them).
+ */
+
+#ifndef RC_INVOKED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Definitions of errno. For _doserrno, sys_nerr and * sys_errlist, see
+ * stdlib.h.
+ */
+#if defined(_UWIN) || defined(_WIN32_WCE)
+#undef errno
+extern int errno;
+#else
+_CRTIMP int* __cdecl _errno(void);
+#define errno (*_errno())
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* Not RC_INVOKED */
+
+#endif /* Not _ERRNO_H_ */
\ No newline at end of file
diff --git a/e502/libusb-1.0/libusb-1.0/msvc/inttypes/inttypes.h b/e502/libusb-1.0/libusb-1.0/msvc/inttypes/inttypes.h
new file mode 100644
index 0000000..289bb50
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/msvc/inttypes/inttypes.h
@@ -0,0 +1,295 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file was original part of the w64 mingw-runtime package.
+ */
+
+/*
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * Modified for libusb/MSVC: Pete Batard
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Date: 2010-04-02
+ */
+
+#ifndef _MSC_VER
+#error This header should only be used with Microsoft compilers
+#endif
+
+/* 7.8 Format conversion of integer types */
+
+#ifndef _INTTYPES_H_
+#define _INTTYPES_H_
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ intmax_t quot;
+ intmax_t rem;
+ } imaxdiv_t;
+
+
+/* 7.8.1 Macros for format specifiers
+ *
+ * MS runtime does not yet understand C9x standard "ll"
+ * length specifier. It appears to treat "ll" as "l".
+ * The non-standard I64 length specifier causes warning in GCC,
+ * but understood by MS runtime functions.
+ */
+
+/* fprintf macros for signed types */
+#define PRId8 "d"
+#define PRId16 "d"
+#define PRId32 "d"
+#define PRId64 "I64d"
+
+#define PRIdLEAST8 "d"
+#define PRIdLEAST16 "d"
+#define PRIdLEAST32 "d"
+#define PRIdLEAST64 "I64d"
+
+#define PRIdFAST8 "d"
+#define PRIdFAST16 "d"
+#define PRIdFAST32 "d"
+#define PRIdFAST64 "I64d"
+
+#define PRIdMAX "I64d"
+
+#define PRIi8 "i"
+#define PRIi16 "i"
+#define PRIi32 "i"
+#define PRIi64 "I64i"
+
+#define PRIiLEAST8 "i"
+#define PRIiLEAST16 "i"
+#define PRIiLEAST32 "i"
+#define PRIiLEAST64 "I64i"
+
+#define PRIiFAST8 "i"
+#define PRIiFAST16 "i"
+#define PRIiFAST32 "i"
+#define PRIiFAST64 "I64i"
+
+#define PRIiMAX "I64i"
+
+#define PRIo8 "o"
+#define PRIo16 "o"
+#define PRIo32 "o"
+#define PRIo64 "I64o"
+
+#define PRIoLEAST8 "o"
+#define PRIoLEAST16 "o"
+#define PRIoLEAST32 "o"
+#define PRIoLEAST64 "I64o"
+
+#define PRIoFAST8 "o"
+#define PRIoFAST16 "o"
+#define PRIoFAST32 "o"
+#define PRIoFAST64 "I64o"
+
+#define PRIoMAX "I64o"
+
+/* fprintf macros for unsigned types */
+#define PRIu8 "u"
+#define PRIu16 "u"
+#define PRIu32 "u"
+#define PRIu64 "I64u"
+
+
+#define PRIuLEAST8 "u"
+#define PRIuLEAST16 "u"
+#define PRIuLEAST32 "u"
+#define PRIuLEAST64 "I64u"
+
+#define PRIuFAST8 "u"
+#define PRIuFAST16 "u"
+#define PRIuFAST32 "u"
+#define PRIuFAST64 "I64u"
+
+#define PRIuMAX "I64u"
+
+#define PRIx8 "x"
+#define PRIx16 "x"
+#define PRIx32 "x"
+#define PRIx64 "I64x"
+
+#define PRIxLEAST8 "x"
+#define PRIxLEAST16 "x"
+#define PRIxLEAST32 "x"
+#define PRIxLEAST64 "I64x"
+
+#define PRIxFAST8 "x"
+#define PRIxFAST16 "x"
+#define PRIxFAST32 "x"
+#define PRIxFAST64 "I64x"
+
+#define PRIxMAX "I64x"
+
+#define PRIX8 "X"
+#define PRIX16 "X"
+#define PRIX32 "X"
+#define PRIX64 "I64X"
+
+#define PRIXLEAST8 "X"
+#define PRIXLEAST16 "X"
+#define PRIXLEAST32 "X"
+#define PRIXLEAST64 "I64X"
+
+#define PRIXFAST8 "X"
+#define PRIXFAST16 "X"
+#define PRIXFAST32 "X"
+#define PRIXFAST64 "I64X"
+
+#define PRIXMAX "I64X"
+
+/*
+ * fscanf macros for signed int types
+ * NOTE: if 32-bit int is used for int_fast8_t and int_fast16_t
+ * (see stdint.h, 7.18.1.3), FAST8 and FAST16 should have
+ * no length identifiers
+ */
+
+#define SCNd16 "hd"
+#define SCNd32 "d"
+#define SCNd64 "I64d"
+
+#define SCNdLEAST16 "hd"
+#define SCNdLEAST32 "d"
+#define SCNdLEAST64 "I64d"
+
+#define SCNdFAST16 "hd"
+#define SCNdFAST32 "d"
+#define SCNdFAST64 "I64d"
+
+#define SCNdMAX "I64d"
+
+#define SCNi16 "hi"
+#define SCNi32 "i"
+#define SCNi64 "I64i"
+
+#define SCNiLEAST16 "hi"
+#define SCNiLEAST32 "i"
+#define SCNiLEAST64 "I64i"
+
+#define SCNiFAST16 "hi"
+#define SCNiFAST32 "i"
+#define SCNiFAST64 "I64i"
+
+#define SCNiMAX "I64i"
+
+#define SCNo16 "ho"
+#define SCNo32 "o"
+#define SCNo64 "I64o"
+
+#define SCNoLEAST16 "ho"
+#define SCNoLEAST32 "o"
+#define SCNoLEAST64 "I64o"
+
+#define SCNoFAST16 "ho"
+#define SCNoFAST32 "o"
+#define SCNoFAST64 "I64o"
+
+#define SCNoMAX "I64o"
+
+#define SCNx16 "hx"
+#define SCNx32 "x"
+#define SCNx64 "I64x"
+
+#define SCNxLEAST16 "hx"
+#define SCNxLEAST32 "x"
+#define SCNxLEAST64 "I64x"
+
+#define SCNxFAST16 "hx"
+#define SCNxFAST32 "x"
+#define SCNxFAST64 "I64x"
+
+#define SCNxMAX "I64x"
+
+/* fscanf macros for unsigned int types */
+
+#define SCNu16 "hu"
+#define SCNu32 "u"
+#define SCNu64 "I64u"
+
+#define SCNuLEAST16 "hu"
+#define SCNuLEAST32 "u"
+#define SCNuLEAST64 "I64u"
+
+#define SCNuFAST16 "hu"
+#define SCNuFAST32 "u"
+#define SCNuFAST64 "I64u"
+
+#define SCNuMAX "I64u"
+
+#ifdef _WIN64
+#define PRIdPTR "I64d"
+#define PRIiPTR "I64i"
+#define PRIoPTR "I64o"
+#define PRIuPTR "I64u"
+#define PRIxPTR "I64x"
+#define PRIXPTR "I64X"
+#define SCNdPTR "I64d"
+#define SCNiPTR "I64i"
+#define SCNoPTR "I64o"
+#define SCNxPTR "I64x"
+#define SCNuPTR "I64u"
+#else
+#define PRIdPTR "d"
+#define PRIiPTR "i"
+#define PRIoPTR "o"
+#define PRIuPTR "u"
+#define PRIxPTR "x"
+#define PRIXPTR "X"
+#define SCNdPTR "d"
+#define SCNiPTR "i"
+#define SCNoPTR "o"
+#define SCNxPTR "x"
+ #define SCNuPTR "u"
+#endif
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+/*
+ * no length modifier for char types prior to C9x
+ * MS runtime scanf appears to treat "hh" as "h"
+ */
+
+/* signed char */
+#define SCNd8 "hhd"
+#define SCNdLEAST8 "hhd"
+#define SCNdFAST8 "hhd"
+
+#define SCNi8 "hhi"
+#define SCNiLEAST8 "hhi"
+#define SCNiFAST8 "hhi"
+
+#define SCNo8 "hho"
+#define SCNoLEAST8 "hho"
+#define SCNoFAST8 "hho"
+
+#define SCNx8 "hhx"
+#define SCNxLEAST8 "hhx"
+#define SCNxFAST8 "hhx"
+
+/* unsigned char */
+#define SCNu8 "hhu"
+#define SCNuLEAST8 "hhu"
+#define SCNuFAST8 "hhu"
+#endif /* __STDC_VERSION__ >= 199901 */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ndef _INTTYPES_H */
diff --git a/e502/libusb-1.0/libusb-1.0/msvc/missing.h b/e502/libusb-1.0/libusb-1.0/msvc/missing.h
new file mode 100644
index 0000000..183b9d3
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/msvc/missing.h
@@ -0,0 +1,32 @@
+/*
+ * Header file for missing WinCE functionality
+ * Copyright © 2012-2013 RealVNC Ltd.
+ *
+ * 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 MISSING_H
+#define MISSING_H
+
+/* Windows CE doesn't have SleepEx() - Fallback to Sleep() */
+#define SleepEx(m, a) Sleep(m)
+
+/* Windows CE doesn't have any APIs to query environment variables.
+ *
+ * This contains a registry based implementation of getenv.
+ */
+char *getenv(const char *name);
+
+#endif
diff --git a/e502/libusb-1.0/libusb-1.0/msvc/stdint/stdint.h b/e502/libusb-1.0/libusb-1.0/msvc/stdint/stdint.h
new file mode 100644
index 0000000..00988d9
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/msvc/stdint/stdint.h
@@ -0,0 +1,256 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file was originally part of the w64 mingw-runtime package.
+ */
+
+/* ISO C9x 7.18 Integer types
+ * Based on ISO/IEC SC22/WG14 9899 Committee draft (SC22 N2794)
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * Contributor: Danny Smith
+ * Modified for libusb/MSVC: Pete Batard
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Date: 2010-04-02
+ */
+
+#ifndef _MSC_VER
+#error This header should only be used with Microsoft compilers
+#endif
+
+#ifndef _STDINT_H
+#define _STDINT_H
+
+#ifndef _INTPTR_T_DEFINED
+#define _INTPTR_T_DEFINED
+#ifndef __intptr_t_defined
+#define __intptr_t_defined
+#undef intptr_t
+#ifdef _WIN64
+ typedef __int64 intptr_t;
+#else
+ typedef int intptr_t;
+#endif /* _WIN64 */
+#endif /* __intptr_t_defined */
+#endif /* _INTPTR_T_DEFINED */
+
+#ifndef _UINTPTR_T_DEFINED
+#define _UINTPTR_T_DEFINED
+#ifndef __uintptr_t_defined
+#define __uintptr_t_defined
+#undef uintptr_t
+#ifdef _WIN64
+ typedef unsigned __int64 uintptr_t;
+#else
+ typedef unsigned int uintptr_t;
+#endif /* _WIN64 */
+#endif /* __uintptr_t_defined */
+#endif /* _UINTPTR_T_DEFINED */
+
+#ifndef _PTRDIFF_T_DEFINED
+#define _PTRDIFF_T_DEFINED
+#ifndef _PTRDIFF_T_
+#define _PTRDIFF_T_
+#undef ptrdiff_t
+#ifdef _WIN64
+ typedef __int64 ptrdiff_t;
+#else
+ typedef int ptrdiff_t;
+#endif /* _WIN64 */
+#endif /* _PTRDIFF_T_ */
+#endif /* _PTRDIFF_T_DEFINED */
+
+#ifndef _WCHAR_T_DEFINED
+#define _WCHAR_T_DEFINED
+#ifndef __cplusplus
+ typedef unsigned short wchar_t;
+#endif /* C++ */
+#endif /* _WCHAR_T_DEFINED */
+
+#ifndef _WCTYPE_T_DEFINED
+#define _WCTYPE_T_DEFINED
+#ifndef _WINT_T
+#define _WINT_T
+ typedef unsigned short wint_t;
+ typedef unsigned short wctype_t;
+#endif /* _WINT_T */
+#endif /* _WCTYPE_T_DEFINED */
+
+/* 7.18.1.1 Exact-width integer types */
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+
+/* 7.18.1.2 Minimum-width integer types */
+typedef signed char int_least8_t;
+typedef unsigned char uint_least8_t;
+typedef short int_least16_t;
+typedef unsigned short uint_least16_t;
+typedef int int_least32_t;
+typedef unsigned uint_least32_t;
+typedef __int64 int_least64_t;
+typedef unsigned __int64 uint_least64_t;
+
+/* 7.18.1.3 Fastest minimum-width integer types
+ * Not actually guaranteed to be fastest for all purposes
+ * Here we use the exact-width types for 8 and 16-bit ints.
+ */
+typedef __int8 int_fast8_t;
+typedef unsigned __int8 uint_fast8_t;
+typedef __int16 int_fast16_t;
+typedef unsigned __int16 uint_fast16_t;
+typedef __int32 int_fast32_t;
+typedef unsigned __int32 uint_fast32_t;
+typedef __int64 int_fast64_t;
+typedef unsigned __int64 uint_fast64_t;
+
+/* 7.18.1.5 Greatest-width integer types */
+typedef __int64 intmax_t;
+typedef unsigned __int64 uintmax_t;
+
+/* 7.18.2 Limits of specified-width integer types */
+
+/* 7.18.2.1 Limits of exact-width integer types */
+#define INT8_MIN (-128)
+#define INT16_MIN (-32768)
+#define INT32_MIN (-2147483647 - 1)
+#define INT64_MIN (-9223372036854775807LL - 1)
+
+#define INT8_MAX 127
+#define INT16_MAX 32767
+#define INT32_MAX 2147483647
+#define INT64_MAX 9223372036854775807LL
+
+#define UINT8_MAX 255
+#define UINT16_MAX 65535
+#define UINT32_MAX 0xffffffffU /* 4294967295U */
+#define UINT64_MAX 0xffffffffffffffffULL /* 18446744073709551615ULL */
+
+/* 7.18.2.2 Limits of minimum-width integer types */
+#define INT_LEAST8_MIN INT8_MIN
+#define INT_LEAST16_MIN INT16_MIN
+#define INT_LEAST32_MIN INT32_MIN
+#define INT_LEAST64_MIN INT64_MIN
+
+#define INT_LEAST8_MAX INT8_MAX
+#define INT_LEAST16_MAX INT16_MAX
+#define INT_LEAST32_MAX INT32_MAX
+#define INT_LEAST64_MAX INT64_MAX
+
+#define UINT_LEAST8_MAX UINT8_MAX
+#define UINT_LEAST16_MAX UINT16_MAX
+#define UINT_LEAST32_MAX UINT32_MAX
+#define UINT_LEAST64_MAX UINT64_MAX
+
+/* 7.18.2.3 Limits of fastest minimum-width integer types */
+#define INT_FAST8_MIN INT8_MIN
+#define INT_FAST16_MIN INT16_MIN
+#define INT_FAST32_MIN INT32_MIN
+#define INT_FAST64_MIN INT64_MIN
+
+#define INT_FAST8_MAX INT8_MAX
+#define INT_FAST16_MAX INT16_MAX
+#define INT_FAST32_MAX INT32_MAX
+#define INT_FAST64_MAX INT64_MAX
+
+#define UINT_FAST8_MAX UINT8_MAX
+#define UINT_FAST16_MAX UINT16_MAX
+#define UINT_FAST32_MAX UINT32_MAX
+#define UINT_FAST64_MAX UINT64_MAX
+
+/* 7.18.2.4 Limits of integer types capable of holding
+ object pointers */
+#ifdef _WIN64
+#define INTPTR_MIN INT64_MIN
+#define INTPTR_MAX INT64_MAX
+#define UINTPTR_MAX UINT64_MAX
+#else
+#define INTPTR_MIN INT32_MIN
+#define INTPTR_MAX INT32_MAX
+#define UINTPTR_MAX UINT32_MAX
+#endif
+
+/* 7.18.2.5 Limits of greatest-width integer types */
+#define INTMAX_MIN INT64_MIN
+#define INTMAX_MAX INT64_MAX
+#define UINTMAX_MAX UINT64_MAX
+
+/* 7.18.3 Limits of other integer types */
+#ifdef _WIN64
+#define PTRDIFF_MIN INT64_MIN
+#define PTRDIFF_MAX INT64_MAX
+#else
+#define PTRDIFF_MIN INT32_MIN
+#define PTRDIFF_MAX INT32_MAX
+#endif
+
+#define SIG_ATOMIC_MIN INT32_MIN
+#define SIG_ATOMIC_MAX INT32_MAX
+
+#ifndef SIZE_MAX
+#ifdef _WIN64
+#define SIZE_MAX UINT64_MAX
+#else
+#define SIZE_MAX UINT32_MAX
+#endif
+#endif
+
+#ifndef WCHAR_MIN /* also in wchar.h */
+#define WCHAR_MIN 0U
+#define WCHAR_MAX 0xffffU
+#endif
+
+/*
+ * wint_t is unsigned short for compatibility with MS runtime
+ */
+#define WINT_MIN 0U
+#define WINT_MAX 0xffffU
+
+
+/* 7.18.4 Macros for integer constants */
+
+/* 7.18.4.1 Macros for minimum-width integer constants
+
+ Accoding to Douglas Gwyn :
+ "This spec was changed in ISO/IEC 9899:1999 TC1; in ISO/IEC
+ 9899:1999 as initially published, the expansion was required
+ to be an integer constant of precisely matching type, which
+ is impossible to accomplish for the shorter types on most
+ platforms, because C99 provides no standard way to designate
+ an integer constant with width less than that of type int.
+ TC1 changed this to require just an integer constant
+ *expression* with *promoted* type."
+
+ The trick used here is from Clive D W Feather.
+*/
+
+#define INT8_C(val) (INT_LEAST8_MAX-INT_LEAST8_MAX+(val))
+#define INT16_C(val) (INT_LEAST16_MAX-INT_LEAST16_MAX+(val))
+#define INT32_C(val) (INT_LEAST32_MAX-INT_LEAST32_MAX+(val))
+/* The 'trick' doesn't work in C89 for long long because, without
+ suffix, (val) will be evaluated as int, not intmax_t */
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val) (val)
+#define UINT16_C(val) (val)
+#define UINT32_C(val) (val##i32)
+#define UINT64_C(val) val##ui64
+
+/* 7.18.4.2 Macros for greatest-width integer constants */
+#define INTMAX_C(val) val##i64
+#define UINTMAX_C(val) val##ui64
+
+#endif
diff --git a/e502/libusb-1.0/libusb-1.0/os/darwin_usb.c b/e502/libusb-1.0/libusb-1.0/os/darwin_usb.c
new file mode 100644
index 0000000..19174b1
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/darwin_usb.c
@@ -0,0 +1,2009 @@
+/* -*- Mode: C; indent-tabs-mode:nil -*- */
+/*
+ * darwin backend for libusb 1.0
+ * Copyright © 2008-2014 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
+ */
+
+#include "config.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
+ #include
+#endif
+
+#include "darwin_usb.h"
+
+/* async event thread */
+static pthread_mutex_t libusb_darwin_at_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER;
+
+static clock_serv_t clock_realtime;
+static clock_serv_t clock_monotonic;
+
+static CFRunLoopRef libusb_darwin_acfl = NULL; /* event cf loop */
+static volatile int32_t initCount = 0;
+
+static usbi_mutex_t darwin_cached_devices_lock = PTHREAD_MUTEX_INITIALIZER;
+static struct list_head darwin_cached_devices = {&darwin_cached_devices, &darwin_cached_devices};
+
+#define DARWIN_CACHED_DEVICE(a) ((struct darwin_cached_device *) (((struct darwin_device_priv *)((a)->os_priv))->dev))
+
+/* async event thread */
+static pthread_t libusb_darwin_at;
+
+static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian);
+static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface);
+static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface);
+static int darwin_reset_device(struct libusb_device_handle *dev_handle);
+static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0);
+
+static int darwin_scan_devices(struct libusb_context *ctx);
+static int process_new_device (struct libusb_context *ctx, io_service_t service);
+
+#if defined(ENABLE_LOGGING)
+static const char *darwin_error_str (int result) {
+ static char string_buffer[50];
+ switch (result) {
+ case kIOReturnSuccess:
+ return "no error";
+ case kIOReturnNotOpen:
+ return "device not opened for exclusive access";
+ case kIOReturnNoDevice:
+ return "no connection to an IOService";
+ case kIOUSBNoAsyncPortErr:
+ return "no async port has been opened for interface";
+ case kIOReturnExclusiveAccess:
+ return "another process has device opened for exclusive access";
+ case kIOUSBPipeStalled:
+ return "pipe is stalled";
+ case kIOReturnError:
+ return "could not establish a connection to the Darwin kernel";
+ case kIOUSBTransactionTimeout:
+ return "transaction timed out";
+ case kIOReturnBadArgument:
+ return "invalid argument";
+ case kIOReturnAborted:
+ return "transaction aborted";
+ case kIOReturnNotResponding:
+ return "device not responding";
+ case kIOReturnOverrun:
+ return "data overrun";
+ case kIOReturnCannotWire:
+ return "physical memory can not be wired down";
+ case kIOReturnNoResources:
+ return "out of resources";
+ case kIOUSBHighSpeedSplitError:
+ return "high speed split error";
+ default:
+ snprintf(string_buffer, sizeof(string_buffer), "unknown error (0x%x)", result);
+ return string_buffer;
+ }
+}
+#endif
+
+static int darwin_to_libusb (int result) {
+ switch (result) {
+ case kIOReturnUnderrun:
+ case kIOReturnSuccess:
+ return LIBUSB_SUCCESS;
+ case kIOReturnNotOpen:
+ case kIOReturnNoDevice:
+ return LIBUSB_ERROR_NO_DEVICE;
+ case kIOReturnExclusiveAccess:
+ return LIBUSB_ERROR_ACCESS;
+ case kIOUSBPipeStalled:
+ return LIBUSB_ERROR_PIPE;
+ case kIOReturnBadArgument:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ case kIOUSBTransactionTimeout:
+ return LIBUSB_ERROR_TIMEOUT;
+ case kIOReturnNotResponding:
+ case kIOReturnAborted:
+ case kIOReturnError:
+ case kIOUSBNoAsyncPortErr:
+ default:
+ return LIBUSB_ERROR_OTHER;
+ }
+}
+
+/* this function must be called with the darwin_cached_devices_lock held */
+static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev) {
+ cached_dev->refcount--;
+ /* free the device and remove it from the cache */
+ if (0 == cached_dev->refcount) {
+ list_del(&cached_dev->list);
+
+ (*(cached_dev->device))->Release(cached_dev->device);
+ free (cached_dev);
+ }
+}
+
+static void darwin_ref_cached_device(struct darwin_cached_device *cached_dev) {
+ cached_dev->refcount++;
+}
+
+static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, uint8_t *pipep, uint8_t *ifcp, struct darwin_interface **interface_out) {
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+
+ /* current interface */
+ struct darwin_interface *cInterface;
+
+ int8_t i, iface;
+
+ usbi_dbg ("converting ep address 0x%02x to pipeRef and interface", ep);
+
+ for (iface = 0 ; iface < USB_MAXINTERFACES ; iface++) {
+ cInterface = &priv->interfaces[iface];
+
+ if (dev_handle->claimed_interfaces & (1 << iface)) {
+ for (i = 0 ; i < cInterface->num_endpoints ; i++) {
+ if (cInterface->endpoint_addrs[i] == ep) {
+ *pipep = i + 1;
+
+ if (ifcp)
+ *ifcp = iface;
+
+ if (interface_out)
+ *interface_out = cInterface;
+
+ usbi_dbg ("pipe %d on interface %d matches", *pipep, iface);
+ return 0;
+ }
+ }
+ }
+ }
+
+ /* No pipe found with the correct endpoint address */
+ usbi_warn (HANDLE_CTX(dev_handle), "no pipeRef found with endpoint address 0x%02x.", ep);
+
+ return LIBUSB_ERROR_NOT_FOUND;
+}
+
+static int usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 location) {
+ CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
+
+ if (!matchingDict)
+ return kIOReturnError;
+
+ if (location) {
+ CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ if (propertyMatchDict) {
+ /* there are no unsigned CFNumber types so treat the value as signed. the os seems to do this
+ internally (CFNumberType of locationID is 3) */
+ CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location);
+
+ CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF);
+ /* release our reference to the CFNumber (CFDictionarySetValue retains it) */
+ CFRelease (locationCF);
+
+ CFDictionarySetValue (matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict);
+ /* release out reference to the CFMutableDictionaryRef (CFDictionarySetValue retains it) */
+ CFRelease (propertyMatchDict);
+ }
+ /* else we can still proceed as long as the caller accounts for the possibility of other devices in the iterator */
+ }
+
+ return IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, deviceIterator);
+}
+
+/* Returns 1 on success, 0 on failure. */
+static int get_ioregistry_value_number (io_service_t service, CFStringRef property, CFNumberType type, void *p) {
+ CFTypeRef cfNumber = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0);
+ int ret = 0;
+
+ if (cfNumber) {
+ if (CFGetTypeID(cfNumber) == CFNumberGetTypeID()) {
+ ret = CFNumberGetValue(cfNumber, type, p);
+ }
+
+ CFRelease (cfNumber);
+ }
+
+ return ret;
+}
+
+static usb_device_t **darwin_device_from_service (io_service_t service)
+{
+ io_cf_plugin_ref_t *plugInInterface = NULL;
+ usb_device_t **device;
+ kern_return_t result;
+ SInt32 score;
+
+ result = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID, &plugInInterface,
+ &score);
+
+ if (kIOReturnSuccess != result || !plugInInterface) {
+ usbi_dbg ("could not set up plugin for service: %s\n", darwin_error_str (result));
+ return NULL;
+ }
+
+ (void)(*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(DeviceInterfaceID),
+ (LPVOID)&device);
+ /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */
+ (*plugInInterface)->Release (plugInInterface);
+
+ return device;
+}
+
+static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
+ struct libusb_context *ctx;
+ io_service_t service;
+
+ usbi_mutex_lock(&active_contexts_lock);
+
+ while ((service = IOIteratorNext(add_devices))) {
+ /* add this device to each active context's device list */
+ list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+ process_new_device (ctx, service);;
+ }
+
+ IOObjectRelease(service);
+ }
+
+ usbi_mutex_unlock(&active_contexts_lock);
+}
+
+static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
+ struct libusb_device *dev = NULL;
+ struct libusb_context *ctx;
+
+ io_service_t device;
+ UInt64 session;
+ int ret;
+
+ usbi_mutex_lock(&active_contexts_lock);
+
+ while ((device = IOIteratorNext (rem_devices)) != 0) {
+ /* get the location from the i/o registry */
+ ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session);
+ IOObjectRelease (device);
+ if (!ret)
+ continue;
+
+ list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+ usbi_dbg ("notifying context %p of device disconnect", ctx);
+
+ dev = usbi_get_device_by_session_id(ctx, (unsigned long) session);
+ if (dev) {
+ /* signal the core that this device has been disconnected. the core will tear down this device
+ when the reference count reaches 0 */
+ usbi_disconnect_device(dev);
+ libusb_unref_device(dev);
+ }
+ }
+ }
+
+ usbi_mutex_unlock(&active_contexts_lock);
+}
+
+static void darwin_hotplug_poll (void)
+{
+ /* not sure if 5 seconds will be too long/short but it should work ok */
+ mach_timespec_t timeout = {.tv_sec = 5, .tv_nsec = 0};
+
+ /* since a kernel thread may nodify the IOInterators used for
+ * hotplug notidication we can't just clear the iterators.
+ * instead just wait until all IOService providers are quiet */
+ (void) IOKitWaitQuiet (kIOMasterPortDefault, &timeout);
+}
+
+static void darwin_clear_iterator (io_iterator_t iter) {
+ io_service_t device;
+
+ while ((device = IOIteratorNext (iter)) != 0)
+ IOObjectRelease (device);
+}
+
+static void *darwin_event_thread_main (void *arg0) {
+ IOReturn kresult;
+ struct libusb_context *ctx = (struct libusb_context *)arg0;
+ CFRunLoopRef runloop;
+
+ /* Set this thread's name, so it can be seen in the debugger
+ and crash reports. */
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
+ pthread_setname_np ("org.libusb.device-hotplug");
+
+ /* Tell the Objective-C garbage collector about this thread.
+ This is required because, unlike NSThreads, pthreads are
+ not automatically registered. Although we don't use
+ Objective-C, we use CoreFoundation, which does. */
+ objc_registerThreadWithCollector();
+#endif
+
+ /* hotplug (device arrival/removal) sources */
+ CFRunLoopSourceRef libusb_notification_cfsource;
+ io_notification_port_t libusb_notification_port;
+ io_iterator_t libusb_rem_device_iterator;
+ io_iterator_t libusb_add_device_iterator;
+
+ usbi_dbg ("creating hotplug event source");
+
+ runloop = CFRunLoopGetCurrent ();
+ CFRetain (runloop);
+
+ /* add the notification port to the run loop */
+ libusb_notification_port = IONotificationPortCreate (kIOMasterPortDefault);
+ libusb_notification_cfsource = IONotificationPortGetRunLoopSource (libusb_notification_port);
+ CFRunLoopAddSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode);
+
+ /* create notifications for removed devices */
+ kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification,
+ IOServiceMatching(kIOUSBDeviceClassName),
+ darwin_devices_detached,
+ ctx, &libusb_rem_device_iterator);
+
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
+
+ pthread_exit (NULL);
+ }
+
+ /* create notifications for attached devices */
+ kresult = IOServiceAddMatchingNotification(libusb_notification_port, kIOFirstMatchNotification,
+ IOServiceMatching(kIOUSBDeviceClassName),
+ darwin_devices_attached,
+ ctx, &libusb_add_device_iterator);
+
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
+
+ pthread_exit (NULL);
+ }
+
+ /* arm notifiers */
+ darwin_clear_iterator (libusb_rem_device_iterator);
+ darwin_clear_iterator (libusb_add_device_iterator);
+
+ usbi_dbg ("darwin event thread ready to receive events");
+
+ /* signal the main thread that the hotplug runloop has been created. */
+ pthread_mutex_lock (&libusb_darwin_at_mutex);
+ libusb_darwin_acfl = runloop;
+ pthread_cond_signal (&libusb_darwin_at_cond);
+ pthread_mutex_unlock (&libusb_darwin_at_mutex);
+
+ /* run the runloop */
+ CFRunLoopRun();
+
+ usbi_dbg ("darwin event thread exiting");
+
+ /* remove the notification cfsource */
+ CFRunLoopRemoveSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode);
+
+ /* delete notification port */
+ IONotificationPortDestroy (libusb_notification_port);
+
+ /* delete iterators */
+ IOObjectRelease (libusb_rem_device_iterator);
+ IOObjectRelease (libusb_add_device_iterator);
+
+ CFRelease (runloop);
+
+ libusb_darwin_acfl = NULL;
+
+ pthread_exit (NULL);
+}
+
+/* cleanup function to destroy cached devices */
+static void __attribute__((destructor)) _darwin_finalize(void) {
+ struct darwin_cached_device *dev, *next;
+
+ usbi_mutex_lock(&darwin_cached_devices_lock);
+ list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) {
+ darwin_deref_cached_device(dev);
+ }
+ usbi_mutex_unlock(&darwin_cached_devices_lock);
+}
+
+static int darwin_init(struct libusb_context *ctx) {
+ host_name_port_t host_self;
+ int rc;
+
+ rc = darwin_scan_devices (ctx);
+ if (LIBUSB_SUCCESS != rc) {
+ return rc;
+ }
+
+ if (OSAtomicIncrement32Barrier(&initCount) == 1) {
+ /* create the clocks that will be used */
+
+ host_self = mach_host_self();
+ host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime);
+ host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic);
+ mach_port_deallocate(mach_task_self(), host_self);
+
+ pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx);
+
+ pthread_mutex_lock (&libusb_darwin_at_mutex);
+ while (!libusb_darwin_acfl)
+ pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex);
+ pthread_mutex_unlock (&libusb_darwin_at_mutex);
+ }
+
+ return rc;
+}
+
+static void darwin_exit (void) {
+ if (OSAtomicDecrement32Barrier(&initCount) == 0) {
+ mach_port_deallocate(mach_task_self(), clock_realtime);
+ mach_port_deallocate(mach_task_self(), clock_monotonic);
+
+ /* stop the event runloop and wait for the thread to terminate. */
+ CFRunLoopStop (libusb_darwin_acfl);
+ pthread_join (libusb_darwin_at, NULL);
+ }
+}
+
+static int darwin_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) {
+ struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
+
+ /* return cached copy */
+ memmove (buffer, &(priv->dev_descriptor), DEVICE_DESC_LENGTH);
+
+ *host_endian = 0;
+
+ return 0;
+}
+
+static int get_configuration_index (struct libusb_device *dev, int config_value) {
+ struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
+ UInt8 i, numConfig;
+ IOUSBConfigurationDescriptorPtr desc;
+ IOReturn kresult;
+
+ /* is there a simpler way to determine the index? */
+ kresult = (*(priv->device))->GetNumberOfConfigurations (priv->device, &numConfig);
+ if (kresult != kIOReturnSuccess)
+ return darwin_to_libusb (kresult);
+
+ for (i = 0 ; i < numConfig ; i++) {
+ (*(priv->device))->GetConfigurationDescriptorPtr (priv->device, i, &desc);
+
+ if (desc->bConfigurationValue == config_value)
+ return i;
+ }
+
+ /* configuration not found */
+ return LIBUSB_ERROR_NOT_FOUND;
+}
+
+static int darwin_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) {
+ struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
+ int config_index;
+
+ if (0 == priv->active_config)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ config_index = get_configuration_index (dev, priv->active_config);
+ if (config_index < 0)
+ return config_index;
+
+ return darwin_get_config_descriptor (dev, config_index, buffer, len, host_endian);
+}
+
+static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) {
+ struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
+ IOUSBConfigurationDescriptorPtr desc;
+ IOReturn kresult;
+ int ret;
+
+ if (!priv || !priv->device)
+ return LIBUSB_ERROR_OTHER;
+
+ kresult = (*priv->device)->GetConfigurationDescriptorPtr (priv->device, config_index, &desc);
+ if (kresult == kIOReturnSuccess) {
+ /* copy descriptor */
+ if (libusb_le16_to_cpu(desc->wTotalLength) < len)
+ len = libusb_le16_to_cpu(desc->wTotalLength);
+
+ memmove (buffer, desc, len);
+
+ /* GetConfigurationDescriptorPtr returns the descriptor in USB bus order */
+ *host_endian = 0;
+ }
+
+ ret = darwin_to_libusb (kresult);
+ if (ret != LIBUSB_SUCCESS)
+ return ret;
+
+ return (int) len;
+}
+
+/* check whether the os has configured the device */
+static int darwin_check_configuration (struct libusb_context *ctx, struct darwin_cached_device *dev) {
+ usb_device_t **darwin_device = dev->device;
+
+ IOUSBConfigurationDescriptorPtr configDesc;
+ IOUSBFindInterfaceRequest request;
+ kern_return_t kresult;
+ io_iterator_t interface_iterator;
+ io_service_t firstInterface;
+
+ if (dev->dev_descriptor.bNumConfigurations < 1) {
+ usbi_err (ctx, "device has no configurations");
+ return LIBUSB_ERROR_OTHER; /* no configurations at this speed so we can't use it */
+ }
+
+ /* find the first configuration */
+ kresult = (*darwin_device)->GetConfigurationDescriptorPtr (darwin_device, 0, &configDesc);
+ dev->first_config = (kIOReturnSuccess == kresult) ? configDesc->bConfigurationValue : 1;
+
+ /* check if the device is already configured. there is probably a better way than iterating over the
+ to accomplish this (the trick is we need to avoid a call to GetConfigurations since buggy devices
+ might lock up on the device request) */
+
+ /* Setup the Interface Request */
+ request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+ request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+
+ kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator);
+ if (kresult)
+ return darwin_to_libusb (kresult);
+
+ /* iterate once */
+ firstInterface = IOIteratorNext(interface_iterator);
+
+ /* done with the interface iterator */
+ IOObjectRelease(interface_iterator);
+
+ if (firstInterface) {
+ IOObjectRelease (firstInterface);
+
+ /* device is configured */
+ if (dev->dev_descriptor.bNumConfigurations == 1)
+ /* to avoid problems with some devices get the configurations value from the configuration descriptor */
+ dev->active_config = dev->first_config;
+ else
+ /* devices with more than one configuration should work with GetConfiguration */
+ (*darwin_device)->GetConfiguration (darwin_device, &dev->active_config);
+ } else
+ /* not configured */
+ dev->active_config = 0;
+
+ usbi_dbg ("active config: %u, first config: %u", dev->active_config, dev->first_config);
+
+ return 0;
+}
+
+static int darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) {
+ IOUSBDevRequestTO req;
+
+ memset (buffer, 0, buffer_size);
+
+ /* Set up request for descriptor/ */
+ req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+ req.bRequest = kUSBRqGetDescriptor;
+ req.wValue = desc << 8;
+ req.wIndex = desc_index;
+ req.wLength = buffer_size;
+ req.pData = buffer;
+ req.noDataTimeout = 20;
+ req.completionTimeout = 100;
+
+ return (*device)->DeviceRequestTO (device, &req);
+}
+
+static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct darwin_cached_device *dev) {
+ usb_device_t **device = dev->device;
+ int retries = 1, delay = 30000;
+ int unsuspended = 0, try_unsuspend = 1, try_reconfigure = 1;
+ int is_open = 0;
+ int ret = 0, ret2;
+ UInt8 bDeviceClass;
+ UInt16 idProduct, idVendor;
+
+ dev->can_enumerate = 0;
+
+ (*device)->GetDeviceClass (device, &bDeviceClass);
+ (*device)->GetDeviceProduct (device, &idProduct);
+ (*device)->GetDeviceVendor (device, &idVendor);
+
+ /* According to Apple's documentation the device must be open for DeviceRequest but we may not be able to open some
+ * devices and Apple's USB Prober doesn't bother to open the device before issuing a descriptor request. Still,
+ * to follow the spec as closely as possible, try opening the device */
+ is_open = ((*device)->USBDeviceOpenSeize(device) == kIOReturnSuccess);
+
+ do {
+ /**** retrieve device descriptor ****/
+ ret = darwin_request_descriptor (device, kUSBDeviceDesc, 0, &dev->dev_descriptor, sizeof(dev->dev_descriptor));
+
+ if (kIOReturnOverrun == ret && kUSBDeviceDesc == dev->dev_descriptor.bDescriptorType)
+ /* received an overrun error but we still received a device descriptor */
+ ret = kIOReturnSuccess;
+
+ if (kIOUSBVendorIDAppleComputer == idVendor) {
+ /* NTH: don't bother retrying or unsuspending Apple devices */
+ break;
+ }
+
+ if (kIOReturnSuccess == ret && (0 == dev->dev_descriptor.bNumConfigurations ||
+ 0 == dev->dev_descriptor.bcdUSB)) {
+ /* work around for incorrectly configured devices */
+ if (try_reconfigure && is_open) {
+ usbi_dbg("descriptor appears to be invalid. resetting configuration before trying again...");
+
+ /* set the first configuration */
+ (*device)->SetConfiguration(device, 1);
+
+ /* don't try to reconfigure again */
+ try_reconfigure = 0;
+ }
+
+ ret = kIOUSBPipeStalled;
+ }
+
+ if (kIOReturnSuccess != ret && is_open && try_unsuspend) {
+ /* device may be suspended. unsuspend it and try again */
+#if DeviceVersion >= 320
+ UInt32 info = 0;
+
+ /* IOUSBFamily 320+ provides a way to detect device suspension but earlier versions do not */
+ (void)(*device)->GetUSBDeviceInformation (device, &info);
+
+ /* note that the device was suspended */
+ if (info & (1 << kUSBInformationDeviceIsSuspendedBit) || 0 == info)
+ try_unsuspend = 1;
+#endif
+
+ if (try_unsuspend) {
+ /* try to unsuspend the device */
+ ret2 = (*device)->USBDeviceSuspend (device, 0);
+ if (kIOReturnSuccess != ret2) {
+ /* prevent log spew from poorly behaving devices. this indicates the
+ os actually had trouble communicating with the device */
+ usbi_dbg("could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2));
+ } else
+ unsuspended = 1;
+
+ try_unsuspend = 0;
+ }
+ }
+
+ if (kIOReturnSuccess != ret) {
+ usbi_dbg("kernel responded with code: 0x%08x. sleeping for %d ms before trying again", ret, delay/1000);
+ /* sleep for a little while before trying again */
+ usleep (delay);
+ }
+ } while (kIOReturnSuccess != ret && retries--);
+
+ if (unsuspended)
+ /* resuspend the device */
+ (void)(*device)->USBDeviceSuspend (device, 1);
+
+ if (is_open)
+ (void) (*device)->USBDeviceClose (device);
+
+ if (ret != kIOReturnSuccess) {
+ /* a debug message was already printed out for this error */
+ if (LIBUSB_CLASS_HUB == bDeviceClass)
+ usbi_dbg ("could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
+ idVendor, idProduct, darwin_error_str (ret), ret);
+ else
+ usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
+ idVendor, idProduct, darwin_error_str (ret), ret);
+ return darwin_to_libusb (ret);
+ }
+
+ /* catch buggy hubs (which appear to be virtual). Apple's own USB prober has problems with these devices. */
+ if (libusb_le16_to_cpu (dev->dev_descriptor.idProduct) != idProduct) {
+ /* not a valid device */
+ usbi_warn (ctx, "idProduct from iokit (%04x) does not match idProduct in descriptor (%04x). skipping device",
+ idProduct, libusb_le16_to_cpu (dev->dev_descriptor.idProduct));
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ usbi_dbg ("cached device descriptor:");
+ usbi_dbg (" bDescriptorType: 0x%02x", dev->dev_descriptor.bDescriptorType);
+ usbi_dbg (" bcdUSB: 0x%04x", dev->dev_descriptor.bcdUSB);
+ usbi_dbg (" bDeviceClass: 0x%02x", dev->dev_descriptor.bDeviceClass);
+ usbi_dbg (" bDeviceSubClass: 0x%02x", dev->dev_descriptor.bDeviceSubClass);
+ usbi_dbg (" bDeviceProtocol: 0x%02x", dev->dev_descriptor.bDeviceProtocol);
+ usbi_dbg (" bMaxPacketSize0: 0x%02x", dev->dev_descriptor.bMaxPacketSize0);
+ usbi_dbg (" idVendor: 0x%04x", dev->dev_descriptor.idVendor);
+ usbi_dbg (" idProduct: 0x%04x", dev->dev_descriptor.idProduct);
+ usbi_dbg (" bcdDevice: 0x%04x", dev->dev_descriptor.bcdDevice);
+ usbi_dbg (" iManufacturer: 0x%02x", dev->dev_descriptor.iManufacturer);
+ usbi_dbg (" iProduct: 0x%02x", dev->dev_descriptor.iProduct);
+ usbi_dbg (" iSerialNumber: 0x%02x", dev->dev_descriptor.iSerialNumber);
+ usbi_dbg (" bNumConfigurations: 0x%02x", dev->dev_descriptor.bNumConfigurations);
+
+ dev->can_enumerate = 1;
+
+ return LIBUSB_SUCCESS;
+}
+
+static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t service,
+ struct darwin_cached_device **cached_out) {
+ struct darwin_cached_device *new_device;
+ UInt64 sessionID = 0, parent_sessionID = 0;
+ int ret = LIBUSB_SUCCESS;
+ usb_device_t **device;
+ io_service_t parent;
+ kern_return_t result;
+ UInt8 port = 0;
+
+ /* get some info from the io registry */
+ (void) get_ioregistry_value_number (service, CFSTR("sessionID"), kCFNumberSInt64Type, &sessionID);
+ (void) get_ioregistry_value_number (service, CFSTR("PortNum"), kCFNumberSInt8Type, &port);
+
+ usbi_dbg("finding cached device for sessionID 0x%" PRIx64, sessionID);
+
+ result = IORegistryEntryGetParentEntry (service, kIOUSBPlane, &parent);
+
+ if (kIOReturnSuccess == result) {
+ (void) get_ioregistry_value_number (parent, CFSTR("sessionID"), kCFNumberSInt64Type, &parent_sessionID);
+ IOObjectRelease(parent);
+ }
+
+ usbi_mutex_lock(&darwin_cached_devices_lock);
+ do {
+ *cached_out = NULL;
+
+ list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) {
+ usbi_dbg("matching sessionID 0x%" PRIx64 " against cached device with sessionID 0x%" PRIx64, sessionID, new_device->session);
+ if (new_device->session == sessionID) {
+ usbi_dbg("using cached device for device");
+ *cached_out = new_device;
+ break;
+ }
+ }
+
+ if (*cached_out)
+ break;
+
+ usbi_dbg("caching new device with sessionID 0x%" PRIx64, sessionID);
+
+ device = darwin_device_from_service (service);
+ if (!device) {
+ ret = LIBUSB_ERROR_NO_DEVICE;
+ break;
+ }
+
+ new_device = calloc (1, sizeof (*new_device));
+ if (!new_device) {
+ ret = LIBUSB_ERROR_NO_MEM;
+ break;
+ }
+
+ /* add this device to the cached device list */
+ list_add(&new_device->list, &darwin_cached_devices);
+
+ (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&new_device->address);
+
+ /* keep a reference to this device */
+ darwin_ref_cached_device(new_device);
+
+ new_device->device = device;
+ new_device->session = sessionID;
+ (*device)->GetLocationID (device, &new_device->location);
+ new_device->port = port;
+ new_device->parent_session = parent_sessionID;
+
+ /* cache the device descriptor */
+ ret = darwin_cache_device_descriptor(ctx, new_device);
+ if (ret)
+ break;
+
+ if (new_device->can_enumerate) {
+ snprintf(new_device->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", new_device->address,
+ new_device->dev_descriptor.idVendor, new_device->dev_descriptor.idProduct,
+ new_device->dev_descriptor.bDeviceClass, new_device->dev_descriptor.bDeviceSubClass);
+ }
+ } while (0);
+
+ usbi_mutex_unlock(&darwin_cached_devices_lock);
+
+ /* keep track of devices regardless of if we successfully enumerate them to
+ prevent them from being enumerated multiple times */
+
+ *cached_out = new_device;
+
+ return ret;
+}
+
+static int process_new_device (struct libusb_context *ctx, io_service_t service) {
+ struct darwin_device_priv *priv;
+ struct libusb_device *dev = NULL;
+ struct darwin_cached_device *cached_device;
+ UInt8 devSpeed;
+ int ret = 0;
+
+ do {
+ ret = darwin_get_cached_device (ctx, service, &cached_device);
+
+ if (ret < 0 || !cached_device->can_enumerate) {
+ return ret;
+ }
+
+ /* check current active configuration (and cache the first configuration value--
+ which may be used by claim_interface) */
+ ret = darwin_check_configuration (ctx, cached_device);
+ if (ret)
+ break;
+
+ usbi_dbg ("allocating new device in context %p for with session 0x%" PRIx64,
+ ctx, cached_device->session);
+
+ dev = usbi_alloc_device(ctx, (unsigned long) cached_device->session);
+ if (!dev) {
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ priv = (struct darwin_device_priv *)dev->os_priv;
+
+ priv->dev = cached_device;
+ darwin_ref_cached_device (priv->dev);
+
+ if (cached_device->parent_session > 0) {
+ dev->parent_dev = usbi_get_device_by_session_id (ctx, (unsigned long) cached_device->parent_session);
+ } else {
+ dev->parent_dev = NULL;
+ }
+ dev->port_number = cached_device->port;
+ dev->bus_number = cached_device->location >> 24;
+ dev->device_address = cached_device->address;
+
+ (*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed);
+
+ switch (devSpeed) {
+ case kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break;
+ case kUSBDeviceSpeedFull: dev->speed = LIBUSB_SPEED_FULL; break;
+ case kUSBDeviceSpeedHigh: dev->speed = LIBUSB_SPEED_HIGH; break;
+#if DeviceVersion >= 500
+ case kUSBDeviceSpeedSuper: dev->speed = LIBUSB_SPEED_SUPER; break;
+#endif
+ default:
+ usbi_warn (ctx, "Got unknown device speed %d", devSpeed);
+ }
+
+ ret = usbi_sanitize_device (dev);
+ if (ret < 0)
+ break;
+
+ usbi_dbg ("found device with address %d port = %d parent = %p at %p", dev->device_address,
+ dev->port_number, (void *) dev->parent_dev, priv->dev->sys_path);
+ } while (0);
+
+ if (0 == ret) {
+ usbi_connect_device (dev);
+ } else {
+ libusb_unref_device (dev);
+ }
+
+ return ret;
+}
+
+static int darwin_scan_devices(struct libusb_context *ctx) {
+ io_iterator_t deviceIterator;
+ io_service_t service;
+ kern_return_t kresult;
+
+ kresult = usb_setup_device_iterator (&deviceIterator, 0);
+ if (kresult != kIOReturnSuccess)
+ return darwin_to_libusb (kresult);
+
+ while ((service = IOIteratorNext (deviceIterator))) {
+ (void) process_new_device (ctx, service);
+
+ IOObjectRelease(service);
+ }
+
+ IOObjectRelease(deviceIterator);
+
+ return 0;
+}
+
+static int darwin_open (struct libusb_device_handle *dev_handle) {
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ IOReturn kresult;
+
+ if (0 == dpriv->open_count) {
+ /* try to open the device */
+ kresult = (*(dpriv->device))->USBDeviceOpenSeize (dpriv->device);
+ if (kresult != kIOReturnSuccess) {
+ usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceOpen: %s", darwin_error_str(kresult));
+
+ if (kIOReturnExclusiveAccess != kresult) {
+ return darwin_to_libusb (kresult);
+ }
+
+ /* it is possible to perform some actions on a device that is not open so do not return an error */
+ priv->is_open = 0;
+ } else {
+ priv->is_open = 1;
+ }
+
+ /* create async event source */
+ kresult = (*(dpriv->device))->CreateDeviceAsyncEventSource (dpriv->device, &priv->cfSource);
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (HANDLE_CTX (dev_handle), "CreateDeviceAsyncEventSource: %s", darwin_error_str(kresult));
+
+ if (priv->is_open) {
+ (*(dpriv->device))->USBDeviceClose (dpriv->device);
+ }
+
+ priv->is_open = 0;
+
+ return darwin_to_libusb (kresult);
+ }
+
+ CFRetain (libusb_darwin_acfl);
+
+ /* add the cfSource to the aync run loop */
+ CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes);
+ }
+
+ /* device opened successfully */
+ dpriv->open_count++;
+
+ /* create a file descriptor for notifications */
+ pipe (priv->fds);
+
+ /* set the pipe to be non-blocking */
+ fcntl (priv->fds[1], F_SETFD, O_NONBLOCK);
+
+ usbi_add_pollfd(HANDLE_CTX(dev_handle), priv->fds[0], POLLIN);
+
+ usbi_dbg ("device open for access");
+
+ return 0;
+}
+
+static void darwin_close (struct libusb_device_handle *dev_handle) {
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ IOReturn kresult;
+ int i;
+
+ if (dpriv->open_count == 0) {
+ /* something is probably very wrong if this is the case */
+ usbi_err (HANDLE_CTX (dev_handle), "Close called on a device that was not open!\n");
+ return;
+ }
+
+ dpriv->open_count--;
+
+ /* make sure all interfaces are released */
+ for (i = 0 ; i < USB_MAXINTERFACES ; i++)
+ if (dev_handle->claimed_interfaces & (1 << i))
+ libusb_release_interface (dev_handle, i);
+
+ if (0 == dpriv->open_count) {
+ /* delete the device's async event source */
+ if (priv->cfSource) {
+ CFRunLoopRemoveSource (libusb_darwin_acfl, priv->cfSource, kCFRunLoopDefaultMode);
+ CFRelease (priv->cfSource);
+ priv->cfSource = NULL;
+ CFRelease (libusb_darwin_acfl);
+ }
+
+ if (priv->is_open) {
+ /* close the device */
+ kresult = (*(dpriv->device))->USBDeviceClose(dpriv->device);
+ if (kresult) {
+ /* Log the fact that we had a problem closing the file, however failing a
+ * close isn't really an error, so return success anyway */
+ usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceClose: %s", darwin_error_str(kresult));
+ }
+ }
+ }
+
+ /* file descriptors are maintained per-instance */
+ usbi_remove_pollfd (HANDLE_CTX (dev_handle), priv->fds[0]);
+ close (priv->fds[1]);
+ close (priv->fds[0]);
+
+ priv->fds[0] = priv->fds[1] = -1;
+}
+
+static int darwin_get_configuration(struct libusb_device_handle *dev_handle, int *config) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+
+ *config = (int) dpriv->active_config;
+
+ return 0;
+}
+
+static int darwin_set_configuration(struct libusb_device_handle *dev_handle, int config) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ IOReturn kresult;
+ int i;
+
+ /* Setting configuration will invalidate the interface, so we need
+ to reclaim it. First, dispose of existing interfaces, if any. */
+ for (i = 0 ; i < USB_MAXINTERFACES ; i++)
+ if (dev_handle->claimed_interfaces & (1 << i))
+ darwin_release_interface (dev_handle, i);
+
+ kresult = (*(dpriv->device))->SetConfiguration (dpriv->device, config);
+ if (kresult != kIOReturnSuccess)
+ return darwin_to_libusb (kresult);
+
+ /* Reclaim any interfaces. */
+ for (i = 0 ; i < USB_MAXINTERFACES ; i++)
+ if (dev_handle->claimed_interfaces & (1 << i))
+ darwin_claim_interface (dev_handle, i);
+
+ dpriv->active_config = config;
+
+ return 0;
+}
+
+static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_service_t *usbInterfacep) {
+ IOUSBFindInterfaceRequest request;
+ kern_return_t kresult;
+ io_iterator_t interface_iterator;
+ UInt8 bInterfaceNumber;
+ int ret;
+
+ *usbInterfacep = IO_OBJECT_NULL;
+
+ /* Setup the Interface Request */
+ request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+ request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+
+ kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator);
+ if (kresult)
+ return kresult;
+
+ while ((*usbInterfacep = IOIteratorNext(interface_iterator))) {
+ /* find the interface number */
+ ret = get_ioregistry_value_number (*usbInterfacep, CFSTR("bInterfaceNumber"), kCFNumberSInt8Type,
+ &bInterfaceNumber);
+
+ if (ret && bInterfaceNumber == ifc) {
+ break;
+ }
+
+ (void) IOObjectRelease (*usbInterfacep);
+ }
+
+ /* done with the interface iterator */
+ IOObjectRelease(interface_iterator);
+
+ return 0;
+}
+
+static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+
+ /* current interface */
+ struct darwin_interface *cInterface = &priv->interfaces[iface];
+
+ kern_return_t kresult;
+
+ u_int8_t numep, direction, number;
+ u_int8_t dont_care1, dont_care3;
+ u_int16_t dont_care2;
+ int i;
+
+ usbi_dbg ("building table of endpoints.");
+
+ /* retrieve the total number of endpoints on this interface */
+ kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep);
+ if (kresult) {
+ usbi_err (HANDLE_CTX (dev_handle), "can't get number of endpoints for interface: %s", darwin_error_str(kresult));
+ return darwin_to_libusb (kresult);
+ }
+
+ /* iterate through pipe references */
+ for (i = 1 ; i <= numep ; i++) {
+ kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1,
+ &dont_care2, &dont_care3);
+
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (HANDLE_CTX (dev_handle), "error getting pipe information for pipe %d: %s", i, darwin_error_str(kresult));
+
+ return darwin_to_libusb (kresult);
+ }
+
+ usbi_dbg ("interface: %i pipe %i: dir: %i number: %i", iface, i, direction, number);
+
+ cInterface->endpoint_addrs[i - 1] = (((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK));
+ }
+
+ cInterface->num_endpoints = numep;
+
+ return 0;
+}
+
+static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+ io_service_t usbInterface = IO_OBJECT_NULL;
+ IOReturn kresult;
+ IOCFPlugInInterface **plugInInterface = NULL;
+ SInt32 score;
+
+ /* current interface */
+ struct darwin_interface *cInterface = &priv->interfaces[iface];
+
+ kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
+ if (kresult != kIOReturnSuccess)
+ return darwin_to_libusb (kresult);
+
+ /* make sure we have an interface */
+ if (!usbInterface && dpriv->first_config != 0) {
+ usbi_info (HANDLE_CTX (dev_handle), "no interface found; setting configuration: %d", dpriv->first_config);
+
+ /* set the configuration */
+ kresult = darwin_set_configuration (dev_handle, dpriv->first_config);
+ if (kresult != LIBUSB_SUCCESS) {
+ usbi_err (HANDLE_CTX (dev_handle), "could not set configuration");
+ return kresult;
+ }
+
+ kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
+ if (kresult) {
+ usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
+ return darwin_to_libusb (kresult);
+ }
+ }
+
+ if (!usbInterface) {
+ usbi_err (HANDLE_CTX (dev_handle), "interface not found");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ /* get an interface to the device's interface */
+ kresult = IOCreatePlugInInterfaceForService (usbInterface, kIOUSBInterfaceUserClientTypeID,
+ kIOCFPlugInInterfaceID, &plugInInterface, &score);
+
+ /* ignore release error */
+ (void)IOObjectRelease (usbInterface);
+
+ if (kresult) {
+ usbi_err (HANDLE_CTX (dev_handle), "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult));
+ return darwin_to_libusb (kresult);
+ }
+
+ if (!plugInInterface) {
+ usbi_err (HANDLE_CTX (dev_handle), "plugin interface not found");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ /* Do the actual claim */
+ kresult = (*plugInInterface)->QueryInterface(plugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
+ (LPVOID)&cInterface->interface);
+ /* We no longer need the intermediate plug-in */
+ /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */
+ (*plugInInterface)->Release (plugInInterface);
+ if (kresult || !cInterface->interface) {
+ usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult));
+ return darwin_to_libusb (kresult);
+ }
+
+ /* claim the interface */
+ kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface);
+ if (kresult) {
+ usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceOpen: %s", darwin_error_str(kresult));
+ return darwin_to_libusb (kresult);
+ }
+
+ /* update list of endpoints */
+ kresult = get_endpoints (dev_handle, iface);
+ if (kresult) {
+ /* this should not happen */
+ darwin_release_interface (dev_handle, iface);
+ usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
+ return kresult;
+ }
+
+ cInterface->cfSource = NULL;
+
+ /* create async event source */
+ kresult = (*(cInterface->interface))->CreateInterfaceAsyncEventSource (cInterface->interface, &cInterface->cfSource);
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (HANDLE_CTX (dev_handle), "could not create async event source");
+
+ /* can't continue without an async event source */
+ (void)darwin_release_interface (dev_handle, iface);
+
+ return darwin_to_libusb (kresult);
+ }
+
+ /* add the cfSource to the async thread's run loop */
+ CFRunLoopAddSource(libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
+
+ usbi_dbg ("interface opened");
+
+ return 0;
+}
+
+static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface) {
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+ IOReturn kresult;
+
+ /* current interface */
+ struct darwin_interface *cInterface = &priv->interfaces[iface];
+
+ /* Check to see if an interface is open */
+ if (!cInterface->interface)
+ return LIBUSB_SUCCESS;
+
+ /* clean up endpoint data */
+ cInterface->num_endpoints = 0;
+
+ /* delete the interface's async event source */
+ if (cInterface->cfSource) {
+ CFRunLoopRemoveSource (libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
+ CFRelease (cInterface->cfSource);
+ }
+
+ kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface);
+ if (kresult)
+ usbi_warn (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult));
+
+ kresult = (*(cInterface->interface))->Release(cInterface->interface);
+ if (kresult != kIOReturnSuccess)
+ usbi_warn (HANDLE_CTX (dev_handle), "Release: %s", darwin_error_str(kresult));
+
+ cInterface->interface = IO_OBJECT_NULL;
+
+ return darwin_to_libusb (kresult);
+}
+
+static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) {
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+ IOReturn kresult;
+
+ /* current interface */
+ struct darwin_interface *cInterface = &priv->interfaces[iface];
+
+ if (!cInterface->interface)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ kresult = (*(cInterface->interface))->SetAlternateInterface (cInterface->interface, altsetting);
+ if (kresult != kIOReturnSuccess)
+ darwin_reset_device (dev_handle);
+
+ /* update list of endpoints */
+ kresult = get_endpoints (dev_handle, iface);
+ if (kresult) {
+ /* this should not happen */
+ darwin_release_interface (dev_handle, iface);
+ usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
+ return kresult;
+ }
+
+ return darwin_to_libusb (kresult);
+}
+
+static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) {
+ /* current interface */
+ struct darwin_interface *cInterface;
+ IOReturn kresult;
+ uint8_t pipeRef;
+
+ /* determine the interface/endpoint to use */
+ if (ep_to_pipeRef (dev_handle, endpoint, &pipeRef, NULL, &cInterface) != 0) {
+ usbi_err (HANDLE_CTX (dev_handle), "endpoint not found on any open interface");
+
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ /* newer versions of darwin support clearing additional bits on the device's endpoint */
+ kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef);
+ if (kresult)
+ usbi_warn (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (kresult));
+
+ return darwin_to_libusb (kresult);
+}
+
+static int darwin_reset_device(struct libusb_device_handle *dev_handle) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ IOUSBDeviceDescriptor descriptor;
+ IOUSBConfigurationDescriptorPtr cached_configuration;
+ IOUSBConfigurationDescriptor configuration;
+ bool reenumerate = false;
+ IOReturn kresult;
+ int i;
+
+ kresult = (*(dpriv->device))->ResetDevice (dpriv->device);
+ if (kresult) {
+ usbi_err (HANDLE_CTX (dev_handle), "ResetDevice: %s", darwin_error_str (kresult));
+ return darwin_to_libusb (kresult);
+ }
+
+ do {
+ usbi_dbg ("darwin/reset_device: checking if device descriptor changed");
+
+ /* ignore return code. if we can't get a descriptor it might be worthwhile re-enumerating anway */
+ (void) darwin_request_descriptor (dpriv->device, kUSBDeviceDesc, 0, &descriptor, sizeof (descriptor));
+
+ /* check if the device descriptor has changed */
+ if (0 != memcmp (&dpriv->dev_descriptor, &descriptor, sizeof (descriptor))) {
+ reenumerate = true;
+ break;
+ }
+
+ /* check if any configuration descriptor has changed */
+ for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) {
+ usbi_dbg ("darwin/reset_device: checking if configuration descriptor %d changed", i);
+
+ (void) darwin_request_descriptor (dpriv->device, kUSBConfDesc, i, &configuration, sizeof (configuration));
+ (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
+
+ if (!cached_configuration || 0 != memcmp (cached_configuration, &configuration, sizeof (configuration))) {
+ reenumerate = true;
+ break;
+ }
+ }
+ } while (0);
+
+ if (reenumerate) {
+ usbi_dbg ("darwin/reset_device: device requires reenumeration");
+ (void) (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ usbi_dbg ("darwin/reset_device: device reset complete");
+
+ return LIBUSB_SUCCESS;
+}
+
+static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, int interface) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ io_service_t usbInterface;
+ CFTypeRef driver;
+ IOReturn kresult;
+
+ kresult = darwin_get_interface (dpriv->device, interface, &usbInterface);
+ if (kresult) {
+ usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
+
+ return darwin_to_libusb (kresult);
+ }
+
+ driver = IORegistryEntryCreateCFProperty (usbInterface, kIOBundleIdentifierKey, kCFAllocatorDefault, 0);
+ IOObjectRelease (usbInterface);
+
+ if (driver) {
+ CFRelease (driver);
+
+ return 1;
+ }
+
+ /* no driver */
+ return 0;
+}
+
+/* attaching/detaching kernel drivers is not currently supported (maybe in the future?) */
+static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) {
+ (void)dev_handle;
+ (void)interface;
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) {
+ (void)dev_handle;
+ (void)interface;
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+static void darwin_destroy_device(struct libusb_device *dev) {
+ struct darwin_device_priv *dpriv = (struct darwin_device_priv *) dev->os_priv;
+
+ if (dpriv->dev) {
+ /* need to hold the lock in case this is the last reference to the device */
+ usbi_mutex_lock(&darwin_cached_devices_lock);
+ darwin_deref_cached_device (dpriv->dev);
+ dpriv->dev = NULL;
+ usbi_mutex_unlock(&darwin_cached_devices_lock);
+ }
+}
+
+static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ IOReturn ret;
+ uint8_t transferType;
+ /* None of the values below are used in libusbx for bulk transfers */
+ uint8_t direction, number, interval, pipeRef;
+ uint16_t maxPacketSize;
+
+ struct darwin_interface *cInterface;
+
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
+ usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
+
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ ret = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
+ &transferType, &maxPacketSize, &interval);
+
+ if (ret) {
+ usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
+ darwin_error_str(ret), ret);
+ return darwin_to_libusb (ret);
+ }
+
+ if (0 != (transfer->length % maxPacketSize)) {
+ /* do not need a zero packet */
+ transfer->flags &= ~LIBUSB_TRANSFER_ADD_ZERO_PACKET;
+ }
+
+ /* submit the request */
+ /* timeouts are unavailable on interrupt endpoints */
+ if (transferType == kUSBInterrupt) {
+ if (IS_XFERIN(transfer))
+ ret = (*(cInterface->interface))->ReadPipeAsync(cInterface->interface, pipeRef, transfer->buffer,
+ transfer->length, darwin_async_io_callback, itransfer);
+ else
+ ret = (*(cInterface->interface))->WritePipeAsync(cInterface->interface, pipeRef, transfer->buffer,
+ transfer->length, darwin_async_io_callback, itransfer);
+ } else {
+ itransfer->flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
+
+ if (IS_XFERIN(transfer))
+ ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer,
+ transfer->length, transfer->timeout, transfer->timeout,
+ darwin_async_io_callback, (void *)itransfer);
+ else
+ ret = (*(cInterface->interface))->WritePipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer,
+ transfer->length, transfer->timeout, transfer->timeout,
+ darwin_async_io_callback, (void *)itransfer);
+ }
+
+ if (ret)
+ usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
+ darwin_error_str(ret), ret);
+
+ return darwin_to_libusb (ret);
+}
+
+#if InterfaceVersion >= 550
+static int submit_stream_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_interface *cInterface;
+ uint8_t pipeRef;
+ IOReturn ret;
+
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
+ usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
+
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ itransfer->flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
+
+ if (IS_XFERIN(transfer))
+ ret = (*(cInterface->interface))->ReadStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id,
+ transfer->buffer, transfer->length, transfer->timeout,
+ transfer->timeout, darwin_async_io_callback, (void *)itransfer);
+ else
+ ret = (*(cInterface->interface))->WriteStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id,
+ transfer->buffer, transfer->length, transfer->timeout,
+ transfer->timeout, darwin_async_io_callback, (void *)itransfer);
+
+ if (ret)
+ usbi_err (TRANSFER_CTX (transfer), "bulk stream transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
+ darwin_error_str(ret), ret);
+
+ return darwin_to_libusb (ret);
+}
+#endif
+
+static int submit_iso_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+
+ IOReturn kresult;
+ uint8_t direction, number, interval, pipeRef, transferType;
+ uint16_t maxPacketSize;
+ UInt64 frame;
+ AbsoluteTime atTime;
+ int i;
+
+ struct darwin_interface *cInterface;
+
+ /* construct an array of IOUSBIsocFrames, reuse the old one if possible */
+ if (tpriv->isoc_framelist && tpriv->num_iso_packets != transfer->num_iso_packets) {
+ free(tpriv->isoc_framelist);
+ tpriv->isoc_framelist = NULL;
+ }
+
+ if (!tpriv->isoc_framelist) {
+ tpriv->num_iso_packets = transfer->num_iso_packets;
+ tpriv->isoc_framelist = (IOUSBIsocFrame*) calloc (transfer->num_iso_packets, sizeof(IOUSBIsocFrame));
+ if (!tpriv->isoc_framelist)
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ /* copy the frame list from the libusb descriptor (the structures differ only is member order) */
+ for (i = 0 ; i < transfer->num_iso_packets ; i++)
+ tpriv->isoc_framelist[i].frReqCount = transfer->iso_packet_desc[i].length;
+
+ /* determine the interface/endpoint to use */
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
+ usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
+
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ /* determine the properties of this endpoint and the speed of the device */
+ (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
+ &transferType, &maxPacketSize, &interval);
+
+ /* Last but not least we need the bus frame number */
+ kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime);
+ if (kresult) {
+ usbi_err (TRANSFER_CTX (transfer), "failed to get bus frame number: %d", kresult);
+ free(tpriv->isoc_framelist);
+ tpriv->isoc_framelist = NULL;
+
+ return darwin_to_libusb (kresult);
+ }
+
+ (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
+ &transferType, &maxPacketSize, &interval);
+
+ /* schedule for a frame a little in the future */
+ frame += 4;
+
+ if (cInterface->frames[transfer->endpoint] && frame < cInterface->frames[transfer->endpoint])
+ frame = cInterface->frames[transfer->endpoint];
+
+ /* submit the request */
+ if (IS_XFERIN(transfer))
+ kresult = (*(cInterface->interface))->ReadIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame,
+ transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
+ itransfer);
+ else
+ kresult = (*(cInterface->interface))->WriteIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame,
+ transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
+ itransfer);
+
+ if (LIBUSB_SPEED_FULL == transfer->dev_handle->dev->speed)
+ /* Full speed */
+ cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1));
+ else
+ /* High/super speed */
+ cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1)) / 8;
+
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (TRANSFER_CTX (transfer), "isochronous transfer failed (dir: %s): %s", IS_XFERIN(transfer) ? "In" : "Out",
+ darwin_error_str(kresult));
+ free (tpriv->isoc_framelist);
+ tpriv->isoc_framelist = NULL;
+ }
+
+ return darwin_to_libusb (kresult);
+}
+
+static int submit_control_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_control_setup *setup = (struct libusb_control_setup *) transfer->buffer;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev);
+ struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+
+ IOReturn kresult;
+
+ bzero(&tpriv->req, sizeof(tpriv->req));
+
+ /* IOUSBDeviceInterface expects the request in cpu endianess */
+ tpriv->req.bmRequestType = setup->bmRequestType;
+ tpriv->req.bRequest = setup->bRequest;
+ /* these values should be in bus order from libusb_fill_control_setup */
+ tpriv->req.wValue = OSSwapLittleToHostInt16 (setup->wValue);
+ tpriv->req.wIndex = OSSwapLittleToHostInt16 (setup->wIndex);
+ tpriv->req.wLength = OSSwapLittleToHostInt16 (setup->wLength);
+ /* data is stored after the libusb control block */
+ tpriv->req.pData = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE;
+ tpriv->req.completionTimeout = transfer->timeout;
+ tpriv->req.noDataTimeout = transfer->timeout;
+
+ itransfer->flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
+
+ /* all transfers in libusb-1.0 are async */
+
+ if (transfer->endpoint) {
+ struct darwin_interface *cInterface;
+ uint8_t pipeRef;
+
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
+ usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
+
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ kresult = (*(cInterface->interface))->ControlRequestAsyncTO (cInterface->interface, pipeRef, &(tpriv->req), darwin_async_io_callback, itransfer);
+ } else
+ /* control request on endpoint 0 */
+ kresult = (*(dpriv->device))->DeviceRequestAsyncTO(dpriv->device, &(tpriv->req), darwin_async_io_callback, itransfer);
+
+ if (kresult != kIOReturnSuccess)
+ usbi_err (TRANSFER_CTX (transfer), "control request failed: %s", darwin_error_str(kresult));
+
+ return darwin_to_libusb (kresult);
+}
+
+static int darwin_submit_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ return submit_control_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ return submit_bulk_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return submit_iso_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+#if InterfaceVersion >= 550
+ return submit_stream_transfer(itransfer);
+#else
+ usbi_err (TRANSFER_CTX(transfer), "IOUSBFamily version does not support bulk stream transfers");
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+#endif
+ default:
+ usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+static int cancel_control_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev);
+ IOReturn kresult;
+
+ usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions control pipe");
+
+ if (!dpriv->device)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ kresult = (*(dpriv->device))->USBDeviceAbortPipeZero (dpriv->device);
+
+ return darwin_to_libusb (kresult);
+}
+
+static int darwin_abort_transfers (struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev);
+ struct darwin_interface *cInterface;
+ uint8_t pipeRef, iface;
+ IOReturn kresult;
+
+ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface, &cInterface) != 0) {
+ usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
+
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ if (!dpriv->device)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions on interface %d pipe %d", iface, pipeRef);
+
+ /* abort transactions */
+#if InterfaceVersion >= 550
+ if (LIBUSB_TRANSFER_TYPE_BULK_STREAM == transfer->type)
+ (*(cInterface->interface))->AbortStreamsPipe (cInterface->interface, pipeRef, itransfer->stream_id);
+ else
+#endif
+ (*(cInterface->interface))->AbortPipe (cInterface->interface, pipeRef);
+
+ usbi_dbg ("calling clear pipe stall to clear the data toggle bit");
+
+ /* newer versions of darwin support clearing additional bits on the device's endpoint */
+ kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef);
+
+ return darwin_to_libusb (kresult);
+}
+
+static int darwin_cancel_transfer(struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ return cancel_control_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return darwin_abort_transfers (itransfer);
+ default:
+ usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+static void darwin_clear_transfer_priv (struct usbi_transfer *itransfer) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+
+ if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS && tpriv->isoc_framelist) {
+ free (tpriv->isoc_framelist);
+ tpriv->isoc_framelist = NULL;
+ }
+}
+
+static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) {
+ struct usbi_transfer *itransfer = (struct usbi_transfer *)refcon;
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv;
+ struct darwin_msg_async_io_complete message = {.itransfer = itransfer, .result = result,
+ .size = (UInt32) (uintptr_t) arg0};
+
+ usbi_dbg ("an async io operation has completed");
+
+ /* if requested write a zero packet */
+ if (kIOReturnSuccess == result && IS_XFEROUT(transfer) && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) {
+ struct darwin_interface *cInterface;
+ uint8_t pipeRef;
+
+ (void) ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface);
+
+ (*(cInterface->interface))->WritePipe (cInterface->interface, pipeRef, transfer->buffer, 0);
+ }
+
+ /* send a completion message to the device's file descriptor */
+ write (priv->fds[1], &message, sizeof (message));
+}
+
+static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_t result) {
+ if (itransfer->flags & USBI_TRANSFER_TIMED_OUT)
+ result = kIOUSBTransactionTimeout;
+
+ switch (result) {
+ case kIOReturnUnderrun:
+ case kIOReturnSuccess:
+ return LIBUSB_TRANSFER_COMPLETED;
+ case kIOReturnAborted:
+ return LIBUSB_TRANSFER_CANCELLED;
+ case kIOUSBPipeStalled:
+ usbi_dbg ("transfer error: pipe is stalled");
+ return LIBUSB_TRANSFER_STALL;
+ case kIOReturnOverrun:
+ usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: data overrun");
+ return LIBUSB_TRANSFER_OVERFLOW;
+ case kIOUSBTransactionTimeout:
+ usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: timed out");
+ itransfer->flags |= USBI_TRANSFER_TIMED_OUT;
+ return LIBUSB_TRANSFER_TIMED_OUT;
+ default:
+ usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result);
+ return LIBUSB_TRANSFER_ERROR;
+ }
+}
+
+static void darwin_handle_callback (struct usbi_transfer *itransfer, kern_return_t result, UInt32 io_size) {
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ int isIsoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type;
+ int isBulk = LIBUSB_TRANSFER_TYPE_BULK == transfer->type;
+ int isControl = LIBUSB_TRANSFER_TYPE_CONTROL == transfer->type;
+ int isInterrupt = LIBUSB_TRANSFER_TYPE_INTERRUPT == transfer->type;
+ int i;
+
+ if (!isIsoc && !isBulk && !isControl && !isInterrupt) {
+ usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+ return;
+ }
+
+ usbi_dbg ("handling %s completion with kernel status %d",
+ isControl ? "control" : isBulk ? "bulk" : isIsoc ? "isoc" : "interrupt", result);
+
+ if (kIOReturnSuccess == result || kIOReturnUnderrun == result) {
+ if (isIsoc && tpriv->isoc_framelist) {
+ /* copy isochronous results back */
+
+ for (i = 0; i < transfer->num_iso_packets ; i++) {
+ struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i];
+ lib_desc->status = darwin_to_libusb (tpriv->isoc_framelist[i].frStatus);
+ lib_desc->actual_length = tpriv->isoc_framelist[i].frActCount;
+ }
+ } else if (!isIsoc)
+ itransfer->transferred += io_size;
+ }
+
+ /* it is ok to handle cancelled transfers without calling usbi_handle_transfer_cancellation (we catch timeout transfers) */
+ usbi_handle_transfer_completion (itransfer, darwin_transfer_status (itransfer, result));
+}
+
+static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready) {
+ struct darwin_msg_async_io_complete message;
+ POLL_NFDS_TYPE i = 0;
+ ssize_t ret;
+
+ usbi_mutex_lock(&ctx->open_devs_lock);
+
+ for (i = 0; i < nfds && num_ready > 0; i++) {
+ struct pollfd *pollfd = &fds[i];
+
+ usbi_dbg ("checking fd %i with revents = %x", pollfd->fd, pollfd->revents);
+
+ if (!pollfd->revents)
+ continue;
+
+ num_ready--;
+
+ if (pollfd->revents & POLLERR) {
+ /* this probably will never happen so ignore the error an move on. */
+ continue;
+ }
+
+ /* there is only one type of message */
+ ret = read (pollfd->fd, &message, sizeof (message));
+ if (ret < (ssize_t) sizeof (message)) {
+ usbi_dbg ("WARNING: short read on async io completion pipe\n");
+ continue;
+ }
+
+ darwin_handle_callback (message.itransfer, message.result, message.size);
+ }
+
+ usbi_mutex_unlock(&ctx->open_devs_lock);
+
+ return 0;
+}
+
+static int darwin_clock_gettime(int clk_id, struct timespec *tp) {
+ mach_timespec_t sys_time;
+ clock_serv_t clock_ref;
+
+ switch (clk_id) {
+ case USBI_CLOCK_REALTIME:
+ /* CLOCK_REALTIME represents time since the epoch */
+ clock_ref = clock_realtime;
+ break;
+ case USBI_CLOCK_MONOTONIC:
+ /* use system boot time as reference for the monotonic clock */
+ clock_ref = clock_monotonic;
+ break;
+ default:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ clock_get_time (clock_ref, &sys_time);
+
+ tp->tv_sec = sys_time.tv_sec;
+ tp->tv_nsec = sys_time.tv_nsec;
+
+ return 0;
+}
+
+#if InterfaceVersion >= 550
+static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32_t num_streams, unsigned char *endpoints,
+ int num_endpoints) {
+ struct darwin_interface *cInterface;
+ UInt32 supportsStreams;
+ uint8_t pipeRef;
+ int rc, i;
+
+ /* find the mimimum number of supported streams on the endpoint list */
+ for (i = 0 ; i < num_endpoints ; ++i) {
+ if (0 != (rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface))) {
+ return rc;
+ }
+
+ (*(cInterface->interface))->SupportsStreams (cInterface->interface, pipeRef, &supportsStreams);
+ if (num_streams > supportsStreams)
+ num_streams = supportsStreams;
+ }
+
+ /* it is an error if any endpoint in endpoints does not support streams */
+ if (0 == num_streams)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ /* create the streams */
+ for (i = 0 ; i < num_endpoints ; ++i) {
+ (void) ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface);
+
+ rc = (*(cInterface->interface))->CreateStreams (cInterface->interface, pipeRef, num_streams);
+ if (kIOReturnSuccess != rc)
+ return darwin_to_libusb(rc);
+ }
+
+ return num_streams;
+}
+
+static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigned char *endpoints, int num_endpoints) {
+ struct darwin_interface *cInterface;
+ UInt32 supportsStreams;
+ uint8_t pipeRef;
+ int rc;
+
+ for (int i = 0 ; i < num_endpoints ; ++i) {
+ if (0 != (rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface)))
+ return rc;
+
+ (*(cInterface->interface))->SupportsStreams (cInterface->interface, pipeRef, &supportsStreams);
+ if (0 == supportsStreams)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ rc = (*(cInterface->interface))->CreateStreams (cInterface->interface, pipeRef, 0);
+ if (kIOReturnSuccess != rc)
+ return darwin_to_libusb(rc);
+ }
+
+ return LIBUSB_SUCCESS;
+}
+#endif
+
+const struct usbi_os_backend darwin_backend = {
+ .name = "Darwin",
+ .caps = 0,
+ .init = darwin_init,
+ .exit = darwin_exit,
+ .get_device_list = NULL, /* not needed */
+ .get_device_descriptor = darwin_get_device_descriptor,
+ .get_active_config_descriptor = darwin_get_active_config_descriptor,
+ .get_config_descriptor = darwin_get_config_descriptor,
+ .hotplug_poll = darwin_hotplug_poll,
+
+ .open = darwin_open,
+ .close = darwin_close,
+ .get_configuration = darwin_get_configuration,
+ .set_configuration = darwin_set_configuration,
+ .claim_interface = darwin_claim_interface,
+ .release_interface = darwin_release_interface,
+
+ .set_interface_altsetting = darwin_set_interface_altsetting,
+ .clear_halt = darwin_clear_halt,
+ .reset_device = darwin_reset_device,
+
+#if InterfaceVersion >= 550
+ .alloc_streams = darwin_alloc_streams,
+ .free_streams = darwin_free_streams,
+#endif
+
+ .kernel_driver_active = darwin_kernel_driver_active,
+ .detach_kernel_driver = darwin_detach_kernel_driver,
+ .attach_kernel_driver = darwin_attach_kernel_driver,
+
+ .destroy_device = darwin_destroy_device,
+
+ .submit_transfer = darwin_submit_transfer,
+ .cancel_transfer = darwin_cancel_transfer,
+ .clear_transfer_priv = darwin_clear_transfer_priv,
+
+ .handle_events = op_handle_events,
+
+ .clock_gettime = darwin_clock_gettime,
+
+ .device_priv_size = sizeof(struct darwin_device_priv),
+ .device_handle_priv_size = sizeof(struct darwin_device_handle_priv),
+ .transfer_priv_size = sizeof(struct darwin_transfer_priv),
+ .add_iso_packet_size = 0,
+};
diff --git a/e502/libusb-1.0/libusb-1.0/os/darwin_usb.h b/e502/libusb-1.0/libusb-1.0/os/darwin_usb.h
new file mode 100644
index 0000000..8838881
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/darwin_usb.h
@@ -0,0 +1,162 @@
+/*
+ * darwin backend for libusb 1.0
+ * Copyright © 2008-2013 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 (kIOUSBInterfaceInterfaceID550)
+
+#define usb_interface_t IOUSBInterfaceInterface550
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550
+#define InterfaceVersion 550
+
+#elif defined (kIOUSBInterfaceInterfaceID500)
+
+#define usb_interface_t IOUSBInterfaceInterface500
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500
+#define InterfaceVersion 500
+
+#elif 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
+
+#else
+
+#error "IOUSBFamily is too old. Please upgrade your OS"
+
+#endif
+
+/* IOUSBDeviceInterface */
+#if defined (kIOUSBDeviceInterfaceID500)
+
+#define usb_device_t IOUSBDeviceInterface500
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID500
+#define DeviceVersion 500
+
+#elif 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 (kIOUSBDeviceInterfaceID220)
+#define usb_device_t IOUSBDeviceInterface197
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID197
+#define DeviceVersion 197
+
+#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_cached_device {
+ struct list_head list;
+ IOUSBDeviceDescriptor dev_descriptor;
+ UInt32 location;
+ UInt64 parent_session;
+ UInt64 session;
+ UInt16 address;
+ char sys_path[21];
+ usb_device_t **device;
+ int open_count;
+ UInt8 first_config, active_config, port;
+ int can_enumerate;
+ int refcount;
+};
+
+struct darwin_device_priv {
+ struct darwin_cached_device *dev;
+};
+
+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;
+ int num_iso_packets;
+
+ /* Control */
+ IOUSBDevRequestTO req;
+
+ /* Bulk */
+};
+
+/* structure for signaling io completion */
+struct darwin_msg_async_io_complete {
+ struct usbi_transfer *itransfer;
+ IOReturn result;
+ UInt32 size;
+};
+
+#endif
diff --git a/e502/libusb-1.0/libusb-1.0/os/linux_netlink.c b/e502/libusb-1.0/libusb-1.0/os/linux_netlink.c
new file mode 100644
index 0000000..306f0b4
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/linux_netlink.c
@@ -0,0 +1,369 @@
+/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */
+/*
+ * Linux usbfs backend for libusb
+ * Copyright (C) 2007-2009 Daniel Drake
+ * Copyright (c) 2001 Johannes Erdfelt
+ * Copyright (c) 2013 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
+ */
+
+#include "config.h"
+#include "libusb.h"
+#include "libusbi.h"
+#include "linux_usbfs.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef HAVE_ASM_TYPES_H
+#include
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include
+#endif
+
+#include
+
+#ifdef HAVE_LINUX_NETLINK_H
+#include
+#endif
+
+#ifdef HAVE_LINUX_FILTER_H
+#include
+#endif
+
+#define KERNEL 1
+
+static int linux_netlink_socket = -1;
+static int netlink_control_pipe[2] = { -1, -1 };
+static pthread_t libusb_linux_event_thread;
+
+static void *linux_netlink_event_thread_main(void *arg);
+
+struct sockaddr_nl snl = { .nl_family=AF_NETLINK, .nl_groups=KERNEL };
+
+static int set_fd_cloexec_nb (int fd)
+{
+ int flags;
+
+#if defined(FD_CLOEXEC)
+ flags = fcntl (linux_netlink_socket, F_GETFD);
+ if (0 > flags) {
+ return -1;
+ }
+
+ if (!(flags & FD_CLOEXEC)) {
+ fcntl (linux_netlink_socket, F_SETFD, flags | FD_CLOEXEC);
+ }
+#endif
+
+ flags = fcntl (linux_netlink_socket, F_GETFL);
+ if (0 > flags) {
+ return -1;
+ }
+
+ if (!(flags & O_NONBLOCK)) {
+ fcntl (linux_netlink_socket, F_SETFL, flags | O_NONBLOCK);
+ }
+
+ return 0;
+}
+
+int linux_netlink_start_event_monitor(void)
+{
+ int socktype = SOCK_RAW;
+ int ret;
+
+ snl.nl_groups = KERNEL;
+
+#if defined(SOCK_CLOEXEC)
+ socktype |= SOCK_CLOEXEC;
+#endif
+#if defined(SOCK_NONBLOCK)
+ socktype |= SOCK_NONBLOCK;
+#endif
+
+ linux_netlink_socket = socket(PF_NETLINK, socktype, NETLINK_KOBJECT_UEVENT);
+ if (-1 == linux_netlink_socket && EINVAL == errno) {
+ linux_netlink_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
+ }
+
+ if (-1 == linux_netlink_socket) {
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ ret = set_fd_cloexec_nb (linux_netlink_socket);
+ if (0 != ret) {
+ close (linux_netlink_socket);
+ linux_netlink_socket = -1;
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ ret = bind(linux_netlink_socket, (struct sockaddr *) &snl, sizeof(snl));
+ if (0 != ret) {
+ close(linux_netlink_socket);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ /* TODO -- add authentication */
+ /* setsockopt(linux_netlink_socket, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); */
+
+ ret = usbi_pipe(netlink_control_pipe);
+ if (ret) {
+ usbi_err(NULL, "could not create netlink control pipe");
+ close(linux_netlink_socket);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ ret = pthread_create(&libusb_linux_event_thread, NULL, linux_netlink_event_thread_main, NULL);
+ if (0 != ret) {
+ close(netlink_control_pipe[0]);
+ close(netlink_control_pipe[1]);
+ close(linux_netlink_socket);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+int linux_netlink_stop_event_monitor(void)
+{
+ int r;
+ char dummy = 1;
+
+ if (-1 == linux_netlink_socket) {
+ /* already closed. nothing to do */
+ return LIBUSB_SUCCESS;
+ }
+
+ /* Write some dummy data to the control pipe and
+ * wait for the thread to exit */
+ r = usbi_write(netlink_control_pipe[1], &dummy, sizeof(dummy));
+ if (r <= 0) {
+ usbi_warn(NULL, "netlink control pipe signal failed");
+ }
+ pthread_join(libusb_linux_event_thread, NULL);
+
+ close(linux_netlink_socket);
+ linux_netlink_socket = -1;
+
+ /* close and reset control pipe */
+ close(netlink_control_pipe[0]);
+ close(netlink_control_pipe[1]);
+ netlink_control_pipe[0] = -1;
+ netlink_control_pipe[1] = -1;
+
+ return LIBUSB_SUCCESS;
+}
+
+static const char *netlink_message_parse (const char *buffer, size_t len, const char *key)
+{
+ size_t keylen = strlen(key);
+ size_t offset;
+
+ for (offset = 0 ; offset < len && '\0' != buffer[offset] ; offset += strlen(buffer + offset) + 1) {
+ if (0 == strncmp(buffer + offset, key, keylen) &&
+ '=' == buffer[offset + keylen]) {
+ return buffer + offset + keylen + 1;
+ }
+ }
+
+ return NULL;
+}
+
+/* parse parts of netlink message common to both libudev and the kernel */
+static int linux_netlink_parse(char *buffer, size_t len, int *detached, const char **sys_name,
+ uint8_t *busnum, uint8_t *devaddr) {
+ const char *tmp;
+ int i;
+
+ errno = 0;
+
+ *sys_name = NULL;
+ *detached = 0;
+ *busnum = 0;
+ *devaddr = 0;
+
+ tmp = netlink_message_parse((const char *) buffer, len, "ACTION");
+ if (tmp == NULL)
+ return -1;
+ if (0 == strcmp(tmp, "remove")) {
+ *detached = 1;
+ } else if (0 != strcmp(tmp, "add")) {
+ usbi_dbg("unknown device action %s", tmp);
+ return -1;
+ }
+
+ /* check that this is a usb message */
+ tmp = netlink_message_parse(buffer, len, "SUBSYSTEM");
+ if (NULL == tmp || 0 != strcmp(tmp, "usb")) {
+ /* not usb. ignore */
+ return -1;
+ }
+
+ tmp = netlink_message_parse(buffer, len, "BUSNUM");
+ if (NULL == tmp) {
+ /* no bus number. try "DEVICE" */
+ tmp = netlink_message_parse(buffer, len, "DEVICE");
+ if (NULL == tmp) {
+ /* not usb. ignore */
+ return -1;
+ }
+
+ /* Parse a device path such as /dev/bus/usb/003/004 */
+ char *pLastSlash = (char*)strrchr(tmp,'/');
+ if(NULL == pLastSlash) {
+ return -1;
+ }
+
+ *devaddr = strtoul(pLastSlash + 1, NULL, 10);
+ if (errno) {
+ errno = 0;
+ return -1;
+ }
+
+ *busnum = strtoul(pLastSlash - 3, NULL, 10);
+ if (errno) {
+ errno = 0;
+ return -1;
+ }
+
+ return 0;
+ }
+
+ *busnum = (uint8_t)(strtoul(tmp, NULL, 10) & 0xff);
+ if (errno) {
+ errno = 0;
+ return -1;
+ }
+
+ tmp = netlink_message_parse(buffer, len, "DEVNUM");
+ if (NULL == tmp) {
+ return -1;
+ }
+
+ *devaddr = (uint8_t)(strtoul(tmp, NULL, 10) & 0xff);
+ if (errno) {
+ errno = 0;
+ return -1;
+ }
+
+ tmp = netlink_message_parse(buffer, len, "DEVPATH");
+ if (NULL == tmp) {
+ return -1;
+ }
+
+ for (i = strlen(tmp) - 1 ; i ; --i) {
+ if ('/' ==tmp[i]) {
+ *sys_name = tmp + i + 1;
+ break;
+ }
+ }
+
+ /* found a usb device */
+ return 0;
+}
+
+static int linux_netlink_read_message(void)
+{
+ unsigned char buffer[1024];
+ struct iovec iov = {.iov_base = buffer, .iov_len = sizeof(buffer)};
+ struct msghdr meh = { .msg_iov=&iov, .msg_iovlen=1,
+ .msg_name=&snl, .msg_namelen=sizeof(snl) };
+ const char *sys_name = NULL;
+ uint8_t busnum, devaddr;
+ int detached, r;
+ size_t len;
+
+ /* read netlink message */
+ memset(buffer, 0, sizeof(buffer));
+ len = recvmsg(linux_netlink_socket, &meh, 0);
+ if (len < 32) {
+ if (errno != EAGAIN)
+ usbi_dbg("error recieving message from netlink");
+ return -1;
+ }
+
+ /* TODO -- authenticate this message is from the kernel or udevd */
+
+ r = linux_netlink_parse(buffer, len, &detached, &sys_name,
+ &busnum, &devaddr);
+ if (r)
+ return r;
+
+ usbi_dbg("netlink hotplug found device busnum: %hhu, devaddr: %hhu, sys_name: %s, removed: %s",
+ busnum, devaddr, sys_name, detached ? "yes" : "no");
+
+ /* signal device is available (or not) to all contexts */
+ if (detached)
+ linux_device_disconnected(busnum, devaddr, sys_name);
+ else
+ linux_hotplug_enumerate(busnum, devaddr, sys_name);
+
+ return 0;
+}
+
+static void *linux_netlink_event_thread_main(void *arg)
+{
+ char dummy;
+ int r;
+ struct pollfd fds[] = {
+ { .fd = netlink_control_pipe[0],
+ .events = POLLIN },
+ { .fd = linux_netlink_socket,
+ .events = POLLIN },
+ };
+
+ /* silence compiler warning */
+ (void) arg;
+
+ while (poll(fds, 2, -1) >= 0) {
+ if (fds[0].revents & POLLIN) {
+ /* activity on control pipe, read the byte and exit */
+ r = usbi_read(netlink_control_pipe[0], &dummy, sizeof(dummy));
+ if (r <= 0) {
+ usbi_warn(NULL, "netlink control pipe read failed");
+ }
+ break;
+ }
+ if (fds[1].revents & POLLIN) {
+ usbi_mutex_static_lock(&linux_hotplug_lock);
+ linux_netlink_read_message();
+ usbi_mutex_static_unlock(&linux_hotplug_lock);
+ }
+ }
+
+ return NULL;
+}
+
+void linux_netlink_hotplug_poll(void)
+{
+ int r;
+
+ usbi_mutex_static_lock(&linux_hotplug_lock);
+ do {
+ r = linux_netlink_read_message();
+ } while (r == 0);
+ usbi_mutex_static_unlock(&linux_hotplug_lock);
+}
diff --git a/e502/libusb-1.0/libusb-1.0/os/linux_udev.c b/e502/libusb-1.0/libusb-1.0/os/linux_udev.c
new file mode 100644
index 0000000..0394048
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/linux_udev.c
@@ -0,0 +1,307 @@
+/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */
+/*
+ * Linux usbfs backend for libusb
+ * Copyright (C) 2007-2009 Daniel Drake
+ * Copyright (c) 2001 Johannes Erdfelt
+ * Copyright (c) 2012-2013 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
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "libusb.h"
+#include "libusbi.h"
+#include "linux_usbfs.h"
+
+/* udev context */
+static struct udev *udev_ctx = NULL;
+static int udev_monitor_fd = -1;
+static int udev_control_pipe[2] = {-1, -1};
+static struct udev_monitor *udev_monitor = NULL;
+static pthread_t linux_event_thread;
+
+static void udev_hotplug_event(struct udev_device* udev_dev);
+static void *linux_udev_event_thread_main(void *arg);
+
+int linux_udev_start_event_monitor(void)
+{
+ int r;
+
+ assert(udev_ctx == NULL);
+ udev_ctx = udev_new();
+ if (!udev_ctx) {
+ usbi_err(NULL, "could not create udev context");
+ goto err;
+ }
+
+ udev_monitor = udev_monitor_new_from_netlink(udev_ctx, "udev");
+ if (!udev_monitor) {
+ usbi_err(NULL, "could not initialize udev monitor");
+ goto err_free_ctx;
+ }
+
+ r = udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", 0);
+ if (r) {
+ usbi_err(NULL, "could not initialize udev monitor filter for \"usb\" subsystem");
+ goto err_free_monitor;
+ }
+
+ if (udev_monitor_enable_receiving(udev_monitor)) {
+ usbi_err(NULL, "failed to enable the udev monitor");
+ goto err_free_monitor;
+ }
+
+ udev_monitor_fd = udev_monitor_get_fd(udev_monitor);
+
+ /* Some older versions of udev are not non-blocking by default,
+ * so make sure this is set */
+ r = fcntl(udev_monitor_fd, F_GETFL);
+ if (r == -1) {
+ usbi_err(NULL, "getting udev monitor fd flags (%d)", errno);
+ goto err_free_monitor;
+ }
+ r = fcntl(udev_monitor_fd, F_SETFL, r | O_NONBLOCK);
+ if (r) {
+ usbi_err(NULL, "setting udev monitor fd flags (%d)", errno);
+ goto err_free_monitor;
+ }
+
+ r = usbi_pipe(udev_control_pipe);
+ if (r) {
+ usbi_err(NULL, "could not create udev control pipe");
+ goto err_free_monitor;
+ }
+
+ r = pthread_create(&linux_event_thread, NULL, linux_udev_event_thread_main, NULL);
+ if (r) {
+ usbi_err(NULL, "creating hotplug event thread (%d)", r);
+ goto err_close_pipe;
+ }
+
+ return LIBUSB_SUCCESS;
+
+err_close_pipe:
+ close(udev_control_pipe[0]);
+ close(udev_control_pipe[1]);
+err_free_monitor:
+ udev_monitor_unref(udev_monitor);
+ udev_monitor = NULL;
+ udev_monitor_fd = -1;
+err_free_ctx:
+ udev_unref(udev_ctx);
+err:
+ udev_ctx = NULL;
+ return LIBUSB_ERROR_OTHER;
+}
+
+int linux_udev_stop_event_monitor(void)
+{
+ char dummy = 1;
+ int r;
+
+ assert(udev_ctx != NULL);
+ assert(udev_monitor != NULL);
+ assert(udev_monitor_fd != -1);
+
+ /* Write some dummy data to the control pipe and
+ * wait for the thread to exit */
+ r = usbi_write(udev_control_pipe[1], &dummy, sizeof(dummy));
+ if (r <= 0) {
+ usbi_warn(NULL, "udev control pipe signal failed");
+ }
+ pthread_join(linux_event_thread, NULL);
+
+ /* Release the udev monitor */
+ udev_monitor_unref(udev_monitor);
+ udev_monitor = NULL;
+ udev_monitor_fd = -1;
+
+ /* Clean up the udev context */
+ udev_unref(udev_ctx);
+ udev_ctx = NULL;
+
+ /* close and reset control pipe */
+ close(udev_control_pipe[0]);
+ close(udev_control_pipe[1]);
+ udev_control_pipe[0] = -1;
+ udev_control_pipe[1] = -1;
+
+ return LIBUSB_SUCCESS;
+}
+
+static void *linux_udev_event_thread_main(void *arg)
+{
+ char dummy;
+ int r;
+ struct udev_device* udev_dev;
+ struct pollfd fds[] = {
+ {.fd = udev_control_pipe[0],
+ .events = POLLIN},
+ {.fd = udev_monitor_fd,
+ .events = POLLIN},
+ };
+
+ usbi_dbg("udev event thread entering.");
+
+ while (poll(fds, 2, -1) >= 0) {
+ if (fds[0].revents & POLLIN) {
+ /* activity on control pipe, read the byte and exit */
+ r = usbi_read(udev_control_pipe[0], &dummy, sizeof(dummy));
+ if (r <= 0) {
+ usbi_warn(NULL, "udev control pipe read failed");
+ }
+ break;
+ }
+ if (fds[1].revents & POLLIN) {
+ usbi_mutex_static_lock(&linux_hotplug_lock);
+ udev_dev = udev_monitor_receive_device(udev_monitor);
+ if (udev_dev)
+ udev_hotplug_event(udev_dev);
+ usbi_mutex_static_unlock(&linux_hotplug_lock);
+ }
+ }
+
+ usbi_dbg("udev event thread exiting");
+
+ return NULL;
+}
+
+static int udev_device_info(struct libusb_context *ctx, int detached,
+ struct udev_device *udev_dev, uint8_t *busnum,
+ uint8_t *devaddr, const char **sys_name) {
+ const char *dev_node;
+
+ dev_node = udev_device_get_devnode(udev_dev);
+ if (!dev_node) {
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ *sys_name = udev_device_get_sysname(udev_dev);
+ if (!*sys_name) {
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return linux_get_device_address(ctx, detached, busnum, devaddr,
+ dev_node, *sys_name);
+}
+
+static void udev_hotplug_event(struct udev_device* udev_dev)
+{
+ const char* udev_action;
+ const char* sys_name = NULL;
+ uint8_t busnum = 0, devaddr = 0;
+ int detached;
+ int r;
+
+ do {
+ udev_action = udev_device_get_action(udev_dev);
+ if (!udev_action) {
+ break;
+ }
+
+ detached = !strncmp(udev_action, "remove", 6);
+
+ r = udev_device_info(NULL, detached, udev_dev, &busnum, &devaddr, &sys_name);
+ if (LIBUSB_SUCCESS != r) {
+ break;
+ }
+
+ usbi_dbg("udev hotplug event. action: %s.", udev_action);
+
+ if (strncmp(udev_action, "add", 3) == 0) {
+ linux_hotplug_enumerate(busnum, devaddr, sys_name);
+ } else if (detached) {
+ linux_device_disconnected(busnum, devaddr, sys_name);
+ } else {
+ usbi_err(NULL, "ignoring udev action %s", udev_action);
+ }
+ } while (0);
+
+ udev_device_unref(udev_dev);
+}
+
+int linux_udev_scan_devices(struct libusb_context *ctx)
+{
+ struct udev_enumerate *enumerator;
+ struct udev_list_entry *devices, *entry;
+ struct udev_device *udev_dev;
+ const char *sys_name;
+ int r;
+
+ assert(udev_ctx != NULL);
+
+ enumerator = udev_enumerate_new(udev_ctx);
+ if (NULL == enumerator) {
+ usbi_err(ctx, "error creating udev enumerator");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ udev_enumerate_add_match_subsystem(enumerator, "usb");
+ udev_enumerate_scan_devices(enumerator);
+ devices = udev_enumerate_get_list_entry(enumerator);
+
+ udev_list_entry_foreach(entry, devices) {
+ const char *path = udev_list_entry_get_name(entry);
+ uint8_t busnum = 0, devaddr = 0;
+
+ udev_dev = udev_device_new_from_syspath(udev_ctx, path);
+
+ r = udev_device_info(ctx, 0, udev_dev, &busnum, &devaddr, &sys_name);
+ if (r) {
+ udev_device_unref(udev_dev);
+ continue;
+ }
+
+ linux_enumerate_device(ctx, busnum, devaddr, sys_name);
+ udev_device_unref(udev_dev);
+ }
+
+ udev_enumerate_unref(enumerator);
+
+ return LIBUSB_SUCCESS;
+}
+
+void linux_udev_hotplug_poll(void)
+{
+ struct udev_device* udev_dev;
+
+ usbi_mutex_static_lock(&linux_hotplug_lock);
+ do {
+ udev_dev = udev_monitor_receive_device(udev_monitor);
+ if (udev_dev) {
+ usbi_dbg("Handling hotplug event from hotplug_poll");
+ udev_hotplug_event(udev_dev);
+ }
+ } while (udev_dev);
+ usbi_mutex_static_unlock(&linux_hotplug_lock);
+}
diff --git a/e502/libusb-1.0/libusb-1.0/os/linux_usbfs.c b/e502/libusb-1.0/libusb-1.0/os/linux_usbfs.c
new file mode 100644
index 0000000..db21710
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/linux_usbfs.c
@@ -0,0 +1,2695 @@
+/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */
+/*
+ * Linux usbfs backend for libusb
+ * Copyright © 2007-2009 Daniel Drake
+ * Copyright © 2001 Johannes Erdfelt
+ * Copyright © 2013 Nathan Hjelm
+ * Copyright © 2012-2013 Hans de Goede
+ *
+ * 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
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "libusb.h"
+#include "libusbi.h"
+#include "linux_usbfs.h"
+
+/* sysfs vs usbfs:
+ * opening a usbfs node causes the device to be resumed, so we attempt to
+ * avoid this during enumeration.
+ *
+ * sysfs allows us to read the kernel's in-memory copies of device descriptors
+ * and so forth, avoiding the need to open the device:
+ * - The binary "descriptors" file contains all config descriptors since
+ * 2.6.26, commit 217a9081d8e69026186067711131b77f0ce219ed
+ * - The binary "descriptors" file was added in 2.6.23, commit
+ * 69d42a78f935d19384d1f6e4f94b65bb162b36df, but it only contains the
+ * active config descriptors
+ * - The "busnum" file was added in 2.6.22, commit
+ * 83f7d958eab2fbc6b159ee92bf1493924e1d0f72
+ * - The "devnum" file has been present since pre-2.6.18
+ * - the "bConfigurationValue" file has been present since pre-2.6.18
+ *
+ * If we have bConfigurationValue, busnum, and devnum, then we can determine
+ * the active configuration without having to open the usbfs node in RDWR mode.
+ * The busnum file is important as that is the only way we can relate sysfs
+ * devices to usbfs nodes.
+ *
+ * If we also have all descriptors, we can obtain the device descriptor and
+ * configuration without touching usbfs at all.
+ */
+
+/* endianness for multi-byte fields:
+ *
+ * Descriptors exposed by usbfs have the multi-byte fields in the device
+ * descriptor as host endian. Multi-byte fields in the other descriptors are
+ * bus-endian. The kernel documentation says otherwise, but it is wrong.
+ *
+ * In sysfs all descriptors are bus-endian.
+ */
+
+static const char *usbfs_path = NULL;
+
+/* use usbdev*.* device names in /dev instead of the usbfs bus directories */
+static int usbdev_names = 0;
+
+/* Linux 2.6.32 adds support for a bulk continuation URB flag. this basically
+ * allows us to mark URBs as being part of a specific logical transfer when
+ * we submit them to the kernel. then, on any error except a cancellation, all
+ * URBs within that transfer will be cancelled and no more URBs will be
+ * accepted for the transfer, meaning that no more data can creep in.
+ *
+ * The BULK_CONTINUATION flag must be set on all URBs within a bulk transfer
+ * (in either direction) except the first.
+ * For IN transfers, we must also set SHORT_NOT_OK on all URBs except the
+ * last; it means that the kernel should treat a short reply as an error.
+ * For OUT transfers, SHORT_NOT_OK must not be set. it isn't needed (OUT
+ * transfers can't be short unless there's already some sort of error), and
+ * setting this flag is disallowed (a kernel with USB debugging enabled will
+ * reject such URBs).
+ */
+static int supports_flag_bulk_continuation = -1;
+
+/* Linux 2.6.31 fixes support for the zero length packet URB flag. This
+ * allows us to mark URBs that should be followed by a zero length data
+ * packet, which can be required by device- or class-specific protocols.
+ */
+static int supports_flag_zero_packet = -1;
+
+/* clock ID for monotonic clock, as not all clock sources are available on all
+ * systems. appropriate choice made at initialization time. */
+static clockid_t monotonic_clkid = -1;
+
+/* Linux 2.6.22 (commit 83f7d958eab2fbc6b159ee92bf1493924e1d0f72) adds a busnum
+ * to sysfs, so we can relate devices. This also implies that we can read
+ * the active configuration through bConfigurationValue */
+static int sysfs_can_relate_devices = -1;
+
+/* Linux 2.6.26 (commit 217a9081d8e69026186067711131b77f0ce219ed) adds all
+ * config descriptors (rather then just the active config) to the sysfs
+ * descriptors file, so from then on we can use them. */
+static int sysfs_has_descriptors = -1;
+
+/* how many times have we initted (and not exited) ? */
+static int init_count = 0;
+
+/* Serialize hotplug start/stop */
+usbi_mutex_static_t linux_hotplug_startstop_lock = USBI_MUTEX_INITIALIZER;
+/* Serialize scan-devices, event-thread, and poll */
+usbi_mutex_static_t linux_hotplug_lock = USBI_MUTEX_INITIALIZER;
+
+static int linux_start_event_monitor(void);
+static int linux_stop_event_monitor(void);
+static int linux_scan_devices(struct libusb_context *ctx);
+static int sysfs_scan_device(struct libusb_context *ctx, const char *devname);
+static int detach_kernel_driver_and_claim(struct libusb_device_handle *, int);
+
+#if !defined(USE_UDEV)
+static int linux_default_scan_devices (struct libusb_context *ctx);
+#endif
+
+struct linux_device_priv {
+ char *sysfs_dir;
+ unsigned char *descriptors;
+ int descriptors_len;
+ int active_config; /* cache val for !sysfs_can_relate_devices */
+};
+
+struct linux_device_handle_priv {
+ int fd;
+ uint32_t caps;
+};
+
+enum reap_action {
+ NORMAL = 0,
+ /* submission failed after the first URB, so await cancellation/completion
+ * of all the others */
+ SUBMIT_FAILED,
+
+ /* cancelled by user or timeout */
+ CANCELLED,
+
+ /* completed multi-URB transfer in non-final URB */
+ COMPLETED_EARLY,
+
+ /* one or more urbs encountered a low-level error */
+ ERROR,
+};
+
+struct linux_transfer_priv {
+ union {
+ struct usbfs_urb *urbs;
+ struct usbfs_urb **iso_urbs;
+ };
+
+ enum reap_action reap_action;
+ int num_urbs;
+ int num_retired;
+ enum libusb_transfer_status reap_status;
+
+ /* next iso packet in user-supplied transfer to be populated */
+ int iso_packet_offset;
+};
+
+static int _get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ char path[PATH_MAX];
+ int fd;
+ int delay = 10000;
+
+ if (usbdev_names)
+ snprintf(path, PATH_MAX, "%s/usbdev%d.%d",
+ usbfs_path, dev->bus_number, dev->device_address);
+ else
+ snprintf(path, PATH_MAX, "%s/%03d/%03d",
+ usbfs_path, dev->bus_number, dev->device_address);
+
+ fd = open(path, mode);
+ if (fd != -1)
+ return fd; /* Success */
+
+ if (errno == ENOENT) {
+ if (!silent)
+ usbi_err(ctx, "File doesn't exist, wait %d ms and try again\n", delay/1000);
+
+ /* Wait 10ms for USB device path creation.*/
+ usleep(delay);
+
+ fd = open(path, mode);
+ if (fd != -1)
+ return fd; /* Success */
+ }
+
+ if (!silent) {
+ usbi_err(ctx, "libusb couldn't open USB device %s: %s",
+ path, strerror(errno));
+ if (errno == EACCES && mode == O_RDWR)
+ usbi_err(ctx, "libusb requires write access to USB "
+ "device nodes.");
+ }
+
+ if (errno == EACCES)
+ return LIBUSB_ERROR_ACCESS;
+ if (errno == ENOENT)
+ return LIBUSB_ERROR_NO_DEVICE;
+ return LIBUSB_ERROR_IO;
+}
+
+static struct linux_device_priv *_device_priv(struct libusb_device *dev)
+{
+ return (struct linux_device_priv *) dev->os_priv;
+}
+
+static struct linux_device_handle_priv *_device_handle_priv(
+ struct libusb_device_handle *handle)
+{
+ return (struct linux_device_handle_priv *) handle->os_priv;
+}
+
+/* check dirent for a /dev/usbdev%d.%d name
+ * optionally return bus/device on success */
+static int _is_usbdev_entry(struct dirent *entry, int *bus_p, int *dev_p)
+{
+ int busnum, devnum;
+
+ if (sscanf(entry->d_name, "usbdev%d.%d", &busnum, &devnum) != 2)
+ return 0;
+
+ usbi_dbg("found: %s", entry->d_name);
+ if (bus_p != NULL)
+ *bus_p = busnum;
+ if (dev_p != NULL)
+ *dev_p = devnum;
+ return 1;
+}
+
+static int check_usb_vfs(const char *dirname)
+{
+ DIR *dir;
+ struct dirent *entry;
+ int found = 0;
+
+ dir = opendir(dirname);
+ if (!dir)
+ return 0;
+
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_name[0] == '.')
+ continue;
+
+ /* We assume if we find any files that it must be the right place */
+ found = 1;
+ break;
+ }
+
+ closedir(dir);
+ return found;
+}
+
+static const char *find_usbfs_path(void)
+{
+ const char *path = "/dev/bus/usb";
+ const char *ret = NULL;
+
+ if (check_usb_vfs(path)) {
+ ret = path;
+ } else {
+ path = "/proc/bus/usb";
+ if (check_usb_vfs(path))
+ ret = path;
+ }
+
+ /* look for /dev/usbdev*.* if the normal places fail */
+ if (ret == NULL) {
+ struct dirent *entry;
+ DIR *dir;
+
+ path = "/dev";
+ dir = opendir(path);
+ if (dir != NULL) {
+ while ((entry = readdir(dir)) != NULL) {
+ if (_is_usbdev_entry(entry, NULL, NULL)) {
+ /* found one; that's enough */
+ ret = path;
+ usbdev_names = 1;
+ break;
+ }
+ }
+ closedir(dir);
+ }
+ }
+
+ if (ret != NULL)
+ usbi_dbg("found usbfs at %s", ret);
+
+ return ret;
+}
+
+/* the monotonic clock is not usable on all systems (e.g. embedded ones often
+ * seem to lack it). fall back to REALTIME if we have to. */
+static clockid_t find_monotonic_clock(void)
+{
+#ifdef CLOCK_MONOTONIC
+ struct timespec ts;
+ int r;
+
+ /* Linux 2.6.28 adds CLOCK_MONOTONIC_RAW but we don't use it
+ * because it's not available through timerfd */
+ r = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (r == 0)
+ return CLOCK_MONOTONIC;
+ usbi_dbg("monotonic clock doesn't work, errno %d", errno);
+#endif
+
+ return CLOCK_REALTIME;
+}
+
+static int kernel_version_ge(int major, int minor, int sublevel)
+{
+ struct utsname uts;
+ int atoms, kmajor, kminor, ksublevel;
+
+ if (uname(&uts) < 0)
+ return -1;
+ atoms = sscanf(uts.release, "%d.%d.%d", &kmajor, &kminor, &ksublevel);
+ if (atoms < 1)
+ return -1;
+
+ if (kmajor > major)
+ return 1;
+ if (kmajor < major)
+ return 0;
+
+ /* kmajor == major */
+ if (atoms < 2)
+ return 0 == minor && 0 == sublevel;
+ if (kminor > minor)
+ return 1;
+ if (kminor < minor)
+ return 0;
+
+ /* kminor == minor */
+ if (atoms < 3)
+ return 0 == sublevel;
+
+ return ksublevel >= sublevel;
+}
+
+static int op_init(struct libusb_context *ctx)
+{
+ struct stat statbuf;
+ int r;
+
+ usbfs_path = find_usbfs_path();
+ if (!usbfs_path) {
+ usbi_err(ctx, "could not find usbfs");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ if (monotonic_clkid == -1)
+ monotonic_clkid = find_monotonic_clock();
+
+ if (supports_flag_bulk_continuation == -1) {
+ /* bulk continuation URB flag available from Linux 2.6.32 */
+ supports_flag_bulk_continuation = kernel_version_ge(2,6,32);
+ if (supports_flag_bulk_continuation == -1) {
+ usbi_err(ctx, "error checking for bulk continuation support");
+ return LIBUSB_ERROR_OTHER;
+ }
+ }
+
+ if (supports_flag_bulk_continuation)
+ usbi_dbg("bulk continuation flag supported");
+
+ if (-1 == supports_flag_zero_packet) {
+ /* zero length packet URB flag fixed since Linux 2.6.31 */
+ supports_flag_zero_packet = kernel_version_ge(2,6,31);
+ if (-1 == supports_flag_zero_packet) {
+ usbi_err(ctx, "error checking for zero length packet support");
+ return LIBUSB_ERROR_OTHER;
+ }
+ }
+
+ if (supports_flag_zero_packet)
+ usbi_dbg("zero length packet flag supported");
+
+ if (-1 == sysfs_has_descriptors) {
+ /* sysfs descriptors has all descriptors since Linux 2.6.26 */
+ sysfs_has_descriptors = kernel_version_ge(2,6,26);
+ if (-1 == sysfs_has_descriptors) {
+ usbi_err(ctx, "error checking for sysfs descriptors");
+ return LIBUSB_ERROR_OTHER;
+ }
+ }
+
+ if (-1 == sysfs_can_relate_devices) {
+ /* sysfs has busnum since Linux 2.6.22 */
+ sysfs_can_relate_devices = kernel_version_ge(2,6,22);
+ if (-1 == sysfs_can_relate_devices) {
+ usbi_err(ctx, "error checking for sysfs busnum");
+ return LIBUSB_ERROR_OTHER;
+ }
+ }
+
+ if (sysfs_can_relate_devices || sysfs_has_descriptors) {
+ r = stat(SYSFS_DEVICE_PATH, &statbuf);
+ if (r != 0 || !S_ISDIR(statbuf.st_mode)) {
+ usbi_warn(ctx, "sysfs not mounted");
+ sysfs_can_relate_devices = 0;
+ sysfs_has_descriptors = 0;
+ }
+ }
+
+ if (sysfs_can_relate_devices)
+ usbi_dbg("sysfs can relate devices");
+
+ if (sysfs_has_descriptors)
+ usbi_dbg("sysfs has complete descriptors");
+
+ usbi_mutex_static_lock(&linux_hotplug_startstop_lock);
+ r = LIBUSB_SUCCESS;
+ if (init_count == 0) {
+ /* start up hotplug event handler */
+ r = linux_start_event_monitor();
+ }
+ if (r == LIBUSB_SUCCESS) {
+ r = linux_scan_devices(ctx);
+ if (r == LIBUSB_SUCCESS)
+ init_count++;
+ else if (init_count == 0)
+ linux_stop_event_monitor();
+ } else
+ usbi_err(ctx, "error starting hotplug event monitor");
+ usbi_mutex_static_unlock(&linux_hotplug_startstop_lock);
+
+ return r;
+}
+
+static void op_exit(void)
+{
+ usbi_mutex_static_lock(&linux_hotplug_startstop_lock);
+ assert(init_count != 0);
+ if (!--init_count) {
+ /* tear down event handler */
+ (void)linux_stop_event_monitor();
+ }
+ usbi_mutex_static_unlock(&linux_hotplug_startstop_lock);
+}
+
+static int linux_start_event_monitor(void)
+{
+#if defined(USE_UDEV)
+ return linux_udev_start_event_monitor();
+#else
+ return linux_netlink_start_event_monitor();
+#endif
+}
+
+static int linux_stop_event_monitor(void)
+{
+#if defined(USE_UDEV)
+ return linux_udev_stop_event_monitor();
+#else
+ return linux_netlink_stop_event_monitor();
+#endif
+}
+
+static int linux_scan_devices(struct libusb_context *ctx)
+{
+ int ret;
+
+ usbi_mutex_static_lock(&linux_hotplug_lock);
+
+#if defined(USE_UDEV)
+ ret = linux_udev_scan_devices(ctx);
+#else
+ ret = linux_default_scan_devices(ctx);
+#endif
+
+ usbi_mutex_static_unlock(&linux_hotplug_lock);
+
+ return ret;
+}
+
+static void op_hotplug_poll(void)
+{
+#if defined(USE_UDEV)
+ linux_udev_hotplug_poll();
+#else
+ linux_netlink_hotplug_poll();
+#endif
+}
+
+static int _open_sysfs_attr(struct libusb_device *dev, const char *attr)
+{
+ struct linux_device_priv *priv = _device_priv(dev);
+ char filename[PATH_MAX];
+ int fd;
+
+ snprintf(filename, PATH_MAX, "%s/%s/%s",
+ SYSFS_DEVICE_PATH, priv->sysfs_dir, attr);
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ usbi_err(DEVICE_CTX(dev),
+ "open %s failed ret=%d errno=%d", filename, fd, errno);
+ return LIBUSB_ERROR_IO;
+ }
+
+ return fd;
+}
+
+/* Note only suitable for attributes which always read >= 0, < 0 is error */
+static int __read_sysfs_attr(struct libusb_context *ctx,
+ const char *devname, const char *attr)
+{
+ char filename[PATH_MAX];
+ FILE *f;
+ int r, value;
+
+ snprintf(filename, PATH_MAX, "%s/%s/%s", SYSFS_DEVICE_PATH,
+ devname, attr);
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ if (errno == ENOENT) {
+ /* File doesn't exist. Assume the device has been
+ disconnected (see trac ticket #70). */
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+ usbi_err(ctx, "open %s failed errno=%d", filename, errno);
+ return LIBUSB_ERROR_IO;
+ }
+
+ r = fscanf(f, "%d", &value);
+ fclose(f);
+ if (r != 1) {
+ usbi_err(ctx, "fscanf %s returned %d, errno=%d", attr, r, errno);
+ return LIBUSB_ERROR_NO_DEVICE; /* For unplug race (trac #70) */
+ }
+ if (value < 0) {
+ usbi_err(ctx, "%s contains a negative value", filename);
+ return LIBUSB_ERROR_IO;
+ }
+
+ return value;
+}
+
+static int op_get_device_descriptor(struct libusb_device *dev,
+ unsigned char *buffer, int *host_endian)
+{
+ struct linux_device_priv *priv = _device_priv(dev);
+
+ *host_endian = sysfs_has_descriptors ? 0 : 1;
+ memcpy(buffer, priv->descriptors, DEVICE_DESC_LENGTH);
+
+ return 0;
+}
+
+/* read the bConfigurationValue for a device */
+static int sysfs_get_active_config(struct libusb_device *dev, int *config)
+{
+ char *endptr;
+ char tmp[5] = {0, 0, 0, 0, 0};
+ long num;
+ int fd;
+ ssize_t r;
+
+ fd = _open_sysfs_attr(dev, "bConfigurationValue");
+ if (fd < 0)
+ return fd;
+
+ r = read(fd, tmp, sizeof(tmp));
+ close(fd);
+ if (r < 0) {
+ usbi_err(DEVICE_CTX(dev),
+ "read bConfigurationValue failed ret=%d errno=%d", r, errno);
+ return LIBUSB_ERROR_IO;
+ } else if (r == 0) {
+ usbi_dbg("device unconfigured");
+ *config = -1;
+ return 0;
+ }
+
+ if (tmp[sizeof(tmp) - 1] != 0) {
+ usbi_err(DEVICE_CTX(dev), "not null-terminated?");
+ return LIBUSB_ERROR_IO;
+ } else if (tmp[0] == 0) {
+ usbi_err(DEVICE_CTX(dev), "no configuration value?");
+ return LIBUSB_ERROR_IO;
+ }
+
+ num = strtol(tmp, &endptr, 10);
+ if (endptr == tmp) {
+ usbi_err(DEVICE_CTX(dev), "error converting '%s' to integer", tmp);
+ return LIBUSB_ERROR_IO;
+ }
+
+ *config = (int) num;
+ return 0;
+}
+
+int linux_get_device_address (struct libusb_context *ctx, int detached,
+ uint8_t *busnum, uint8_t *devaddr,const char *dev_node,
+ const char *sys_name)
+{
+ int sysfs_attr;
+
+ usbi_dbg("getting address for device: %s detached: %d", sys_name, detached);
+ /* can't use sysfs to read the bus and device number if the
+ * device has been detached */
+ if (!sysfs_can_relate_devices || detached || NULL == sys_name) {
+ if (NULL == dev_node) {
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ /* will this work with all supported kernel versions? */
+ if (!strncmp(dev_node, "/dev/bus/usb", 12)) {
+ sscanf (dev_node, "/dev/bus/usb/%hhd/%hhd", busnum, devaddr);
+ } else if (!strncmp(dev_node, "/proc/bus/usb", 13)) {
+ sscanf (dev_node, "/proc/bus/usb/%hhd/%hhd", busnum, devaddr);
+ }
+
+ return LIBUSB_SUCCESS;
+ }
+
+ usbi_dbg("scan %s", sys_name);
+
+ sysfs_attr = __read_sysfs_attr(ctx, sys_name, "busnum");
+ if (0 > sysfs_attr)
+ return sysfs_attr;
+ if (sysfs_attr > 255)
+ return LIBUSB_ERROR_INVALID_PARAM;
+ *busnum = (uint8_t) sysfs_attr;
+
+ sysfs_attr = __read_sysfs_attr(ctx, sys_name, "devnum");
+ if (0 > sysfs_attr)
+ return sysfs_attr;
+ if (sysfs_attr > 255)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ *devaddr = (uint8_t) sysfs_attr;
+
+ usbi_dbg("bus=%d dev=%d", *busnum, *devaddr);
+
+ return LIBUSB_SUCCESS;
+}
+
+/* Return offset of the next descriptor with the given type */
+static int seek_to_next_descriptor(struct libusb_context *ctx,
+ uint8_t descriptor_type, unsigned char *buffer, int size)
+{
+ struct usb_descriptor_header header;
+ int i;
+
+ for (i = 0; size >= 0; i += header.bLength, size -= header.bLength) {
+ if (size == 0)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ if (size < 2) {
+ usbi_err(ctx, "short descriptor read %d/2", size);
+ return LIBUSB_ERROR_IO;
+ }
+ usbi_parse_descriptor(buffer + i, "bb", &header, 0);
+
+ if (i && header.bDescriptorType == descriptor_type)
+ return i;
+ }
+ usbi_err(ctx, "bLength overflow by %d bytes", -size);
+ return LIBUSB_ERROR_IO;
+}
+
+/* Return offset to next config */
+static int seek_to_next_config(struct libusb_context *ctx,
+ unsigned char *buffer, int size)
+{
+ struct libusb_config_descriptor config;
+
+ if (size == 0)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ if (size < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(ctx, "short descriptor read %d/%d",
+ size, LIBUSB_DT_CONFIG_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ usbi_parse_descriptor(buffer, "bbwbbbbb", &config, 0);
+ if (config.bDescriptorType != LIBUSB_DT_CONFIG) {
+ usbi_err(ctx, "descriptor is not a config desc (type 0x%02x)",
+ config.bDescriptorType);
+ return LIBUSB_ERROR_IO;
+ }
+
+ /*
+ * In usbfs the config descriptors are config.wTotalLength bytes apart,
+ * with any short reads from the device appearing as holes in the file.
+ *
+ * In sysfs wTotalLength is ignored, instead the kernel returns a
+ * config descriptor with verified bLength fields, with descriptors
+ * with an invalid bLength removed.
+ */
+ if (sysfs_has_descriptors) {
+ int next = seek_to_next_descriptor(ctx, LIBUSB_DT_CONFIG,
+ buffer, size);
+ if (next == LIBUSB_ERROR_NOT_FOUND)
+ next = size;
+ if (next < 0)
+ return next;
+
+ if (next != config.wTotalLength)
+ usbi_warn(ctx, "config length mismatch wTotalLength "
+ "%d real %d", config.wTotalLength, next);
+ return next;
+ } else {
+ if (config.wTotalLength < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(ctx, "invalid wTotalLength %d",
+ config.wTotalLength);
+ return LIBUSB_ERROR_IO;
+ } else if (config.wTotalLength > size) {
+ usbi_warn(ctx, "short descriptor read %d/%d",
+ size, config.wTotalLength);
+ return size;
+ } else
+ return config.wTotalLength;
+ }
+}
+
+static int op_get_config_descriptor_by_value(struct libusb_device *dev,
+ uint8_t value, unsigned char **buffer, int *host_endian)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ struct linux_device_priv *priv = _device_priv(dev);
+ unsigned char *descriptors = priv->descriptors;
+ int size = priv->descriptors_len;
+ struct libusb_config_descriptor *config;
+
+ *buffer = NULL;
+ /* Unlike the device desc. config descs. are always in raw format */
+ *host_endian = 0;
+
+ /* Skip device header */
+ descriptors += DEVICE_DESC_LENGTH;
+ size -= DEVICE_DESC_LENGTH;
+
+ /* Seek till the config is found, or till "EOF" */
+ while (1) {
+ int next = seek_to_next_config(ctx, descriptors, size);
+ if (next < 0)
+ return next;
+ config = (struct libusb_config_descriptor *)descriptors;
+ if (config->bConfigurationValue == value) {
+ *buffer = descriptors;
+ return next;
+ }
+ size -= next;
+ descriptors += next;
+ }
+}
+
+static int op_get_active_config_descriptor(struct libusb_device *dev,
+ unsigned char *buffer, size_t len, int *host_endian)
+{
+ int r, config;
+ unsigned char *config_desc;
+
+ if (sysfs_can_relate_devices) {
+ r = sysfs_get_active_config(dev, &config);
+ if (r < 0)
+ return r;
+ } else {
+ /* Use cached bConfigurationValue */
+ struct linux_device_priv *priv = _device_priv(dev);
+ config = priv->active_config;
+ }
+ if (config == -1)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ r = op_get_config_descriptor_by_value(dev, config, &config_desc,
+ host_endian);
+ if (r < 0)
+ return r;
+
+ len = MIN(len, r);
+ memcpy(buffer, config_desc, len);
+ return len;
+}
+
+static int op_get_config_descriptor(struct libusb_device *dev,
+ uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian)
+{
+ struct linux_device_priv *priv = _device_priv(dev);
+ unsigned char *descriptors = priv->descriptors;
+ int i, r, size = priv->descriptors_len;
+
+ /* Unlike the device desc. config descs. are always in raw format */
+ *host_endian = 0;
+
+ /* Skip device header */
+ descriptors += DEVICE_DESC_LENGTH;
+ size -= DEVICE_DESC_LENGTH;
+
+ /* Seek till the config is found, or till "EOF" */
+ for (i = 0; ; i++) {
+ r = seek_to_next_config(DEVICE_CTX(dev), descriptors, size);
+ if (r < 0)
+ return r;
+ if (i == config_index)
+ break;
+ size -= r;
+ descriptors += r;
+ }
+
+ len = MIN(len, r);
+ memcpy(buffer, descriptors, len);
+ return len;
+}
+
+/* send a control message to retrieve active configuration */
+static int usbfs_get_active_config(struct libusb_device *dev, int fd)
+{
+ unsigned char active_config = 0;
+ int r;
+
+ struct usbfs_ctrltransfer ctrl = {
+ .bmRequestType = LIBUSB_ENDPOINT_IN,
+ .bRequest = LIBUSB_REQUEST_GET_CONFIGURATION,
+ .wValue = 0,
+ .wIndex = 0,
+ .wLength = 1,
+ .timeout = 1000,
+ .data = &active_config
+ };
+
+ r = ioctl(fd, IOCTL_USBFS_CONTROL, &ctrl);
+ if (r < 0) {
+ if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ /* we hit this error path frequently with buggy devices :( */
+ usbi_warn(DEVICE_CTX(dev),
+ "get_configuration failed ret=%d errno=%d", r, errno);
+ return LIBUSB_ERROR_IO;
+ }
+
+ return active_config;
+}
+
+static int initialize_device(struct libusb_device *dev, uint8_t busnum,
+ uint8_t devaddr, const char *sysfs_dir)
+{
+ struct linux_device_priv *priv = _device_priv(dev);
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ int descriptors_size = 512; /* Begin with a 1024 byte alloc */
+ int fd, speed;
+ ssize_t r;
+
+ dev->bus_number = busnum;
+ dev->device_address = devaddr;
+
+ if (sysfs_dir) {
+ priv->sysfs_dir = malloc(strlen(sysfs_dir) + 1);
+ if (!priv->sysfs_dir)
+ return LIBUSB_ERROR_NO_MEM;
+ strcpy(priv->sysfs_dir, sysfs_dir);
+
+ /* Note speed can contain 1.5, in this case __read_sysfs_attr
+ will stop parsing at the '.' and return 1 */
+ speed = __read_sysfs_attr(DEVICE_CTX(dev), sysfs_dir, "speed");
+ if (speed >= 0) {
+ switch (speed) {
+ case 1: dev->speed = LIBUSB_SPEED_LOW; break;
+ case 12: dev->speed = LIBUSB_SPEED_FULL; break;
+ case 480: dev->speed = LIBUSB_SPEED_HIGH; break;
+ case 5000: dev->speed = LIBUSB_SPEED_SUPER; break;
+ default:
+ usbi_warn(DEVICE_CTX(dev), "Unknown device speed: %d Mbps", speed);
+ }
+ }
+ }
+
+ /* cache descriptors in memory */
+ if (sysfs_has_descriptors)
+ fd = _open_sysfs_attr(dev, "descriptors");
+ else
+ fd = _get_usbfs_fd(dev, O_RDONLY, 0);
+ if (fd < 0)
+ return fd;
+
+ do {
+ descriptors_size *= 2;
+ priv->descriptors = usbi_reallocf(priv->descriptors,
+ descriptors_size);
+ if (!priv->descriptors) {
+ close(fd);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+ /* usbfs has holes in the file */
+ if (!sysfs_has_descriptors) {
+ memset(priv->descriptors + priv->descriptors_len,
+ 0, descriptors_size - priv->descriptors_len);
+ }
+ r = read(fd, priv->descriptors + priv->descriptors_len,
+ descriptors_size - priv->descriptors_len);
+ if (r < 0) {
+ usbi_err(ctx, "read descriptor failed ret=%d errno=%d",
+ fd, errno);
+ close(fd);
+ return LIBUSB_ERROR_IO;
+ }
+ priv->descriptors_len += r;
+ } while (priv->descriptors_len == descriptors_size);
+
+ close(fd);
+
+ if (priv->descriptors_len < DEVICE_DESC_LENGTH) {
+ usbi_err(ctx, "short descriptor read (%d)",
+ priv->descriptors_len);
+ return LIBUSB_ERROR_IO;
+ }
+
+ if (sysfs_can_relate_devices)
+ return LIBUSB_SUCCESS;
+
+ /* cache active config */
+ fd = _get_usbfs_fd(dev, O_RDWR, 1);
+ if (fd < 0) {
+ /* cannot send a control message to determine the active
+ * config. just assume the first one is active. */
+ usbi_warn(ctx, "Missing rw usbfs access; cannot determine "
+ "active configuration descriptor");
+ if (priv->descriptors_len >=
+ (DEVICE_DESC_LENGTH + LIBUSB_DT_CONFIG_SIZE)) {
+ struct libusb_config_descriptor config;
+ usbi_parse_descriptor(
+ priv->descriptors + DEVICE_DESC_LENGTH,
+ "bbwbbbbb", &config, 0);
+ priv->active_config = config.bConfigurationValue;
+ } else
+ priv->active_config = -1; /* No config dt */
+
+ return LIBUSB_SUCCESS;
+ }
+
+ r = usbfs_get_active_config(dev, fd);
+ if (r > 0) {
+ priv->active_config = r;
+ r = LIBUSB_SUCCESS;
+ } else if (r == 0) {
+ /* some buggy devices have a configuration 0, but we're
+ * reaching into the corner of a corner case here, so let's
+ * not support buggy devices in these circumstances.
+ * stick to the specs: a configuration value of 0 means
+ * unconfigured. */
+ usbi_dbg("active cfg 0? assuming unconfigured device");
+ priv->active_config = -1;
+ r = LIBUSB_SUCCESS;
+ } else if (r == LIBUSB_ERROR_IO) {
+ /* buggy devices sometimes fail to report their active config.
+ * assume unconfigured and continue the probing */
+ usbi_warn(ctx, "couldn't query active configuration, assuming"
+ " unconfigured");
+ priv->active_config = -1;
+ r = LIBUSB_SUCCESS;
+ } /* else r < 0, just return the error code */
+
+ close(fd);
+ return r;
+}
+
+static int linux_get_parent_info(struct libusb_device *dev, const char *sysfs_dir)
+{
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ struct libusb_device *it;
+ char *parent_sysfs_dir, *tmp;
+ int ret, add_parent = 1;
+
+ /* XXX -- can we figure out the topology when using usbfs? */
+ if (NULL == sysfs_dir || 0 == strncmp(sysfs_dir, "usb", 3)) {
+ /* either using usbfs or finding the parent of a root hub */
+ return LIBUSB_SUCCESS;
+ }
+
+ parent_sysfs_dir = strdup(sysfs_dir);
+ if (NULL != (tmp = strrchr(parent_sysfs_dir, '.')) ||
+ NULL != (tmp = strrchr(parent_sysfs_dir, '-'))) {
+ dev->port_number = atoi(tmp + 1);
+ *tmp = '\0';
+ } else {
+ usbi_warn(ctx, "Can not parse sysfs_dir: %s, no parent info",
+ parent_sysfs_dir);
+ free (parent_sysfs_dir);
+ return LIBUSB_SUCCESS;
+ }
+
+ /* is the parent a root hub? */
+ if (NULL == strchr(parent_sysfs_dir, '-')) {
+ tmp = parent_sysfs_dir;
+ ret = asprintf (&parent_sysfs_dir, "usb%s", tmp);
+ free (tmp);
+ if (0 > ret) {
+ return LIBUSB_ERROR_NO_MEM;
+ }
+ }
+
+retry:
+ /* find the parent in the context */
+ usbi_mutex_lock(&ctx->usb_devs_lock);
+ list_for_each_entry(it, &ctx->usb_devs, list, struct libusb_device) {
+ struct linux_device_priv *priv = _device_priv(it);
+ if (0 == strcmp (priv->sysfs_dir, parent_sysfs_dir)) {
+ dev->parent_dev = libusb_ref_device(it);
+ break;
+ }
+ }
+ usbi_mutex_unlock(&ctx->usb_devs_lock);
+
+ if (!dev->parent_dev && add_parent) {
+ usbi_dbg("parent_dev %s not enumerated yet, enumerating now",
+ parent_sysfs_dir);
+ sysfs_scan_device(ctx, parent_sysfs_dir);
+ add_parent = 0;
+ goto retry;
+ }
+
+ usbi_dbg("Dev %p (%s) has parent %p (%s) port %d", dev, sysfs_dir,
+ dev->parent_dev, parent_sysfs_dir, dev->port_number);
+
+ free (parent_sysfs_dir);
+
+ return LIBUSB_SUCCESS;
+}
+
+int linux_enumerate_device(struct libusb_context *ctx,
+ uint8_t busnum, uint8_t devaddr, const char *sysfs_dir)
+{
+ unsigned long session_id;
+ struct libusb_device *dev;
+ int r = 0;
+
+ /* FIXME: session ID is not guaranteed unique as addresses can wrap and
+ * will be reused. instead we should add a simple sysfs attribute with
+ * a session ID. */
+ session_id = busnum << 8 | devaddr;
+ usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr,
+ session_id);
+
+ dev = usbi_get_device_by_session_id(ctx, session_id);
+ if (dev) {
+ /* device already exists in the context */
+ usbi_dbg("session_id %ld already exists", session_id);
+ libusb_unref_device(dev);
+ return LIBUSB_SUCCESS;
+ }
+
+ usbi_dbg("allocating new device for %d/%d (session %ld)",
+ busnum, devaddr, session_id);
+ dev = usbi_alloc_device(ctx, session_id);
+ if (!dev)
+ return LIBUSB_ERROR_NO_MEM;
+
+ r = initialize_device(dev, busnum, devaddr, sysfs_dir);
+ if (r < 0)
+ goto out;
+ r = usbi_sanitize_device(dev);
+ if (r < 0)
+ goto out;
+
+ r = linux_get_parent_info(dev, sysfs_dir);
+ if (r < 0)
+ goto out;
+out:
+ if (r < 0)
+ libusb_unref_device(dev);
+ else
+ usbi_connect_device(dev);
+
+ return r;
+}
+
+void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_name)
+{
+ struct libusb_context *ctx;
+
+ usbi_mutex_static_lock(&active_contexts_lock);
+ list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+ linux_enumerate_device(ctx, busnum, devaddr, sys_name);
+ }
+ usbi_mutex_static_unlock(&active_contexts_lock);
+}
+
+void linux_device_disconnected(uint8_t busnum, uint8_t devaddr, const char *sys_name)
+{
+ struct libusb_context *ctx;
+ struct libusb_device *dev;
+ unsigned long session_id = busnum << 8 | devaddr;
+
+ usbi_mutex_static_lock(&active_contexts_lock);
+ list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+ dev = usbi_get_device_by_session_id (ctx, session_id);
+ if (NULL != dev) {
+ usbi_disconnect_device (dev);
+ libusb_unref_device(dev);
+ } else {
+ usbi_dbg("device not found for session %x", session_id);
+ }
+ }
+ usbi_mutex_static_unlock(&active_contexts_lock);
+}
+
+#if !defined(USE_UDEV)
+/* open a bus directory and adds all discovered devices to the context */
+static int usbfs_scan_busdir(struct libusb_context *ctx, uint8_t busnum)
+{
+ DIR *dir;
+ char dirpath[PATH_MAX];
+ struct dirent *entry;
+ int r = LIBUSB_ERROR_IO;
+
+ snprintf(dirpath, PATH_MAX, "%s/%03d", usbfs_path, busnum);
+ usbi_dbg("%s", dirpath);
+ dir = opendir(dirpath);
+ if (!dir) {
+ usbi_err(ctx, "opendir '%s' failed, errno=%d", dirpath, errno);
+ /* FIXME: should handle valid race conditions like hub unplugged
+ * during directory iteration - this is not an error */
+ return r;
+ }
+
+ while ((entry = readdir(dir))) {
+ int devaddr;
+
+ if (entry->d_name[0] == '.')
+ continue;
+
+ devaddr = atoi(entry->d_name);
+ if (devaddr == 0) {
+ usbi_dbg("unknown dir entry %s", entry->d_name);
+ continue;
+ }
+
+ if (linux_enumerate_device(ctx, busnum, (uint8_t) devaddr, NULL)) {
+ usbi_dbg("failed to enumerate dir entry %s", entry->d_name);
+ continue;
+ }
+
+ r = 0;
+ }
+
+ closedir(dir);
+ return r;
+}
+
+static int usbfs_get_device_list(struct libusb_context *ctx)
+{
+ struct dirent *entry;
+ DIR *buses = opendir(usbfs_path);
+ int r = 0;
+
+ if (!buses) {
+ usbi_err(ctx, "opendir buses failed errno=%d", errno);
+ return LIBUSB_ERROR_IO;
+ }
+
+ while ((entry = readdir(buses))) {
+ int busnum;
+
+ if (entry->d_name[0] == '.')
+ continue;
+
+ if (usbdev_names) {
+ int devaddr;
+ if (!_is_usbdev_entry(entry, &busnum, &devaddr))
+ continue;
+
+ r = linux_enumerate_device(ctx, busnum, (uint8_t) devaddr, NULL);
+ if (r < 0) {
+ usbi_dbg("failed to enumerate dir entry %s", entry->d_name);
+ continue;
+ }
+ } else {
+ busnum = atoi(entry->d_name);
+ if (busnum == 0) {
+ usbi_dbg("unknown dir entry %s", entry->d_name);
+ continue;
+ }
+
+ r = usbfs_scan_busdir(ctx, busnum);
+ if (r < 0)
+ break;
+ }
+ }
+
+ closedir(buses);
+ return r;
+
+}
+#endif
+
+static int sysfs_scan_device(struct libusb_context *ctx, const char *devname)
+{
+ uint8_t busnum, devaddr;
+ int ret;
+
+ ret = linux_get_device_address (ctx, 0, &busnum, &devaddr, NULL, devname);
+ if (LIBUSB_SUCCESS != ret) {
+ return ret;
+ }
+
+ return linux_enumerate_device(ctx, busnum & 0xff, devaddr & 0xff,
+ devname);
+}
+
+#if !defined(USE_UDEV)
+static int sysfs_get_device_list(struct libusb_context *ctx)
+{
+ DIR *devices = opendir(SYSFS_DEVICE_PATH);
+ struct dirent *entry;
+ int r = LIBUSB_ERROR_IO;
+
+ if (!devices) {
+ usbi_err(ctx, "opendir devices failed errno=%d", errno);
+ return r;
+ }
+
+ while ((entry = readdir(devices))) {
+ if ((!isdigit(entry->d_name[0]) && strncmp(entry->d_name, "usb", 3))
+ || strchr(entry->d_name, ':'))
+ continue;
+
+ if (sysfs_scan_device(ctx, entry->d_name)) {
+ usbi_dbg("failed to enumerate dir entry %s", entry->d_name);
+ continue;
+ }
+
+ r = 0;
+ }
+
+ closedir(devices);
+ return r;
+}
+
+static int linux_default_scan_devices (struct libusb_context *ctx)
+{
+ /* we can retrieve device list and descriptors from sysfs or usbfs.
+ * sysfs is preferable, because if we use usbfs we end up resuming
+ * any autosuspended USB devices. however, sysfs is not available
+ * everywhere, so we need a usbfs fallback too.
+ *
+ * as described in the "sysfs vs usbfs" comment at the top of this
+ * file, sometimes we have sysfs but not enough information to
+ * relate sysfs devices to usbfs nodes. op_init() determines the
+ * adequacy of sysfs and sets sysfs_can_relate_devices.
+ */
+ if (sysfs_can_relate_devices != 0)
+ return sysfs_get_device_list(ctx);
+ else
+ return usbfs_get_device_list(ctx);
+}
+#endif
+
+static int op_open(struct libusb_device_handle *handle)
+{
+ struct linux_device_handle_priv *hpriv = _device_handle_priv(handle);
+ int r;
+
+ hpriv->fd = _get_usbfs_fd(handle->dev, O_RDWR, 0);
+ if (hpriv->fd < 0) {
+ if (hpriv->fd == LIBUSB_ERROR_NO_DEVICE) {
+ /* device will still be marked as attached if hotplug monitor thread
+ * hasn't processed remove event yet */
+ usbi_mutex_static_lock(&linux_hotplug_lock);
+ if (handle->dev->attached) {
+ usbi_dbg("open failed with no device, but device still attached");
+ linux_device_disconnected(handle->dev->bus_number,
+ handle->dev->device_address, NULL);
+ }
+ usbi_mutex_static_unlock(&linux_hotplug_lock);
+ }
+ return hpriv->fd;
+ }
+
+ r = ioctl(hpriv->fd, IOCTL_USBFS_GET_CAPABILITIES, &hpriv->caps);
+ if (r < 0) {
+ if (errno == ENOTTY)
+ usbi_dbg("getcap not available");
+ else
+ usbi_err(HANDLE_CTX(handle), "getcap failed (%d)", errno);
+ hpriv->caps = 0;
+ if (supports_flag_zero_packet)
+ hpriv->caps |= USBFS_CAP_ZERO_PACKET;
+ if (supports_flag_bulk_continuation)
+ hpriv->caps |= USBFS_CAP_BULK_CONTINUATION;
+ }
+
+ return usbi_add_pollfd(HANDLE_CTX(handle), hpriv->fd, POLLOUT);
+}
+
+static void op_close(struct libusb_device_handle *dev_handle)
+{
+ int fd = _device_handle_priv(dev_handle)->fd;
+ usbi_remove_pollfd(HANDLE_CTX(dev_handle), fd);
+ close(fd);
+}
+
+static int op_get_configuration(struct libusb_device_handle *handle,
+ int *config)
+{
+ int r;
+
+ if (sysfs_can_relate_devices) {
+ r = sysfs_get_active_config(handle->dev, config);
+ } else {
+ r = usbfs_get_active_config(handle->dev,
+ _device_handle_priv(handle)->fd);
+ }
+ if (r < 0)
+ return r;
+
+ if (*config == -1) {
+ usbi_err(HANDLE_CTX(handle), "device unconfigured");
+ *config = 0;
+ }
+
+ return 0;
+}
+
+static int op_set_configuration(struct libusb_device_handle *handle, int config)
+{
+ struct linux_device_priv *priv = _device_priv(handle->dev);
+ int fd = _device_handle_priv(handle)->fd;
+ int r = ioctl(fd, IOCTL_USBFS_SETCONFIG, &config);
+ if (r) {
+ if (errno == EINVAL)
+ return LIBUSB_ERROR_NOT_FOUND;
+ else if (errno == EBUSY)
+ return LIBUSB_ERROR_BUSY;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle), "failed, error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ /* update our cached active config descriptor */
+ priv->active_config = config;
+
+ return LIBUSB_SUCCESS;
+}
+
+static int claim_interface(struct libusb_device_handle *handle, int iface)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ int r = ioctl(fd, IOCTL_USBFS_CLAIMINTF, &iface);
+ if (r) {
+ if (errno == ENOENT)
+ return LIBUSB_ERROR_NOT_FOUND;
+ else if (errno == EBUSY)
+ return LIBUSB_ERROR_BUSY;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle),
+ "claim interface failed, error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+ return 0;
+}
+
+static int release_interface(struct libusb_device_handle *handle, int iface)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ int r = ioctl(fd, IOCTL_USBFS_RELEASEINTF, &iface);
+ if (r) {
+ if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle),
+ "release interface failed, error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+ return 0;
+}
+
+static int op_set_interface(struct libusb_device_handle *handle, int iface,
+ int altsetting)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ struct usbfs_setinterface setintf;
+ int r;
+
+ setintf.interface = iface;
+ setintf.altsetting = altsetting;
+ r = ioctl(fd, IOCTL_USBFS_SETINTF, &setintf);
+ if (r) {
+ if (errno == EINVAL)
+ return LIBUSB_ERROR_NOT_FOUND;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle),
+ "setintf failed error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+
+static int op_clear_halt(struct libusb_device_handle *handle,
+ unsigned char endpoint)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ unsigned int _endpoint = endpoint;
+ int r = ioctl(fd, IOCTL_USBFS_CLEAR_HALT, &_endpoint);
+ if (r) {
+ if (errno == ENOENT)
+ return LIBUSB_ERROR_NOT_FOUND;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle),
+ "clear_halt failed error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+
+static int op_reset_device(struct libusb_device_handle *handle)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ int i, r, ret = 0;
+
+ /* Doing a device reset will cause the usbfs driver to get unbound
+ from any interfaces it is bound to. By voluntarily unbinding
+ the usbfs driver ourself, we stop the kernel from rebinding
+ the interface after reset (which would end up with the interface
+ getting bound to the in kernel driver if any). */
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ if (handle->claimed_interfaces & (1L << i)) {
+ release_interface(handle, i);
+ }
+ }
+
+ usbi_mutex_lock(&handle->lock);
+ r = ioctl(fd, IOCTL_USBFS_RESET, NULL);
+ if (r) {
+ if (errno == ENODEV) {
+ ret = LIBUSB_ERROR_NOT_FOUND;
+ goto out;
+ }
+
+ usbi_err(HANDLE_CTX(handle),
+ "reset failed error %d errno %d", r, errno);
+ ret = LIBUSB_ERROR_OTHER;
+ goto out;
+ }
+
+ /* And re-claim any interfaces which were claimed before the reset */
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ if (handle->claimed_interfaces & (1L << i)) {
+ /*
+ * A driver may have completed modprobing during
+ * IOCTL_USBFS_RESET, and bound itself as soon as
+ * IOCTL_USBFS_RESET released the device lock
+ */
+ r = detach_kernel_driver_and_claim(handle, i);
+ if (r) {
+ usbi_warn(HANDLE_CTX(handle),
+ "failed to re-claim interface %d after reset: %s",
+ i, libusb_error_name(r));
+ handle->claimed_interfaces &= ~(1L << i);
+ ret = LIBUSB_ERROR_NOT_FOUND;
+ }
+ }
+ }
+out:
+ usbi_mutex_unlock(&handle->lock);
+ return ret;
+}
+
+static int do_streams_ioctl(struct libusb_device_handle *handle, long req,
+ uint32_t num_streams, unsigned char *endpoints, int num_endpoints)
+{
+ int r, fd = _device_handle_priv(handle)->fd;
+ struct usbfs_streams *streams;
+
+ if (num_endpoints > 30) /* Max 15 in + 15 out eps */
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ streams = malloc(sizeof(struct usbfs_streams) + num_endpoints);
+ if (!streams)
+ return LIBUSB_ERROR_NO_MEM;
+
+ streams->num_streams = num_streams;
+ streams->num_eps = num_endpoints;
+ memcpy(streams->eps, endpoints, num_endpoints);
+
+ r = ioctl(fd, req, streams);
+
+ free(streams);
+
+ if (r < 0) {
+ if (errno == ENOTTY)
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ else if (errno == EINVAL)
+ return LIBUSB_ERROR_INVALID_PARAM;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle),
+ "streams-ioctl failed error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+ return r;
+}
+
+static int op_alloc_streams(struct libusb_device_handle *handle,
+ uint32_t num_streams, unsigned char *endpoints, int num_endpoints)
+{
+ return do_streams_ioctl(handle, IOCTL_USBFS_ALLOC_STREAMS,
+ num_streams, endpoints, num_endpoints);
+}
+
+static int op_free_streams(struct libusb_device_handle *handle,
+ unsigned char *endpoints, int num_endpoints)
+{
+ return do_streams_ioctl(handle, IOCTL_USBFS_FREE_STREAMS, 0,
+ endpoints, num_endpoints);
+}
+
+static int op_kernel_driver_active(struct libusb_device_handle *handle,
+ int interface)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ struct usbfs_getdriver getdrv;
+ int r;
+
+ getdrv.interface = interface;
+ r = ioctl(fd, IOCTL_USBFS_GETDRIVER, &getdrv);
+ if (r) {
+ if (errno == ENODATA)
+ return 0;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle),
+ "get driver failed error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return (strcmp(getdrv.driver, "usbfs") == 0) ? 0 : 1;
+}
+
+static int op_detach_kernel_driver(struct libusb_device_handle *handle,
+ int interface)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ struct usbfs_ioctl command;
+ struct usbfs_getdriver getdrv;
+ int r;
+
+ command.ifno = interface;
+ command.ioctl_code = IOCTL_USBFS_DISCONNECT;
+ command.data = NULL;
+
+ getdrv.interface = interface;
+ r = ioctl(fd, IOCTL_USBFS_GETDRIVER, &getdrv);
+ if (r == 0 && strcmp(getdrv.driver, "usbfs") == 0)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ r = ioctl(fd, IOCTL_USBFS_IOCTL, &command);
+ if (r) {
+ if (errno == ENODATA)
+ return LIBUSB_ERROR_NOT_FOUND;
+ else if (errno == EINVAL)
+ return LIBUSB_ERROR_INVALID_PARAM;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle),
+ "detach failed error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+
+static int op_attach_kernel_driver(struct libusb_device_handle *handle,
+ int interface)
+{
+ int fd = _device_handle_priv(handle)->fd;
+ struct usbfs_ioctl command;
+ int r;
+
+ command.ifno = interface;
+ command.ioctl_code = IOCTL_USBFS_CONNECT;
+ command.data = NULL;
+
+ r = ioctl(fd, IOCTL_USBFS_IOCTL, &command);
+ if (r < 0) {
+ if (errno == ENODATA)
+ return LIBUSB_ERROR_NOT_FOUND;
+ else if (errno == EINVAL)
+ return LIBUSB_ERROR_INVALID_PARAM;
+ else if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+ else if (errno == EBUSY)
+ return LIBUSB_ERROR_BUSY;
+
+ usbi_err(HANDLE_CTX(handle),
+ "attach failed error %d errno %d", r, errno);
+ return LIBUSB_ERROR_OTHER;
+ } else if (r == 0) {
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ return 0;
+}
+
+static int detach_kernel_driver_and_claim(struct libusb_device_handle *handle,
+ int interface)
+{
+ struct usbfs_disconnect_claim dc;
+ int r, fd = _device_handle_priv(handle)->fd;
+
+ dc.interface = interface;
+ strcpy(dc.driver, "usbfs");
+ dc.flags = USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER;
+ r = ioctl(fd, IOCTL_USBFS_DISCONNECT_CLAIM, &dc);
+ if (r == 0 || (r != 0 && errno != ENOTTY)) {
+ if (r == 0)
+ return 0;
+
+ switch (errno) {
+ case EBUSY:
+ return LIBUSB_ERROR_BUSY;
+ case EINVAL:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ case ENODEV:
+ return LIBUSB_ERROR_NO_DEVICE;
+ }
+ usbi_err(HANDLE_CTX(handle),
+ "disconnect-and-claim failed errno %d", errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ /* Fallback code for kernels which don't support the
+ disconnect-and-claim ioctl */
+ r = op_detach_kernel_driver(handle, interface);
+ if (r != 0 && r != LIBUSB_ERROR_NOT_FOUND)
+ return r;
+
+ return claim_interface(handle, interface);
+}
+
+static int op_claim_interface(struct libusb_device_handle *handle, int iface)
+{
+ if (handle->auto_detach_kernel_driver)
+ return detach_kernel_driver_and_claim(handle, iface);
+ else
+ return claim_interface(handle, iface);
+}
+
+static int op_release_interface(struct libusb_device_handle *handle, int iface)
+{
+ int r;
+
+ r = release_interface(handle, iface);
+ if (r)
+ return r;
+
+ if (handle->auto_detach_kernel_driver)
+ op_attach_kernel_driver(handle, iface);
+
+ return 0;
+}
+
+static void op_destroy_device(struct libusb_device *dev)
+{
+ struct linux_device_priv *priv = _device_priv(dev);
+ if (priv->descriptors)
+ free(priv->descriptors);
+ if (priv->sysfs_dir)
+ free(priv->sysfs_dir);
+}
+
+/* URBs are discarded in reverse order of submission to avoid races. */
+static int discard_urbs(struct usbi_transfer *itransfer, int first, int last_plus_one)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct linux_transfer_priv *tpriv =
+ usbi_transfer_get_os_priv(itransfer);
+ struct linux_device_handle_priv *dpriv =
+ _device_handle_priv(transfer->dev_handle);
+ int i, ret = 0;
+ struct usbfs_urb *urb;
+
+ for (i = last_plus_one - 1; i >= first; i--) {
+ if (LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type)
+ urb = tpriv->iso_urbs[i];
+ else
+ urb = &tpriv->urbs[i];
+
+ if (0 == ioctl(dpriv->fd, IOCTL_USBFS_DISCARDURB, urb))
+ continue;
+
+ if (EINVAL == errno) {
+ usbi_dbg("URB not found --> assuming ready to be reaped");
+ if (i == (last_plus_one - 1))
+ ret = LIBUSB_ERROR_NOT_FOUND;
+ } else if (ENODEV == errno) {
+ usbi_dbg("Device not found for URB --> assuming ready to be reaped");
+ ret = LIBUSB_ERROR_NO_DEVICE;
+ } else {
+ usbi_warn(TRANSFER_CTX(transfer),
+ "unrecognised discard errno %d", errno);
+ ret = LIBUSB_ERROR_OTHER;
+ }
+ }
+ return ret;
+}
+
+static void free_iso_urbs(struct linux_transfer_priv *tpriv)
+{
+ int i;
+ for (i = 0; i < tpriv->num_urbs; i++) {
+ struct usbfs_urb *urb = tpriv->iso_urbs[i];
+ if (!urb)
+ break;
+ free(urb);
+ }
+
+ free(tpriv->iso_urbs);
+ tpriv->iso_urbs = NULL;
+}
+
+static int submit_bulk_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ struct linux_device_handle_priv *dpriv =
+ _device_handle_priv(transfer->dev_handle);
+ struct usbfs_urb *urbs;
+ int is_out = (transfer->endpoint & LIBUSB_ENDPOINT_DIR_MASK)
+ == LIBUSB_ENDPOINT_OUT;
+ int bulk_buffer_len, use_bulk_continuation;
+ int r;
+ int i;
+ size_t alloc_size;
+
+ if (tpriv->urbs)
+ return LIBUSB_ERROR_BUSY;
+
+ if (is_out && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) &&
+ !(dpriv->caps & USBFS_CAP_ZERO_PACKET))
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+
+ /*
+ * Older versions of usbfs place a 16kb limit on bulk URBs. We work
+ * around this by splitting large transfers into 16k blocks, and then
+ * submit all urbs at once. it would be simpler to submit one urb at
+ * a time, but there is a big performance gain doing it this way.
+ *
+ * Newer versions lift the 16k limit (USBFS_CAP_NO_PACKET_SIZE_LIM),
+ * using arbritary large transfers can still be a bad idea though, as
+ * the kernel needs to allocate physical contiguous memory for this,
+ * which may fail for large buffers.
+ *
+ * The kernel solves this problem by splitting the transfer into
+ * blocks itself when the host-controller is scatter-gather capable
+ * (USBFS_CAP_BULK_SCATTER_GATHER), which most controllers are.
+ *
+ * Last, there is the issue of short-transfers when splitting, for
+ * short split-transfers to work reliable USBFS_CAP_BULK_CONTINUATION
+ * is needed, but this is not always available.
+ */
+ if (dpriv->caps & USBFS_CAP_BULK_SCATTER_GATHER) {
+ /* Good! Just submit everything in one go */
+ bulk_buffer_len = transfer->length ? transfer->length : 1;
+ use_bulk_continuation = 0;
+ } else if (dpriv->caps & USBFS_CAP_BULK_CONTINUATION) {
+ /* Split the transfers and use bulk-continuation to
+ avoid issues with short-transfers */
+ bulk_buffer_len = MAX_BULK_BUFFER_LENGTH;
+ use_bulk_continuation = 1;
+ } else if (dpriv->caps & USBFS_CAP_NO_PACKET_SIZE_LIM) {
+ /* Don't split, assume the kernel can alloc the buffer
+ (otherwise the submit will fail with -ENOMEM) */
+ bulk_buffer_len = transfer->length ? transfer->length : 1;
+ use_bulk_continuation = 0;
+ } else {
+ /* Bad, splitting without bulk-continuation, short transfers
+ which end before the last urb will not work reliable! */
+ /* Note we don't warn here as this is "normal" on kernels <
+ 2.6.32 and not a problem for most applications */
+ bulk_buffer_len = MAX_BULK_BUFFER_LENGTH;
+ use_bulk_continuation = 0;
+ }
+
+ int num_urbs = transfer->length / bulk_buffer_len;
+ int last_urb_partial = 0;
+
+ if (transfer->length == 0) {
+ num_urbs = 1;
+ } else if ((transfer->length % bulk_buffer_len) > 0) {
+ last_urb_partial = 1;
+ num_urbs++;
+ }
+ usbi_dbg("need %d urbs for new transfer with length %d", num_urbs,
+ transfer->length);
+ alloc_size = num_urbs * sizeof(struct usbfs_urb);
+ urbs = calloc(1, alloc_size);
+ if (!urbs)
+ return LIBUSB_ERROR_NO_MEM;
+ tpriv->urbs = urbs;
+ tpriv->num_urbs = num_urbs;
+ tpriv->num_retired = 0;
+ tpriv->reap_action = NORMAL;
+ tpriv->reap_status = LIBUSB_TRANSFER_COMPLETED;
+
+ for (i = 0; i < num_urbs; i++) {
+ struct usbfs_urb *urb = &urbs[i];
+ urb->usercontext = itransfer;
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ urb->type = USBFS_URB_TYPE_BULK;
+ urb->stream_id = 0;
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ urb->type = USBFS_URB_TYPE_BULK;
+ urb->stream_id = itransfer->stream_id;
+ break;
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ urb->type = USBFS_URB_TYPE_INTERRUPT;
+ break;
+ }
+ urb->endpoint = transfer->endpoint;
+ urb->buffer = transfer->buffer + (i * bulk_buffer_len);
+ /* don't set the short not ok flag for the last URB */
+ if (use_bulk_continuation && !is_out && (i < num_urbs - 1))
+ urb->flags = USBFS_URB_SHORT_NOT_OK;
+ if (i == num_urbs - 1 && last_urb_partial)
+ urb->buffer_length = transfer->length % bulk_buffer_len;
+ else if (transfer->length == 0)
+ urb->buffer_length = 0;
+ else
+ urb->buffer_length = bulk_buffer_len;
+
+ if (i > 0 && use_bulk_continuation)
+ urb->flags |= USBFS_URB_BULK_CONTINUATION;
+
+ /* we have already checked that the flag is supported */
+ if (is_out && i == num_urbs - 1 &&
+ transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET)
+ urb->flags |= USBFS_URB_ZERO_PACKET;
+
+ r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb);
+ if (r < 0) {
+ if (errno == ENODEV) {
+ r = LIBUSB_ERROR_NO_DEVICE;
+ } else {
+ usbi_err(TRANSFER_CTX(transfer),
+ "submiturb failed error %d errno=%d", r, errno);
+ r = LIBUSB_ERROR_IO;
+ }
+
+ /* if the first URB submission fails, we can simply free up and
+ * return failure immediately. */
+ if (i == 0) {
+ usbi_dbg("first URB failed, easy peasy");
+ free(urbs);
+ tpriv->urbs = NULL;
+ return r;
+ }
+
+ /* if it's not the first URB that failed, the situation is a bit
+ * tricky. we may need to discard all previous URBs. there are
+ * complications:
+ * - discarding is asynchronous - discarded urbs will be reaped
+ * later. the user must not have freed the transfer when the
+ * discarded URBs are reaped, otherwise libusb will be using
+ * freed memory.
+ * - the earlier URBs may have completed successfully and we do
+ * not want to throw away any data.
+ * - this URB failing may be no error; EREMOTEIO means that
+ * this transfer simply didn't need all the URBs we submitted
+ * so, we report that the transfer was submitted successfully and
+ * in case of error we discard all previous URBs. later when
+ * the final reap completes we can report error to the user,
+ * or success if an earlier URB was completed successfully.
+ */
+ tpriv->reap_action = EREMOTEIO == errno ? COMPLETED_EARLY : SUBMIT_FAILED;
+
+ /* The URBs we haven't submitted yet we count as already
+ * retired. */
+ tpriv->num_retired += num_urbs - i;
+
+ /* If we completed short then don't try to discard. */
+ if (COMPLETED_EARLY == tpriv->reap_action)
+ return 0;
+
+ discard_urbs(itransfer, 0, i);
+
+ usbi_dbg("reporting successful submission but waiting for %d "
+ "discards before reporting error", i);
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int submit_iso_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ struct linux_device_handle_priv *dpriv =
+ _device_handle_priv(transfer->dev_handle);
+ struct usbfs_urb **urbs;
+ size_t alloc_size;
+ int num_packets = transfer->num_iso_packets;
+ int i;
+ int this_urb_len = 0;
+ int num_urbs = 1;
+ int packet_offset = 0;
+ unsigned int packet_len;
+ unsigned char *urb_buffer = transfer->buffer;
+
+ if (tpriv->iso_urbs)
+ return LIBUSB_ERROR_BUSY;
+
+ /* usbfs places a 32kb limit on iso URBs. we divide up larger requests
+ * into smaller units to meet such restriction, then fire off all the
+ * units at once. it would be simpler if we just fired one unit at a time,
+ * but there is a big performance gain through doing it this way.
+ *
+ * Newer kernels lift the 32k limit (USBFS_CAP_NO_PACKET_SIZE_LIM),
+ * using arbritary large transfers is still be a bad idea though, as
+ * the kernel needs to allocate physical contiguous memory for this,
+ * which may fail for large buffers.
+ */
+
+ /* calculate how many URBs we need */
+ for (i = 0; i < num_packets; i++) {
+ unsigned int space_remaining = MAX_ISO_BUFFER_LENGTH - this_urb_len;
+ packet_len = transfer->iso_packet_desc[i].length;
+
+ if (packet_len > space_remaining) {
+ num_urbs++;
+ this_urb_len = packet_len;
+ } else {
+ this_urb_len += packet_len;
+ }
+ }
+ usbi_dbg("need %d 32k URBs for transfer", num_urbs);
+
+ alloc_size = num_urbs * sizeof(*urbs);
+ urbs = calloc(1, alloc_size);
+ if (!urbs)
+ return LIBUSB_ERROR_NO_MEM;
+
+ tpriv->iso_urbs = urbs;
+ tpriv->num_urbs = num_urbs;
+ tpriv->num_retired = 0;
+ tpriv->reap_action = NORMAL;
+ tpriv->iso_packet_offset = 0;
+
+ /* allocate + initialize each URB with the correct number of packets */
+ for (i = 0; i < num_urbs; i++) {
+ struct usbfs_urb *urb;
+ unsigned int space_remaining_in_urb = MAX_ISO_BUFFER_LENGTH;
+ int urb_packet_offset = 0;
+ unsigned char *urb_buffer_orig = urb_buffer;
+ int j;
+ int k;
+
+ /* swallow up all the packets we can fit into this URB */
+ while (packet_offset < transfer->num_iso_packets) {
+ packet_len = transfer->iso_packet_desc[packet_offset].length;
+ if (packet_len <= space_remaining_in_urb) {
+ /* throw it in */
+ urb_packet_offset++;
+ packet_offset++;
+ space_remaining_in_urb -= packet_len;
+ urb_buffer += packet_len;
+ } else {
+ /* it can't fit, save it for the next URB */
+ break;
+ }
+ }
+
+ alloc_size = sizeof(*urb)
+ + (urb_packet_offset * sizeof(struct usbfs_iso_packet_desc));
+ urb = calloc(1, alloc_size);
+ if (!urb) {
+ free_iso_urbs(tpriv);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+ urbs[i] = urb;
+
+ /* populate packet lengths */
+ for (j = 0, k = packet_offset - urb_packet_offset;
+ k < packet_offset; k++, j++) {
+ packet_len = transfer->iso_packet_desc[k].length;
+ urb->iso_frame_desc[j].length = packet_len;
+ }
+
+ urb->usercontext = itransfer;
+ urb->type = USBFS_URB_TYPE_ISO;
+ /* FIXME: interface for non-ASAP data? */
+ urb->flags = USBFS_URB_ISO_ASAP;
+ urb->endpoint = transfer->endpoint;
+ urb->number_of_packets = urb_packet_offset;
+ urb->buffer = urb_buffer_orig;
+ }
+
+ /* submit URBs */
+ for (i = 0; i < num_urbs; i++) {
+ int r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urbs[i]);
+ if (r < 0) {
+ if (errno == ENODEV) {
+ r = LIBUSB_ERROR_NO_DEVICE;
+ } else {
+ usbi_err(TRANSFER_CTX(transfer),
+ "submiturb failed error %d errno=%d", r, errno);
+ r = LIBUSB_ERROR_IO;
+ }
+
+ /* if the first URB submission fails, we can simply free up and
+ * return failure immediately. */
+ if (i == 0) {
+ usbi_dbg("first URB failed, easy peasy");
+ free_iso_urbs(tpriv);
+ return r;
+ }
+
+ /* if it's not the first URB that failed, the situation is a bit
+ * tricky. we must discard all previous URBs. there are
+ * complications:
+ * - discarding is asynchronous - discarded urbs will be reaped
+ * later. the user must not have freed the transfer when the
+ * discarded URBs are reaped, otherwise libusb will be using
+ * freed memory.
+ * - the earlier URBs may have completed successfully and we do
+ * not want to throw away any data.
+ * so, in this case we discard all the previous URBs BUT we report
+ * that the transfer was submitted successfully. then later when
+ * the final discard completes we can report error to the user.
+ */
+ tpriv->reap_action = SUBMIT_FAILED;
+
+ /* The URBs we haven't submitted yet we count as already
+ * retired. */
+ tpriv->num_retired = num_urbs - i;
+ discard_urbs(itransfer, 0, i);
+
+ usbi_dbg("reporting successful submission but waiting for %d "
+ "discards before reporting error", i);
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int submit_control_transfer(struct usbi_transfer *itransfer)
+{
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct linux_device_handle_priv *dpriv =
+ _device_handle_priv(transfer->dev_handle);
+ struct usbfs_urb *urb;
+ int r;
+
+ if (tpriv->urbs)
+ return LIBUSB_ERROR_BUSY;
+
+ if (transfer->length - LIBUSB_CONTROL_SETUP_SIZE > MAX_CTRL_BUFFER_LENGTH)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ urb = calloc(1, sizeof(struct usbfs_urb));
+ if (!urb)
+ return LIBUSB_ERROR_NO_MEM;
+ tpriv->urbs = urb;
+ tpriv->num_urbs = 1;
+ tpriv->reap_action = NORMAL;
+
+ urb->usercontext = itransfer;
+ urb->type = USBFS_URB_TYPE_CONTROL;
+ urb->endpoint = transfer->endpoint;
+ urb->buffer = transfer->buffer;
+ urb->buffer_length = transfer->length;
+
+ r = ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb);
+ if (r < 0) {
+ free(urb);
+ tpriv->urbs = NULL;
+ if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(TRANSFER_CTX(transfer),
+ "submiturb failed error %d errno=%d", r, errno);
+ return LIBUSB_ERROR_IO;
+ }
+ return 0;
+}
+
+static int op_submit_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ return submit_control_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ return submit_bulk_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ return submit_bulk_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return submit_iso_transfer(itransfer);
+ default:
+ usbi_err(TRANSFER_CTX(transfer),
+ "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+static int op_cancel_transfer(struct usbi_transfer *itransfer)
+{
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ if (tpriv->reap_action == ERROR)
+ break;
+ /* else, fall through */
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ tpriv->reap_action = CANCELLED;
+ break;
+ default:
+ usbi_err(TRANSFER_CTX(transfer),
+ "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ if (!tpriv->urbs)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ return discard_urbs(itransfer, 0, tpriv->num_urbs);
+}
+
+static void op_clear_transfer_priv(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+
+ /* urbs can be freed also in submit_transfer so lock mutex first */
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ usbi_mutex_lock(&itransfer->lock);
+ if (tpriv->urbs)
+ free(tpriv->urbs);
+ tpriv->urbs = NULL;
+ usbi_mutex_unlock(&itransfer->lock);
+ break;
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ usbi_mutex_lock(&itransfer->lock);
+ if (tpriv->iso_urbs)
+ free_iso_urbs(tpriv);
+ usbi_mutex_unlock(&itransfer->lock);
+ break;
+ default:
+ usbi_err(TRANSFER_CTX(transfer),
+ "unknown endpoint type %d", transfer->type);
+ }
+}
+
+static int handle_bulk_completion(struct usbi_transfer *itransfer,
+ struct usbfs_urb *urb)
+{
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ int urb_idx = urb - tpriv->urbs;
+
+ usbi_mutex_lock(&itransfer->lock);
+ usbi_dbg("handling completion status %d of bulk urb %d/%d", urb->status,
+ urb_idx + 1, tpriv->num_urbs);
+
+ tpriv->num_retired++;
+
+ if (tpriv->reap_action != NORMAL) {
+ /* cancelled, submit_fail, or completed early */
+ usbi_dbg("abnormal reap: urb status %d", urb->status);
+
+ /* even though we're in the process of cancelling, it's possible that
+ * we may receive some data in these URBs that we don't want to lose.
+ * examples:
+ * 1. while the kernel is cancelling all the packets that make up an
+ * URB, a few of them might complete. so we get back a successful
+ * cancellation *and* some data.
+ * 2. we receive a short URB which marks the early completion condition,
+ * so we start cancelling the remaining URBs. however, we're too
+ * slow and another URB completes (or at least completes partially).
+ * (this can't happen since we always use BULK_CONTINUATION.)
+ *
+ * When this happens, our objectives are not to lose any "surplus" data,
+ * and also to stick it at the end of the previously-received data
+ * (closing any holes), so that libusb reports the total amount of
+ * transferred data and presents it in a contiguous chunk.
+ */
+ if (urb->actual_length > 0) {
+ unsigned char *target = transfer->buffer + itransfer->transferred;
+ usbi_dbg("received %d bytes of surplus data", urb->actual_length);
+ if (urb->buffer != target) {
+ usbi_dbg("moving surplus data from offset %d to offset %d",
+ (unsigned char *) urb->buffer - transfer->buffer,
+ target - transfer->buffer);
+ memmove(target, urb->buffer, urb->actual_length);
+ }
+ itransfer->transferred += urb->actual_length;
+ }
+
+ if (tpriv->num_retired == tpriv->num_urbs) {
+ usbi_dbg("abnormal reap: last URB handled, reporting");
+ if (tpriv->reap_action != COMPLETED_EARLY &&
+ tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED)
+ tpriv->reap_status = LIBUSB_TRANSFER_ERROR;
+ goto completed;
+ }
+ goto out_unlock;
+ }
+
+ itransfer->transferred += urb->actual_length;
+
+ /* Many of these errors can occur on *any* urb of a multi-urb
+ * transfer. When they do, we tear down the rest of the transfer.
+ */
+ switch (urb->status) {
+ case 0:
+ break;
+ case -EREMOTEIO: /* short transfer */
+ break;
+ case -ENOENT: /* cancelled */
+ case -ECONNRESET:
+ break;
+ case -ENODEV:
+ case -ESHUTDOWN:
+ usbi_dbg("device removed");
+ tpriv->reap_status = LIBUSB_TRANSFER_NO_DEVICE;
+ goto cancel_remaining;
+ case -EPIPE:
+ usbi_dbg("detected endpoint stall");
+ if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED)
+ tpriv->reap_status = LIBUSB_TRANSFER_STALL;
+ goto cancel_remaining;
+ case -EOVERFLOW:
+ /* overflow can only ever occur in the last urb */
+ usbi_dbg("overflow, actual_length=%d", urb->actual_length);
+ if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED)
+ tpriv->reap_status = LIBUSB_TRANSFER_OVERFLOW;
+ goto completed;
+ case -ETIME:
+ case -EPROTO:
+ case -EILSEQ:
+ case -ECOMM:
+ case -ENOSR:
+ usbi_dbg("low level error %d", urb->status);
+ tpriv->reap_action = ERROR;
+ goto cancel_remaining;
+ default:
+ usbi_warn(ITRANSFER_CTX(itransfer),
+ "unrecognised urb status %d", urb->status);
+ tpriv->reap_action = ERROR;
+ goto cancel_remaining;
+ }
+
+ /* if we're the last urb or we got less data than requested then we're
+ * done */
+ if (urb_idx == tpriv->num_urbs - 1) {
+ usbi_dbg("last URB in transfer --> complete!");
+ goto completed;
+ } else if (urb->actual_length < urb->buffer_length) {
+ usbi_dbg("short transfer %d/%d --> complete!",
+ urb->actual_length, urb->buffer_length);
+ if (tpriv->reap_action == NORMAL)
+ tpriv->reap_action = COMPLETED_EARLY;
+ } else
+ goto out_unlock;
+
+cancel_remaining:
+ if (ERROR == tpriv->reap_action && LIBUSB_TRANSFER_COMPLETED == tpriv->reap_status)
+ tpriv->reap_status = LIBUSB_TRANSFER_ERROR;
+
+ if (tpriv->num_retired == tpriv->num_urbs) /* nothing to cancel */
+ goto completed;
+
+ /* cancel remaining urbs and wait for their completion before
+ * reporting results */
+ discard_urbs(itransfer, urb_idx + 1, tpriv->num_urbs);
+
+out_unlock:
+ usbi_mutex_unlock(&itransfer->lock);
+ return 0;
+
+completed:
+ free(tpriv->urbs);
+ tpriv->urbs = NULL;
+ usbi_mutex_unlock(&itransfer->lock);
+ return CANCELLED == tpriv->reap_action ?
+ usbi_handle_transfer_cancellation(itransfer) :
+ usbi_handle_transfer_completion(itransfer, tpriv->reap_status);
+}
+
+static int handle_iso_completion(struct usbi_transfer *itransfer,
+ struct usbfs_urb *urb)
+{
+ struct libusb_transfer *transfer =
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ int num_urbs = tpriv->num_urbs;
+ int urb_idx = 0;
+ int i;
+ enum libusb_transfer_status status = LIBUSB_TRANSFER_COMPLETED;
+
+ usbi_mutex_lock(&itransfer->lock);
+ for (i = 0; i < num_urbs; i++) {
+ if (urb == tpriv->iso_urbs[i]) {
+ urb_idx = i + 1;
+ break;
+ }
+ }
+ if (urb_idx == 0) {
+ usbi_err(TRANSFER_CTX(transfer), "could not locate urb!");
+ usbi_mutex_unlock(&itransfer->lock);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ usbi_dbg("handling completion status %d of iso urb %d/%d", urb->status,
+ urb_idx, num_urbs);
+
+ /* copy isochronous results back in */
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ struct usbfs_iso_packet_desc *urb_desc = &urb->iso_frame_desc[i];
+ struct libusb_iso_packet_descriptor *lib_desc =
+ &transfer->iso_packet_desc[tpriv->iso_packet_offset++];
+ lib_desc->status = LIBUSB_TRANSFER_COMPLETED;
+ switch (urb_desc->status) {
+ case 0:
+ break;
+ case -ENOENT: /* cancelled */
+ case -ECONNRESET:
+ break;
+ case -ENODEV:
+ case -ESHUTDOWN:
+ usbi_dbg("device removed");
+ lib_desc->status = LIBUSB_TRANSFER_NO_DEVICE;
+ break;
+ case -EPIPE:
+ usbi_dbg("detected endpoint stall");
+ lib_desc->status = LIBUSB_TRANSFER_STALL;
+ break;
+ case -EOVERFLOW:
+ usbi_dbg("overflow error");
+ lib_desc->status = LIBUSB_TRANSFER_OVERFLOW;
+ break;
+ case -ETIME:
+ case -EPROTO:
+ case -EILSEQ:
+ case -ECOMM:
+ case -ENOSR:
+ case -EXDEV:
+ usbi_dbg("low-level USB error %d", urb_desc->status);
+ lib_desc->status = LIBUSB_TRANSFER_ERROR;
+ break;
+ default:
+ usbi_warn(TRANSFER_CTX(transfer),
+ "unrecognised urb status %d", urb_desc->status);
+ lib_desc->status = LIBUSB_TRANSFER_ERROR;
+ break;
+ }
+ lib_desc->actual_length = urb_desc->actual_length;
+ }
+
+ tpriv->num_retired++;
+
+ if (tpriv->reap_action != NORMAL) { /* cancelled or submit_fail */
+ usbi_dbg("CANCEL: urb status %d", urb->status);
+
+ if (tpriv->num_retired == num_urbs) {
+ usbi_dbg("CANCEL: last URB handled, reporting");
+ free_iso_urbs(tpriv);
+ if (tpriv->reap_action == CANCELLED) {
+ usbi_mutex_unlock(&itransfer->lock);
+ return usbi_handle_transfer_cancellation(itransfer);
+ } else {
+ usbi_mutex_unlock(&itransfer->lock);
+ return usbi_handle_transfer_completion(itransfer,
+ LIBUSB_TRANSFER_ERROR);
+ }
+ }
+ goto out;
+ }
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ENOENT: /* cancelled */
+ case -ECONNRESET:
+ break;
+ case -ESHUTDOWN:
+ usbi_dbg("device removed");
+ status = LIBUSB_TRANSFER_NO_DEVICE;
+ break;
+ default:
+ usbi_warn(TRANSFER_CTX(transfer),
+ "unrecognised urb status %d", urb->status);
+ status = LIBUSB_TRANSFER_ERROR;
+ break;
+ }
+
+ /* if we're the last urb then we're done */
+ if (urb_idx == num_urbs) {
+ usbi_dbg("last URB in transfer --> complete!");
+ free_iso_urbs(tpriv);
+ usbi_mutex_unlock(&itransfer->lock);
+ return usbi_handle_transfer_completion(itransfer, status);
+ }
+
+out:
+ usbi_mutex_unlock(&itransfer->lock);
+ return 0;
+}
+
+static int handle_control_completion(struct usbi_transfer *itransfer,
+ struct usbfs_urb *urb)
+{
+ struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+ int status;
+
+ usbi_mutex_lock(&itransfer->lock);
+ usbi_dbg("handling completion status %d", urb->status);
+
+ itransfer->transferred += urb->actual_length;
+
+ if (tpriv->reap_action == CANCELLED) {
+ if (urb->status != 0 && urb->status != -ENOENT)
+ usbi_warn(ITRANSFER_CTX(itransfer),
+ "cancel: unrecognised urb status %d", urb->status);
+ free(tpriv->urbs);
+ tpriv->urbs = NULL;
+ usbi_mutex_unlock(&itransfer->lock);
+ return usbi_handle_transfer_cancellation(itransfer);
+ }
+
+ switch (urb->status) {
+ case 0:
+ status = LIBUSB_TRANSFER_COMPLETED;
+ break;
+ case -ENOENT: /* cancelled */
+ status = LIBUSB_TRANSFER_CANCELLED;
+ break;
+ case -ENODEV:
+ case -ESHUTDOWN:
+ usbi_dbg("device removed");
+ status = LIBUSB_TRANSFER_NO_DEVICE;
+ break;
+ case -EPIPE:
+ usbi_dbg("unsupported control request");
+ status = LIBUSB_TRANSFER_STALL;
+ break;
+ case -EOVERFLOW:
+ usbi_dbg("control overflow error");
+ status = LIBUSB_TRANSFER_OVERFLOW;
+ break;
+ case -ETIME:
+ case -EPROTO:
+ case -EILSEQ:
+ case -ECOMM:
+ case -ENOSR:
+ usbi_dbg("low-level bus error occurred");
+ status = LIBUSB_TRANSFER_ERROR;
+ break;
+ default:
+ usbi_warn(ITRANSFER_CTX(itransfer),
+ "unrecognised urb status %d", urb->status);
+ status = LIBUSB_TRANSFER_ERROR;
+ break;
+ }
+
+ free(tpriv->urbs);
+ tpriv->urbs = NULL;
+ usbi_mutex_unlock(&itransfer->lock);
+ return usbi_handle_transfer_completion(itransfer, status);
+}
+
+static int reap_for_handle(struct libusb_device_handle *handle)
+{
+ struct linux_device_handle_priv *hpriv = _device_handle_priv(handle);
+ int r;
+ struct usbfs_urb *urb;
+ struct usbi_transfer *itransfer;
+ struct libusb_transfer *transfer;
+
+ r = ioctl(hpriv->fd, IOCTL_USBFS_REAPURBNDELAY, &urb);
+ if (r == -1 && errno == EAGAIN)
+ return 1;
+ if (r < 0) {
+ if (errno == ENODEV)
+ return LIBUSB_ERROR_NO_DEVICE;
+
+ usbi_err(HANDLE_CTX(handle), "reap failed error %d errno=%d",
+ r, errno);
+ return LIBUSB_ERROR_IO;
+ }
+
+ itransfer = urb->usercontext;
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ usbi_dbg("urb type=%d status=%d transferred=%d", urb->type, urb->status,
+ urb->actual_length);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return handle_iso_completion(itransfer, urb);
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ return handle_bulk_completion(itransfer, urb);
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ return handle_control_completion(itransfer, urb);
+ default:
+ usbi_err(HANDLE_CTX(handle), "unrecognised endpoint type %x",
+ transfer->type);
+ return LIBUSB_ERROR_OTHER;
+ }
+}
+
+static int op_handle_events(struct libusb_context *ctx,
+ struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready)
+{
+ int r;
+ unsigned int i = 0;
+
+ usbi_mutex_lock(&ctx->open_devs_lock);
+ for (i = 0; i < nfds && num_ready > 0; i++) {
+ struct pollfd *pollfd = &fds[i];
+ struct libusb_device_handle *handle;
+ struct linux_device_handle_priv *hpriv = NULL;
+
+ if (!pollfd->revents)
+ continue;
+
+ num_ready--;
+ list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) {
+ hpriv = _device_handle_priv(handle);
+ if (hpriv->fd == pollfd->fd)
+ break;
+ }
+
+ if (!hpriv || hpriv->fd != pollfd->fd) {
+ usbi_err(ctx, "cannot find handle for fd %d\n",
+ pollfd->fd);
+ continue;
+ }
+
+ if (pollfd->revents & POLLERR) {
+ usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->fd);
+ usbi_handle_disconnect(handle);
+ /* device will still be marked as attached if hotplug monitor thread
+ * hasn't processed remove event yet */
+ usbi_mutex_static_lock(&linux_hotplug_lock);
+ if (handle->dev->attached)
+ linux_device_disconnected(handle->dev->bus_number,
+ handle->dev->device_address, NULL);
+ usbi_mutex_static_unlock(&linux_hotplug_lock);
+ continue;
+ }
+
+ do {
+ r = reap_for_handle(handle);
+ } while (r == 0);
+ if (r == 1 || r == LIBUSB_ERROR_NO_DEVICE)
+ continue;
+ else if (r < 0)
+ goto out;
+ }
+
+ r = 0;
+out:
+ usbi_mutex_unlock(&ctx->open_devs_lock);
+ return r;
+}
+
+static int op_clock_gettime(int clk_id, struct timespec *tp)
+{
+ switch (clk_id) {
+ case USBI_CLOCK_MONOTONIC:
+ return clock_gettime(monotonic_clkid, tp);
+ case USBI_CLOCK_REALTIME:
+ return clock_gettime(CLOCK_REALTIME, tp);
+ default:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+#ifdef USBI_TIMERFD_AVAILABLE
+static clockid_t op_get_timerfd_clockid(void)
+{
+ return monotonic_clkid;
+
+}
+#endif
+
+const struct usbi_os_backend linux_usbfs_backend = {
+ .name = "Linux usbfs",
+ .caps = USBI_CAP_HAS_HID_ACCESS|USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER,
+ .init = op_init,
+ .exit = op_exit,
+ .get_device_list = NULL,
+ .hotplug_poll = op_hotplug_poll,
+ .get_device_descriptor = op_get_device_descriptor,
+ .get_active_config_descriptor = op_get_active_config_descriptor,
+ .get_config_descriptor = op_get_config_descriptor,
+ .get_config_descriptor_by_value = op_get_config_descriptor_by_value,
+
+ .open = op_open,
+ .close = op_close,
+ .get_configuration = op_get_configuration,
+ .set_configuration = op_set_configuration,
+ .claim_interface = op_claim_interface,
+ .release_interface = op_release_interface,
+
+ .set_interface_altsetting = op_set_interface,
+ .clear_halt = op_clear_halt,
+ .reset_device = op_reset_device,
+
+ .alloc_streams = op_alloc_streams,
+ .free_streams = op_free_streams,
+
+ .kernel_driver_active = op_kernel_driver_active,
+ .detach_kernel_driver = op_detach_kernel_driver,
+ .attach_kernel_driver = op_attach_kernel_driver,
+
+ .destroy_device = op_destroy_device,
+
+ .submit_transfer = op_submit_transfer,
+ .cancel_transfer = op_cancel_transfer,
+ .clear_transfer_priv = op_clear_transfer_priv,
+
+ .handle_events = op_handle_events,
+
+ .clock_gettime = op_clock_gettime,
+
+#ifdef USBI_TIMERFD_AVAILABLE
+ .get_timerfd_clockid = op_get_timerfd_clockid,
+#endif
+
+ .device_priv_size = sizeof(struct linux_device_priv),
+ .device_handle_priv_size = sizeof(struct linux_device_handle_priv),
+ .transfer_priv_size = sizeof(struct linux_transfer_priv),
+ .add_iso_packet_size = 0,
+};
diff --git a/e502/libusb-1.0/libusb-1.0/os/linux_usbfs.h b/e502/libusb-1.0/libusb-1.0/os/linux_usbfs.h
new file mode 100644
index 0000000..f2404ab
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/linux_usbfs.h
@@ -0,0 +1,192 @@
+/*
+ * usbfs header structures
+ * Copyright © 2007 Daniel Drake
+ * Copyright © 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
+
+#include
+
+#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;
+ union {
+ int number_of_packets; /* Only used for isoc urbs */
+ unsigned int stream_id; /* Only used with bulk streams */
+ };
+ 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 USBFS_CAP_ZERO_PACKET 0x01
+#define USBFS_CAP_BULK_CONTINUATION 0x02
+#define USBFS_CAP_NO_PACKET_SIZE_LIM 0x04
+#define USBFS_CAP_BULK_SCATTER_GATHER 0x08
+
+#define USBFS_DISCONNECT_CLAIM_IF_DRIVER 0x01
+#define USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER 0x02
+
+struct usbfs_disconnect_claim {
+ unsigned int interface;
+ unsigned int flags;
+ char driver[USBFS_MAXDRIVERNAME + 1];
+};
+
+struct usbfs_streams {
+ unsigned int num_streams; /* Not used by USBDEVFS_FREE_STREAMS */
+ unsigned int num_eps;
+ unsigned char eps[0];
+};
+
+#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)
+#define IOCTL_USBFS_CLAIM_PORT _IOR('U', 24, unsigned int)
+#define IOCTL_USBFS_RELEASE_PORT _IOR('U', 25, unsigned int)
+#define IOCTL_USBFS_GET_CAPABILITIES _IOR('U', 26, __u32)
+#define IOCTL_USBFS_DISCONNECT_CLAIM _IOR('U', 27, struct usbfs_disconnect_claim)
+#define IOCTL_USBFS_ALLOC_STREAMS _IOR('U', 28, struct usbfs_streams)
+#define IOCTL_USBFS_FREE_STREAMS _IOR('U', 29, struct usbfs_streams)
+
+extern usbi_mutex_static_t linux_hotplug_lock;
+
+#if defined(HAVE_LIBUDEV)
+int linux_udev_start_event_monitor(void);
+int linux_udev_stop_event_monitor(void);
+int linux_udev_scan_devices(struct libusb_context *ctx);
+void linux_udev_hotplug_poll(void);
+#else
+int linux_netlink_start_event_monitor(void);
+int linux_netlink_stop_event_monitor(void);
+void linux_netlink_hotplug_poll(void);
+#endif
+
+void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_name);
+void linux_device_disconnected(uint8_t busnum, uint8_t devaddr, const char *sys_name);
+
+int linux_get_device_address (struct libusb_context *ctx, int detached,
+ uint8_t *busnum, uint8_t *devaddr, const char *dev_node,
+ const char *sys_name);
+int linux_enumerate_device(struct libusb_context *ctx,
+ uint8_t busnum, uint8_t devaddr, const char *sysfs_dir);
+
+#endif
diff --git a/e502/libusb-1.0/libusb-1.0/os/netbsd_usb.c b/e502/libusb-1.0/libusb-1.0/os/netbsd_usb.c
new file mode 100644
index 0000000..ed4f50e
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/netbsd_usb.c
@@ -0,0 +1,738 @@
+/*
+ * Copyright © 2011 Martin Pieuchot
+ *
+ * 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
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "libusb.h"
+#include "libusbi.h"
+
+struct device_priv {
+ char devnode[16];
+ int fd;
+
+ unsigned char *cdesc; /* active config descriptor */
+ usb_device_descriptor_t ddesc; /* usb device descriptor */
+};
+
+struct handle_priv {
+ int pipe[2]; /* for event notification */
+ int endpoints[USB_MAX_ENDPOINTS];
+};
+
+/*
+ * Backend functions
+ */
+static int netbsd_get_device_list(struct libusb_context *,
+ struct discovered_devs **);
+static int netbsd_open(struct libusb_device_handle *);
+static void netbsd_close(struct libusb_device_handle *);
+
+static int netbsd_get_device_descriptor(struct libusb_device *, unsigned char *,
+ int *);
+static int netbsd_get_active_config_descriptor(struct libusb_device *,
+ unsigned char *, size_t, int *);
+static int netbsd_get_config_descriptor(struct libusb_device *, uint8_t,
+ unsigned char *, size_t, int *);
+
+static int netbsd_get_configuration(struct libusb_device_handle *, int *);
+static int netbsd_set_configuration(struct libusb_device_handle *, int);
+
+static int netbsd_claim_interface(struct libusb_device_handle *, int);
+static int netbsd_release_interface(struct libusb_device_handle *, int);
+
+static int netbsd_set_interface_altsetting(struct libusb_device_handle *, int,
+ int);
+static int netbsd_clear_halt(struct libusb_device_handle *, unsigned char);
+static int netbsd_reset_device(struct libusb_device_handle *);
+static void netbsd_destroy_device(struct libusb_device *);
+
+static int netbsd_submit_transfer(struct usbi_transfer *);
+static int netbsd_cancel_transfer(struct usbi_transfer *);
+static void netbsd_clear_transfer_priv(struct usbi_transfer *);
+static int netbsd_handle_events(struct libusb_context *ctx, struct pollfd *,
+ nfds_t, int);
+static int netbsd_clock_gettime(int, struct timespec *);
+
+/*
+ * Private functions
+ */
+static int _errno_to_libusb(int);
+static int _cache_active_config_descriptor(struct libusb_device *, int);
+static int _sync_control_transfer(struct usbi_transfer *);
+static int _sync_gen_transfer(struct usbi_transfer *);
+static int _access_endpoint(struct libusb_transfer *);
+
+const struct usbi_os_backend netbsd_backend = {
+ "Synchronous NetBSD backend",
+ 0,
+ NULL, /* init() */
+ NULL, /* exit() */
+ netbsd_get_device_list,
+ NULL, /* hotplug_poll */
+ netbsd_open,
+ netbsd_close,
+
+ netbsd_get_device_descriptor,
+ netbsd_get_active_config_descriptor,
+ netbsd_get_config_descriptor,
+ NULL, /* get_config_descriptor_by_value() */
+
+ netbsd_get_configuration,
+ netbsd_set_configuration,
+
+ netbsd_claim_interface,
+ netbsd_release_interface,
+
+ netbsd_set_interface_altsetting,
+ netbsd_clear_halt,
+ netbsd_reset_device,
+
+ NULL, /* alloc_streams */
+ NULL, /* free_streams */
+
+ NULL, /* kernel_driver_active() */
+ NULL, /* detach_kernel_driver() */
+ NULL, /* attach_kernel_driver() */
+
+ netbsd_destroy_device,
+
+ netbsd_submit_transfer,
+ netbsd_cancel_transfer,
+ netbsd_clear_transfer_priv,
+
+ netbsd_handle_events,
+
+ netbsd_clock_gettime,
+ sizeof(struct device_priv),
+ sizeof(struct handle_priv),
+ 0, /* transfer_priv_size */
+ 0, /* add_iso_packet_size */
+};
+
+int
+netbsd_get_device_list(struct libusb_context * ctx,
+ struct discovered_devs **discdevs)
+{
+ struct libusb_device *dev;
+ struct device_priv *dpriv;
+ struct usb_device_info di;
+ unsigned long session_id;
+ char devnode[16];
+ int fd, err, i;
+
+ usbi_dbg("");
+
+ /* Only ugen(4) is supported */
+ for (i = 0; i < USB_MAX_DEVICES; i++) {
+ /* Control endpoint is always .00 */
+ snprintf(devnode, sizeof(devnode), "/dev/ugen%d.00", i);
+
+ if ((fd = open(devnode, O_RDONLY)) < 0) {
+ if (errno != ENOENT && errno != ENXIO)
+ usbi_err(ctx, "could not open %s", devnode);
+ continue;
+ }
+
+ if (ioctl(fd, USB_GET_DEVICEINFO, &di) < 0)
+ continue;
+
+ session_id = (di.udi_bus << 8 | di.udi_addr);
+ dev = usbi_get_device_by_session_id(ctx, session_id);
+
+ if (dev == NULL) {
+ dev = usbi_alloc_device(ctx, session_id);
+ if (dev == NULL)
+ return (LIBUSB_ERROR_NO_MEM);
+
+ dev->bus_number = di.udi_bus;
+ dev->device_address = di.udi_addr;
+ dev->speed = di.udi_speed;
+
+ dpriv = (struct device_priv *)dev->os_priv;
+ strlcpy(dpriv->devnode, devnode, sizeof(devnode));
+ dpriv->fd = -1;
+
+ if (ioctl(fd, USB_GET_DEVICE_DESC, &dpriv->ddesc) < 0) {
+ err = errno;
+ goto error;
+ }
+
+ dpriv->cdesc = NULL;
+ if (_cache_active_config_descriptor(dev, fd)) {
+ err = errno;
+ goto error;
+ }
+
+ if ((err = usbi_sanitize_device(dev)))
+ goto error;
+ }
+ close(fd);
+
+ if (discovered_devs_append(*discdevs, dev) == NULL)
+ return (LIBUSB_ERROR_NO_MEM);
+
+ libusb_unref_device(dev);
+ }
+
+ return (LIBUSB_SUCCESS);
+
+error:
+ close(fd);
+ libusb_unref_device(dev);
+ return _errno_to_libusb(err);
+}
+
+int
+netbsd_open(struct libusb_device_handle *handle)
+{
+ struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+
+ dpriv->fd = open(dpriv->devnode, O_RDWR);
+ if (dpriv->fd < 0) {
+ dpriv->fd = open(dpriv->devnode, O_RDONLY);
+ if (dpriv->fd < 0)
+ return _errno_to_libusb(errno);
+ }
+
+ usbi_dbg("open %s: fd %d", dpriv->devnode, dpriv->fd);
+
+ if (pipe(hpriv->pipe) < 0)
+ return _errno_to_libusb(errno);
+
+ return usbi_add_pollfd(HANDLE_CTX(handle), hpriv->pipe[0], POLLIN);
+}
+
+void
+netbsd_close(struct libusb_device_handle *handle)
+{
+ struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+
+ usbi_dbg("close: fd %d", dpriv->fd);
+
+ close(dpriv->fd);
+ dpriv->fd = -1;
+
+ usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->pipe[0]);
+
+ close(hpriv->pipe[0]);
+ close(hpriv->pipe[1]);
+}
+
+int
+netbsd_get_device_descriptor(struct libusb_device *dev, unsigned char *buf,
+ int *host_endian)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+
+ usbi_dbg("");
+
+ memcpy(buf, &dpriv->ddesc, DEVICE_DESC_LENGTH);
+
+ *host_endian = 0;
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_get_active_config_descriptor(struct libusb_device *dev,
+ unsigned char *buf, size_t len, int *host_endian)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+ usb_config_descriptor_t *ucd;
+
+ ucd = (usb_config_descriptor_t *) dpriv->cdesc;
+ len = MIN(len, UGETW(ucd->wTotalLength));
+
+ usbi_dbg("len %d", len);
+
+ memcpy(buf, dpriv->cdesc, len);
+
+ *host_endian = 0;
+
+ return len;
+}
+
+int
+netbsd_get_config_descriptor(struct libusb_device *dev, uint8_t idx,
+ unsigned char *buf, size_t len, int *host_endian)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+ struct usb_full_desc ufd;
+ int fd, err;
+
+ usbi_dbg("index %d, len %d", idx, len);
+
+ /* A config descriptor may be requested before opening the device */
+ if (dpriv->fd >= 0) {
+ fd = dpriv->fd;
+ } else {
+ fd = open(dpriv->devnode, O_RDONLY);
+ if (fd < 0)
+ return _errno_to_libusb(errno);
+ }
+
+ ufd.ufd_config_index = idx;
+ ufd.ufd_size = len;
+ ufd.ufd_data = buf;
+
+ if ((ioctl(fd, USB_GET_FULL_DESC, &ufd)) < 0) {
+ err = errno;
+ if (dpriv->fd < 0)
+ close(fd);
+ return _errno_to_libusb(err);
+ }
+
+ if (dpriv->fd < 0)
+ close(fd);
+
+ *host_endian = 0;
+
+ return len;
+}
+
+int
+netbsd_get_configuration(struct libusb_device_handle *handle, int *config)
+{
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+
+ usbi_dbg("");
+
+ if (ioctl(dpriv->fd, USB_GET_CONFIG, config) < 0)
+ return _errno_to_libusb(errno);
+
+ usbi_dbg("configuration %d", *config);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_set_configuration(struct libusb_device_handle *handle, int config)
+{
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+
+ usbi_dbg("configuration %d", config);
+
+ if (ioctl(dpriv->fd, USB_SET_CONFIG, &config) < 0)
+ return _errno_to_libusb(errno);
+
+ return _cache_active_config_descriptor(handle->dev, dpriv->fd);
+}
+
+int
+netbsd_claim_interface(struct libusb_device_handle *handle, int iface)
+{
+ struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
+ int i;
+
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+ hpriv->endpoints[i] = -1;
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_release_interface(struct libusb_device_handle *handle, int iface)
+{
+ struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
+ int i;
+
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+ if (hpriv->endpoints[i] >= 0)
+ close(hpriv->endpoints[i]);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_set_interface_altsetting(struct libusb_device_handle *handle, int iface,
+ int altsetting)
+{
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+ struct usb_alt_interface intf;
+
+ usbi_dbg("iface %d, setting %d", iface, altsetting);
+
+ memset(&intf, 0, sizeof(intf));
+
+ intf.uai_interface_index = iface;
+ intf.uai_alt_no = altsetting;
+
+ if (ioctl(dpriv->fd, USB_SET_ALTINTERFACE, &intf) < 0)
+ return _errno_to_libusb(errno);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint)
+{
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+ struct usb_ctl_request req;
+
+ usbi_dbg("");
+
+ req.ucr_request.bmRequestType = UT_WRITE_ENDPOINT;
+ req.ucr_request.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.ucr_request.wValue, UF_ENDPOINT_HALT);
+ USETW(req.ucr_request.wIndex, endpoint);
+ USETW(req.ucr_request.wLength, 0);
+
+ if (ioctl(dpriv->fd, USB_DO_REQUEST, &req) < 0)
+ return _errno_to_libusb(errno);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_reset_device(struct libusb_device_handle *handle)
+{
+ usbi_dbg("");
+
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+}
+
+void
+netbsd_destroy_device(struct libusb_device *dev)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+
+ usbi_dbg("");
+
+ free(dpriv->cdesc);
+}
+
+int
+netbsd_submit_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer;
+ struct handle_priv *hpriv;
+ int err = 0;
+
+ usbi_dbg("");
+
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ hpriv = (struct handle_priv *)transfer->dev_handle->os_priv;
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ err = _sync_control_transfer(itransfer);
+ break;
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ if (IS_XFEROUT(transfer)) {
+ /* Isochronous write is not supported */
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+ err = _sync_gen_transfer(itransfer);
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ if (IS_XFEROUT(transfer) &&
+ transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) {
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+ err = _sync_gen_transfer(itransfer);
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+
+ if (err)
+ return (err);
+
+ if (write(hpriv->pipe[1], &itransfer, sizeof(itransfer)) < 0)
+ return _errno_to_libusb(errno);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_cancel_transfer(struct usbi_transfer *itransfer)
+{
+ usbi_dbg("");
+
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+}
+
+void
+netbsd_clear_transfer_priv(struct usbi_transfer *itransfer)
+{
+ usbi_dbg("");
+
+ /* Nothing to do */
+}
+
+int
+netbsd_handle_events(struct libusb_context *ctx, struct pollfd *fds, nfds_t nfds,
+ int num_ready)
+{
+ struct libusb_device_handle *handle;
+ struct handle_priv *hpriv = NULL;
+ struct usbi_transfer *itransfer;
+ struct pollfd *pollfd;
+ int i, err = 0;
+
+ usbi_dbg("");
+
+ pthread_mutex_lock(&ctx->open_devs_lock);
+ for (i = 0; i < nfds && num_ready > 0; i++) {
+ pollfd = &fds[i];
+
+ if (!pollfd->revents)
+ continue;
+
+ hpriv = NULL;
+ num_ready--;
+ list_for_each_entry(handle, &ctx->open_devs, list,
+ struct libusb_device_handle) {
+ hpriv = (struct handle_priv *)handle->os_priv;
+
+ if (hpriv->pipe[0] == pollfd->fd)
+ break;
+
+ hpriv = NULL;
+ }
+
+ if (NULL == hpriv) {
+ usbi_dbg("fd %d is not an event pipe!", pollfd->fd);
+ err = ENOENT;
+ break;
+ }
+
+ if (pollfd->revents & POLLERR) {
+ usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->pipe[0]);
+ usbi_handle_disconnect(handle);
+ continue;
+ }
+
+ if (read(hpriv->pipe[0], &itransfer, sizeof(itransfer)) < 0) {
+ err = errno;
+ break;
+ }
+
+ if ((err = usbi_handle_transfer_completion(itransfer,
+ LIBUSB_TRANSFER_COMPLETED)))
+ break;
+ }
+ pthread_mutex_unlock(&ctx->open_devs_lock);
+
+ if (err)
+ return _errno_to_libusb(err);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+netbsd_clock_gettime(int clkid, struct timespec *tp)
+{
+ usbi_dbg("clock %d", clkid);
+
+ if (clkid == USBI_CLOCK_REALTIME)
+ return clock_gettime(CLOCK_REALTIME, tp);
+
+ if (clkid == USBI_CLOCK_MONOTONIC)
+ return clock_gettime(CLOCK_MONOTONIC, tp);
+
+ return (LIBUSB_ERROR_INVALID_PARAM);
+}
+
+int
+_errno_to_libusb(int err)
+{
+ switch (err) {
+ case EIO:
+ return (LIBUSB_ERROR_IO);
+ case EACCES:
+ return (LIBUSB_ERROR_ACCESS);
+ case ENOENT:
+ return (LIBUSB_ERROR_NO_DEVICE);
+ case ENOMEM:
+ return (LIBUSB_ERROR_NO_MEM);
+ }
+
+ usbi_dbg("error: %s", strerror(err));
+
+ return (LIBUSB_ERROR_OTHER);
+}
+
+int
+_cache_active_config_descriptor(struct libusb_device *dev, int fd)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+ struct usb_config_desc ucd;
+ struct usb_full_desc ufd;
+ unsigned char* buf;
+ int len;
+
+ usbi_dbg("fd %d", fd);
+
+ ucd.ucd_config_index = USB_CURRENT_CONFIG_INDEX;
+
+ if ((ioctl(fd, USB_GET_CONFIG_DESC, &ucd)) < 0)
+ return _errno_to_libusb(errno);
+
+ usbi_dbg("active bLength %d", ucd.ucd_desc.bLength);
+
+ len = UGETW(ucd.ucd_desc.wTotalLength);
+ buf = malloc(len);
+ if (buf == NULL)
+ return (LIBUSB_ERROR_NO_MEM);
+
+ ufd.ufd_config_index = ucd.ucd_config_index;
+ ufd.ufd_size = len;
+ ufd.ufd_data = buf;
+
+ usbi_dbg("index %d, len %d", ufd.ufd_config_index, len);
+
+ if ((ioctl(fd, USB_GET_FULL_DESC, &ufd)) < 0) {
+ free(buf);
+ return _errno_to_libusb(errno);
+ }
+
+ if (dpriv->cdesc)
+ free(dpriv->cdesc);
+ dpriv->cdesc = buf;
+
+ return (0);
+}
+
+int
+_sync_control_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer;
+ struct libusb_control_setup *setup;
+ struct device_priv *dpriv;
+ struct usb_ctl_request req;
+
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv;
+ setup = (struct libusb_control_setup *)transfer->buffer;
+
+ usbi_dbg("type %d request %d value %d index %d length %d timeout %d",
+ setup->bmRequestType, setup->bRequest,
+ libusb_le16_to_cpu(setup->wValue),
+ libusb_le16_to_cpu(setup->wIndex),
+ libusb_le16_to_cpu(setup->wLength), transfer->timeout);
+
+ req.ucr_request.bmRequestType = setup->bmRequestType;
+ req.ucr_request.bRequest = setup->bRequest;
+ /* Don't use USETW, libusb already deals with the endianness */
+ (*(uint16_t *)req.ucr_request.wValue) = setup->wValue;
+ (*(uint16_t *)req.ucr_request.wIndex) = setup->wIndex;
+ (*(uint16_t *)req.ucr_request.wLength) = setup->wLength;
+ req.ucr_data = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE;
+
+ if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0)
+ req.ucr_flags = USBD_SHORT_XFER_OK;
+
+ if ((ioctl(dpriv->fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0)
+ return _errno_to_libusb(errno);
+
+ if ((ioctl(dpriv->fd, USB_DO_REQUEST, &req)) < 0)
+ return _errno_to_libusb(errno);
+
+ itransfer->transferred = req.ucr_actlen;
+
+ usbi_dbg("transferred %d", itransfer->transferred);
+
+ return (0);
+}
+
+int
+_access_endpoint(struct libusb_transfer *transfer)
+{
+ struct handle_priv *hpriv;
+ struct device_priv *dpriv;
+ char *s, devnode[16];
+ int fd, endpt;
+ mode_t mode;
+
+ hpriv = (struct handle_priv *)transfer->dev_handle->os_priv;
+ dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv;
+
+ endpt = UE_GET_ADDR(transfer->endpoint);
+ mode = IS_XFERIN(transfer) ? O_RDONLY : O_WRONLY;
+
+ usbi_dbg("endpoint %d mode %d", endpt, mode);
+
+ if (hpriv->endpoints[endpt] < 0) {
+ /* Pick the right node given the control one */
+ strlcpy(devnode, dpriv->devnode, sizeof(devnode));
+ s = strchr(devnode, '.');
+ snprintf(s, 4, ".%02d", endpt);
+
+ /* We may need to read/write to the same endpoint later. */
+ if (((fd = open(devnode, O_RDWR)) < 0) && (errno == ENXIO))
+ if ((fd = open(devnode, mode)) < 0)
+ return (-1);
+
+ hpriv->endpoints[endpt] = fd;
+ }
+
+ return (hpriv->endpoints[endpt]);
+}
+
+int
+_sync_gen_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer;
+ int fd, nr = 1;
+
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ /*
+ * Bulk, Interrupt or Isochronous transfer depends on the
+ * endpoint and thus the node to open.
+ */
+ if ((fd = _access_endpoint(transfer)) < 0)
+ return _errno_to_libusb(errno);
+
+ if ((ioctl(fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0)
+ return _errno_to_libusb(errno);
+
+ if (IS_XFERIN(transfer)) {
+ if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0)
+ if ((ioctl(fd, USB_SET_SHORT_XFER, &nr)) < 0)
+ return _errno_to_libusb(errno);
+
+ nr = read(fd, transfer->buffer, transfer->length);
+ } else {
+ nr = write(fd, transfer->buffer, transfer->length);
+ }
+
+ if (nr < 0)
+ return _errno_to_libusb(errno);
+
+ itransfer->transferred = nr;
+
+ return (0);
+}
diff --git a/e502/libusb-1.0/libusb-1.0/os/openbsd_usb.c b/e502/libusb-1.0/libusb-1.0/os/openbsd_usb.c
new file mode 100644
index 0000000..ff470be
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/openbsd_usb.c
@@ -0,0 +1,832 @@
+/*
+ * Copyright © 2011-2013 Martin Pieuchot
+ *
+ * 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
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "libusb.h"
+#include "libusbi.h"
+
+struct device_priv {
+ char *devname; /* name of the ugen(4) node */
+ int fd; /* device file descriptor */
+
+ unsigned char *cdesc; /* active config descriptor */
+ usb_device_descriptor_t ddesc; /* usb device descriptor */
+};
+
+struct handle_priv {
+ int pipe[2]; /* for event notification */
+ int endpoints[USB_MAX_ENDPOINTS];
+};
+
+/*
+ * Backend functions
+ */
+static int obsd_get_device_list(struct libusb_context *,
+ struct discovered_devs **);
+static int obsd_open(struct libusb_device_handle *);
+static void obsd_close(struct libusb_device_handle *);
+
+static int obsd_get_device_descriptor(struct libusb_device *, unsigned char *,
+ int *);
+static int obsd_get_active_config_descriptor(struct libusb_device *,
+ unsigned char *, size_t, int *);
+static int obsd_get_config_descriptor(struct libusb_device *, uint8_t,
+ unsigned char *, size_t, int *);
+
+static int obsd_get_configuration(struct libusb_device_handle *, int *);
+static int obsd_set_configuration(struct libusb_device_handle *, int);
+
+static int obsd_claim_interface(struct libusb_device_handle *, int);
+static int obsd_release_interface(struct libusb_device_handle *, int);
+
+static int obsd_set_interface_altsetting(struct libusb_device_handle *, int,
+ int);
+static int obsd_clear_halt(struct libusb_device_handle *, unsigned char);
+static int obsd_reset_device(struct libusb_device_handle *);
+static void obsd_destroy_device(struct libusb_device *);
+
+static int obsd_submit_transfer(struct usbi_transfer *);
+static int obsd_cancel_transfer(struct usbi_transfer *);
+static void obsd_clear_transfer_priv(struct usbi_transfer *);
+static int obsd_handle_events(struct libusb_context *ctx, struct pollfd *,
+ nfds_t, int);
+static int obsd_clock_gettime(int, struct timespec *);
+
+/*
+ * Private functions
+ */
+static int _errno_to_libusb(int);
+static int _cache_active_config_descriptor(struct libusb_device *);
+static int _sync_control_transfer(struct usbi_transfer *);
+static int _sync_gen_transfer(struct usbi_transfer *);
+static int _access_endpoint(struct libusb_transfer *);
+
+static int _bus_open(int);
+
+
+const struct usbi_os_backend openbsd_backend = {
+ "Synchronous OpenBSD backend",
+ 0,
+ NULL, /* init() */
+ NULL, /* exit() */
+ obsd_get_device_list,
+ NULL, /* hotplug_poll */
+ obsd_open,
+ obsd_close,
+
+ obsd_get_device_descriptor,
+ obsd_get_active_config_descriptor,
+ obsd_get_config_descriptor,
+ NULL, /* get_config_descriptor_by_value() */
+
+ obsd_get_configuration,
+ obsd_set_configuration,
+
+ obsd_claim_interface,
+ obsd_release_interface,
+
+ obsd_set_interface_altsetting,
+ obsd_clear_halt,
+ obsd_reset_device,
+
+ NULL, /* alloc_streams */
+ NULL, /* free_streams */
+
+ NULL, /* kernel_driver_active() */
+ NULL, /* detach_kernel_driver() */
+ NULL, /* attach_kernel_driver() */
+
+ obsd_destroy_device,
+
+ obsd_submit_transfer,
+ obsd_cancel_transfer,
+ obsd_clear_transfer_priv,
+
+ obsd_handle_events,
+
+ obsd_clock_gettime,
+ sizeof(struct device_priv),
+ sizeof(struct handle_priv),
+ 0, /* transfer_priv_size */
+ 0, /* add_iso_packet_size */
+};
+
+#define DEVPATH "/dev/"
+#define USBDEV DEVPATH "usb"
+
+int
+obsd_get_device_list(struct libusb_context * ctx,
+ struct discovered_devs **discdevs)
+{
+ struct discovered_devs *ddd;
+ struct libusb_device *dev;
+ struct device_priv *dpriv;
+ struct usb_device_info di;
+ struct usb_device_ddesc dd;
+ unsigned long session_id;
+ char devices[USB_MAX_DEVICES];
+ char busnode[16];
+ char *udevname;
+ int fd, addr, i, j;
+
+ usbi_dbg("");
+
+ for (i = 0; i < 8; i++) {
+ snprintf(busnode, sizeof(busnode), USBDEV "%d", i);
+
+ if ((fd = open(busnode, O_RDWR)) < 0) {
+ if (errno != ENOENT && errno != ENXIO)
+ usbi_err(ctx, "could not open %s", busnode);
+ continue;
+ }
+
+ bzero(devices, sizeof(devices));
+ for (addr = 1; addr < USB_MAX_DEVICES; addr++) {
+ if (devices[addr])
+ continue;
+
+ di.udi_addr = addr;
+ if (ioctl(fd, USB_DEVICEINFO, &di) < 0)
+ continue;
+
+ /*
+ * XXX If ugen(4) is attached to the USB device
+ * it will be used.
+ */
+ udevname = NULL;
+ for (j = 0; j < USB_MAX_DEVNAMES; j++)
+ if (!strncmp("ugen", di.udi_devnames[j], 4)) {
+ udevname = strdup(di.udi_devnames[j]);
+ break;
+ }
+
+ session_id = (di.udi_bus << 8 | di.udi_addr);
+ dev = usbi_get_device_by_session_id(ctx, session_id);
+
+ if (dev == NULL) {
+ dev = usbi_alloc_device(ctx, session_id);
+ if (dev == NULL) {
+ close(fd);
+ return (LIBUSB_ERROR_NO_MEM);
+ }
+
+ dev->bus_number = di.udi_bus;
+ dev->device_address = di.udi_addr;
+ dev->speed = di.udi_speed;
+
+ dpriv = (struct device_priv *)dev->os_priv;
+ dpriv->fd = -1;
+ dpriv->cdesc = NULL;
+ dpriv->devname = udevname;
+
+ dd.udd_bus = di.udi_bus;
+ dd.udd_addr = di.udi_addr;
+ if (ioctl(fd, USB_DEVICE_GET_DDESC, &dd) < 0) {
+ libusb_unref_device(dev);
+ continue;
+ }
+ dpriv->ddesc = dd.udd_desc;
+
+ if (_cache_active_config_descriptor(dev)) {
+ libusb_unref_device(dev);
+ continue;
+ }
+
+ if (usbi_sanitize_device(dev)) {
+ libusb_unref_device(dev);
+ continue;
+ }
+ }
+
+ ddd = discovered_devs_append(*discdevs, dev);
+ if (ddd == NULL) {
+ close(fd);
+ return (LIBUSB_ERROR_NO_MEM);
+ }
+ libusb_unref_device(dev);
+
+ *discdevs = ddd;
+ devices[addr] = 1;
+ }
+
+ close(fd);
+ }
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_open(struct libusb_device_handle *handle)
+{
+ struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+ char devnode[16];
+
+ if (dpriv->devname) {
+ /*
+ * Only open ugen(4) attached devices read-write, all
+ * read-only operations are done through the bus node.
+ */
+ snprintf(devnode, sizeof(devnode), DEVPATH "%s.00",
+ dpriv->devname);
+ dpriv->fd = open(devnode, O_RDWR);
+ if (dpriv->fd < 0)
+ return _errno_to_libusb(errno);
+
+ usbi_dbg("open %s: fd %d", devnode, dpriv->fd);
+ }
+
+ if (pipe(hpriv->pipe) < 0)
+ return _errno_to_libusb(errno);
+
+ return usbi_add_pollfd(HANDLE_CTX(handle), hpriv->pipe[0], POLLIN);
+}
+
+void
+obsd_close(struct libusb_device_handle *handle)
+{
+ struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+
+ if (dpriv->devname) {
+ usbi_dbg("close: fd %d", dpriv->fd);
+
+ close(dpriv->fd);
+ dpriv->fd = -1;
+ }
+
+ usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->pipe[0]);
+
+ close(hpriv->pipe[0]);
+ close(hpriv->pipe[1]);
+}
+
+int
+obsd_get_device_descriptor(struct libusb_device *dev, unsigned char *buf,
+ int *host_endian)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+
+ usbi_dbg("");
+
+ memcpy(buf, &dpriv->ddesc, DEVICE_DESC_LENGTH);
+
+ *host_endian = 0;
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_get_active_config_descriptor(struct libusb_device *dev,
+ unsigned char *buf, size_t len, int *host_endian)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+ usb_config_descriptor_t *ucd = (usb_config_descriptor_t *)dpriv->cdesc;
+
+ len = MIN(len, UGETW(ucd->wTotalLength));
+
+ usbi_dbg("len %d", len);
+
+ memcpy(buf, dpriv->cdesc, len);
+
+ *host_endian = 0;
+
+ return (len);
+}
+
+int
+obsd_get_config_descriptor(struct libusb_device *dev, uint8_t idx,
+ unsigned char *buf, size_t len, int *host_endian)
+{
+ struct usb_device_fdesc udf;
+ int fd, err;
+
+ if ((fd = _bus_open(dev->bus_number)) < 0)
+ return _errno_to_libusb(errno);
+
+ udf.udf_bus = dev->bus_number;
+ udf.udf_addr = dev->device_address;
+ udf.udf_config_index = idx;
+ udf.udf_size = len;
+ udf.udf_data = buf;
+
+ usbi_dbg("index %d, len %d", udf.udf_config_index, len);
+
+ if (ioctl(fd, USB_DEVICE_GET_FDESC, &udf) < 0) {
+ err = errno;
+ close(fd);
+ return _errno_to_libusb(err);
+ }
+ close(fd);
+
+ *host_endian = 0;
+
+ return (len);
+}
+
+int
+obsd_get_configuration(struct libusb_device_handle *handle, int *config)
+{
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+ usb_config_descriptor_t *ucd = (usb_config_descriptor_t *)dpriv->cdesc;
+
+ *config = ucd->bConfigurationValue;
+
+ usbi_dbg("bConfigurationValue %d", *config);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_set_configuration(struct libusb_device_handle *handle, int config)
+{
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+
+ if (dpriv->devname == NULL)
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+
+ usbi_dbg("bConfigurationValue %d", config);
+
+ if (ioctl(dpriv->fd, USB_SET_CONFIG, &config) < 0)
+ return _errno_to_libusb(errno);
+
+ return _cache_active_config_descriptor(handle->dev);
+}
+
+int
+obsd_claim_interface(struct libusb_device_handle *handle, int iface)
+{
+ struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
+ int i;
+
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+ hpriv->endpoints[i] = -1;
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_release_interface(struct libusb_device_handle *handle, int iface)
+{
+ struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
+ int i;
+
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+ if (hpriv->endpoints[i] >= 0)
+ close(hpriv->endpoints[i]);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_set_interface_altsetting(struct libusb_device_handle *handle, int iface,
+ int altsetting)
+{
+ struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
+ struct usb_alt_interface intf;
+
+ if (dpriv->devname == NULL)
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+
+ usbi_dbg("iface %d, setting %d", iface, altsetting);
+
+ memset(&intf, 0, sizeof(intf));
+
+ intf.uai_interface_index = iface;
+ intf.uai_alt_no = altsetting;
+
+ if (ioctl(dpriv->fd, USB_SET_ALTINTERFACE, &intf) < 0)
+ return _errno_to_libusb(errno);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint)
+{
+ struct usb_ctl_request req;
+ int fd, err;
+
+ if ((fd = _bus_open(handle->dev->bus_number)) < 0)
+ return _errno_to_libusb(errno);
+
+ usbi_dbg("");
+
+ req.ucr_addr = handle->dev->device_address;
+ req.ucr_request.bmRequestType = UT_WRITE_ENDPOINT;
+ req.ucr_request.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.ucr_request.wValue, UF_ENDPOINT_HALT);
+ USETW(req.ucr_request.wIndex, endpoint);
+ USETW(req.ucr_request.wLength, 0);
+
+ if (ioctl(fd, USB_REQUEST, &req) < 0) {
+ err = errno;
+ close(fd);
+ return _errno_to_libusb(err);
+ }
+ close(fd);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_reset_device(struct libusb_device_handle *handle)
+{
+ usbi_dbg("");
+
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+}
+
+void
+obsd_destroy_device(struct libusb_device *dev)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+
+ usbi_dbg("");
+
+ free(dpriv->cdesc);
+ free(dpriv->devname);
+}
+
+int
+obsd_submit_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer;
+ struct handle_priv *hpriv;
+ int err = 0;
+
+ usbi_dbg("");
+
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ hpriv = (struct handle_priv *)transfer->dev_handle->os_priv;
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ err = _sync_control_transfer(itransfer);
+ break;
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ if (IS_XFEROUT(transfer)) {
+ /* Isochronous write is not supported */
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+ err = _sync_gen_transfer(itransfer);
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ if (IS_XFEROUT(transfer) &&
+ transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) {
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+ err = _sync_gen_transfer(itransfer);
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ err = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
+ }
+
+ if (err)
+ return (err);
+
+ if (write(hpriv->pipe[1], &itransfer, sizeof(itransfer)) < 0)
+ return _errno_to_libusb(errno);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_cancel_transfer(struct usbi_transfer *itransfer)
+{
+ usbi_dbg("");
+
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+}
+
+void
+obsd_clear_transfer_priv(struct usbi_transfer *itransfer)
+{
+ usbi_dbg("");
+
+ /* Nothing to do */
+}
+
+int
+obsd_handle_events(struct libusb_context *ctx, struct pollfd *fds, nfds_t nfds,
+ int num_ready)
+{
+ struct libusb_device_handle *handle;
+ struct handle_priv *hpriv = NULL;
+ struct usbi_transfer *itransfer;
+ struct pollfd *pollfd;
+ int i, err = 0;
+
+ usbi_dbg("");
+
+ pthread_mutex_lock(&ctx->open_devs_lock);
+ for (i = 0; i < nfds && num_ready > 0; i++) {
+ pollfd = &fds[i];
+
+ if (!pollfd->revents)
+ continue;
+
+ hpriv = NULL;
+ num_ready--;
+ list_for_each_entry(handle, &ctx->open_devs, list,
+ struct libusb_device_handle) {
+ hpriv = (struct handle_priv *)handle->os_priv;
+
+ if (hpriv->pipe[0] == pollfd->fd)
+ break;
+
+ hpriv = NULL;
+ }
+
+ if (NULL == hpriv) {
+ usbi_dbg("fd %d is not an event pipe!", pollfd->fd);
+ err = ENOENT;
+ break;
+ }
+
+ if (pollfd->revents & POLLERR) {
+ usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->pipe[0]);
+ usbi_handle_disconnect(handle);
+ continue;
+ }
+
+ if (read(hpriv->pipe[0], &itransfer, sizeof(itransfer)) < 0) {
+ err = errno;
+ break;
+ }
+
+ if ((err = usbi_handle_transfer_completion(itransfer,
+ LIBUSB_TRANSFER_COMPLETED)))
+ break;
+ }
+ pthread_mutex_unlock(&ctx->open_devs_lock);
+
+ if (err)
+ return _errno_to_libusb(err);
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+obsd_clock_gettime(int clkid, struct timespec *tp)
+{
+ usbi_dbg("clock %d", clkid);
+
+ if (clkid == USBI_CLOCK_REALTIME)
+ return clock_gettime(CLOCK_REALTIME, tp);
+
+ if (clkid == USBI_CLOCK_MONOTONIC)
+ return clock_gettime(CLOCK_MONOTONIC, tp);
+
+ return (LIBUSB_ERROR_INVALID_PARAM);
+}
+
+int
+_errno_to_libusb(int err)
+{
+ usbi_dbg("error: %s (%d)", strerror(err), err);
+
+ switch (err) {
+ case EIO:
+ return (LIBUSB_ERROR_IO);
+ case EACCES:
+ return (LIBUSB_ERROR_ACCESS);
+ case ENOENT:
+ return (LIBUSB_ERROR_NO_DEVICE);
+ case ENOMEM:
+ return (LIBUSB_ERROR_NO_MEM);
+ case ETIMEDOUT:
+ return (LIBUSB_ERROR_TIMEOUT);
+ }
+
+ return (LIBUSB_ERROR_OTHER);
+}
+
+int
+_cache_active_config_descriptor(struct libusb_device *dev)
+{
+ struct device_priv *dpriv = (struct device_priv *)dev->os_priv;
+ struct usb_device_cdesc udc;
+ struct usb_device_fdesc udf;
+ unsigned char* buf;
+ int fd, len, err;
+
+ if ((fd = _bus_open(dev->bus_number)) < 0)
+ return _errno_to_libusb(errno);
+
+ usbi_dbg("fd %d, addr %d", fd, dev->device_address);
+
+ udc.udc_bus = dev->bus_number;
+ udc.udc_addr = dev->device_address;
+ udc.udc_config_index = USB_CURRENT_CONFIG_INDEX;
+ if (ioctl(fd, USB_DEVICE_GET_CDESC, &udc) < 0) {
+ err = errno;
+ close(fd);
+ return _errno_to_libusb(errno);
+ }
+
+ usbi_dbg("active bLength %d", udc.udc_desc.bLength);
+
+ len = UGETW(udc.udc_desc.wTotalLength);
+ buf = malloc(len);
+ if (buf == NULL)
+ return (LIBUSB_ERROR_NO_MEM);
+
+ udf.udf_bus = dev->bus_number;
+ udf.udf_addr = dev->device_address;
+ udf.udf_config_index = udc.udc_config_index;
+ udf.udf_size = len;
+ udf.udf_data = buf;
+
+ usbi_dbg("index %d, len %d", udf.udf_config_index, len);
+
+ if (ioctl(fd, USB_DEVICE_GET_FDESC, &udf) < 0) {
+ err = errno;
+ close(fd);
+ free(buf);
+ return _errno_to_libusb(err);
+ }
+ close(fd);
+
+ if (dpriv->cdesc)
+ free(dpriv->cdesc);
+ dpriv->cdesc = buf;
+
+ return (LIBUSB_SUCCESS);
+}
+
+int
+_sync_control_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer;
+ struct libusb_control_setup *setup;
+ struct device_priv *dpriv;
+ struct usb_ctl_request req;
+
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv;
+ setup = (struct libusb_control_setup *)transfer->buffer;
+
+ usbi_dbg("type %x request %x value %x index %d length %d timeout %d",
+ setup->bmRequestType, setup->bRequest,
+ libusb_le16_to_cpu(setup->wValue),
+ libusb_le16_to_cpu(setup->wIndex),
+ libusb_le16_to_cpu(setup->wLength), transfer->timeout);
+
+ req.ucr_addr = transfer->dev_handle->dev->device_address;
+ req.ucr_request.bmRequestType = setup->bmRequestType;
+ req.ucr_request.bRequest = setup->bRequest;
+ /* Don't use USETW, libusb already deals with the endianness */
+ (*(uint16_t *)req.ucr_request.wValue) = setup->wValue;
+ (*(uint16_t *)req.ucr_request.wIndex) = setup->wIndex;
+ (*(uint16_t *)req.ucr_request.wLength) = setup->wLength;
+ req.ucr_data = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE;
+
+ if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0)
+ req.ucr_flags = USBD_SHORT_XFER_OK;
+
+ if (dpriv->devname == NULL) {
+ /*
+ * XXX If the device is not attached to ugen(4) it is
+ * XXX still possible to submit a control transfer but
+ * XXX with the default timeout only.
+ */
+ int fd, err;
+
+ if ((fd = _bus_open(transfer->dev_handle->dev->bus_number)) < 0)
+ return _errno_to_libusb(errno);
+
+ if ((ioctl(fd, USB_REQUEST, &req)) < 0) {
+ err = errno;
+ close(fd);
+ return _errno_to_libusb(err);
+ }
+ close(fd);
+ } else {
+ if ((ioctl(dpriv->fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0)
+ return _errno_to_libusb(errno);
+
+ if ((ioctl(dpriv->fd, USB_DO_REQUEST, &req)) < 0)
+ return _errno_to_libusb(errno);
+ }
+
+ itransfer->transferred = req.ucr_actlen;
+
+ usbi_dbg("transferred %d", itransfer->transferred);
+
+ return (0);
+}
+
+int
+_access_endpoint(struct libusb_transfer *transfer)
+{
+ struct handle_priv *hpriv;
+ struct device_priv *dpriv;
+ char devnode[16];
+ int fd, endpt;
+ mode_t mode;
+
+ hpriv = (struct handle_priv *)transfer->dev_handle->os_priv;
+ dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv;
+
+ endpt = UE_GET_ADDR(transfer->endpoint);
+ mode = IS_XFERIN(transfer) ? O_RDONLY : O_WRONLY;
+
+ usbi_dbg("endpoint %d mode %d", endpt, mode);
+
+ if (hpriv->endpoints[endpt] < 0) {
+ /* Pick the right endpoint node */
+ snprintf(devnode, sizeof(devnode), DEVPATH "%s.%02d",
+ dpriv->devname, endpt);
+
+ /* We may need to read/write to the same endpoint later. */
+ if (((fd = open(devnode, O_RDWR)) < 0) && (errno == ENXIO))
+ if ((fd = open(devnode, mode)) < 0)
+ return (-1);
+
+ hpriv->endpoints[endpt] = fd;
+ }
+
+ return (hpriv->endpoints[endpt]);
+}
+
+int
+_sync_gen_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer;
+ struct device_priv *dpriv;
+ int fd, nr = 1;
+
+ transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ dpriv = (struct device_priv *)transfer->dev_handle->dev->os_priv;
+
+ if (dpriv->devname == NULL)
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+
+ /*
+ * Bulk, Interrupt or Isochronous transfer depends on the
+ * endpoint and thus the node to open.
+ */
+ if ((fd = _access_endpoint(transfer)) < 0)
+ return _errno_to_libusb(errno);
+
+ if ((ioctl(fd, USB_SET_TIMEOUT, &transfer->timeout)) < 0)
+ return _errno_to_libusb(errno);
+
+ if (IS_XFERIN(transfer)) {
+ if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0)
+ if ((ioctl(fd, USB_SET_SHORT_XFER, &nr)) < 0)
+ return _errno_to_libusb(errno);
+
+ nr = read(fd, transfer->buffer, transfer->length);
+ } else {
+ nr = write(fd, transfer->buffer, transfer->length);
+ }
+
+ if (nr < 0)
+ return _errno_to_libusb(errno);
+
+ itransfer->transferred = nr;
+
+ return (0);
+}
+
+int
+_bus_open(int number)
+{
+ char busnode[16];
+
+ snprintf(busnode, sizeof(busnode), USBDEV "%d", number);
+
+ return open(busnode, O_RDWR);
+}
diff --git a/e502/libusb-1.0/libusb-1.0/os/poll_posix.c b/e502/libusb-1.0/libusb-1.0/os/poll_posix.c
new file mode 100644
index 0000000..eeaf5dc
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/poll_posix.c
@@ -0,0 +1,51 @@
+/*
+ * poll_posix: poll compatibility wrapper for POSIX systems
+ * Copyright © 2013 RealVNC Ltd.
+ *
+ * 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
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+#include "libusbi.h"
+
+int usbi_pipe(int pipefd[2])
+{
+ int ret = pipe(pipefd);
+ if (ret != 0) {
+ return ret;
+ }
+ ret = fcntl(pipefd[1], F_GETFL);
+ if (ret == -1) {
+ usbi_dbg("Failed to get pipe fd flags: %d", errno);
+ goto err_close_pipe;
+ }
+ ret = fcntl(pipefd[1], F_SETFL, ret | O_NONBLOCK);
+ if (ret != 0) {
+ usbi_dbg("Failed to set non-blocking on new pipe: %d", errno);
+ goto err_close_pipe;
+ }
+
+ return 0;
+
+err_close_pipe:
+ usbi_close(pipefd[0]);
+ usbi_close(pipefd[1]);
+ return ret;
+}
diff --git a/e502/libusb-1.0/libusb-1.0/os/poll_posix.h b/e502/libusb-1.0/libusb-1.0/os/poll_posix.h
new file mode 100644
index 0000000..5b4b2c9
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/poll_posix.h
@@ -0,0 +1,11 @@
+#ifndef LIBUSB_POLL_POSIX_H
+#define LIBUSB_POLL_POSIX_H
+
+#define usbi_write write
+#define usbi_read read
+#define usbi_close close
+#define usbi_poll poll
+
+int usbi_pipe(int pipefd[2]);
+
+#endif /* LIBUSB_POLL_POSIX_H */
diff --git a/e502/libusb-1.0/libusb-1.0/os/poll_windows.c b/e502/libusb-1.0/libusb-1.0/os/poll_windows.c
new file mode 100644
index 0000000..03160bd
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/poll_windows.c
@@ -0,0 +1,764 @@
+/*
+ * poll_windows: poll compatibility wrapper for Windows
+ * Copyright © 2012-2013 RealVNC Ltd.
+ * Copyright © 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
+ *
+ */
+
+/*
+ * poll() and pipe() Windows compatibility layer for libusb 1.0
+ *
+ * The way this layer works is by using OVERLAPPED with async I/O transfers, as
+ * OVERLAPPED have an associated event which is flagged for I/O completion.
+ *
+ * For USB pollable async I/O, you would typically:
+ * - obtain a Windows HANDLE to a file or device that has been opened in
+ * OVERLAPPED mode
+ * - call usbi_create_fd with this handle to obtain a custom fd.
+ * Note that if you need simultaneous R/W access, you need to call create_fd
+ * twice, once in RW_READ and once in RW_WRITE mode to obtain 2 separate
+ * pollable fds
+ * - leave the core functions call the poll routine and flag POLLIN/POLLOUT
+ *
+ * The pipe pollable synchronous I/O works using the overlapped event associated
+ * with a fake pipe. The read/write functions are only meant to be used in that
+ * context.
+ */
+#include
+#include
+#include
+
+#include "libusb-1.0/libusbi.h"
+
+// Uncomment to debug the polling layer
+//#define DEBUG_POLL_WINDOWS
+#if defined(DEBUG_POLL_WINDOWS)
+#define poll_dbg usbi_dbg
+#else
+// MSVC++ < 2005 cannot use a variadic argument and non MSVC
+// compilers produce warnings if parenthesis are ommitted.
+#if defined(_MSC_VER) && (_MSC_VER < 1400)
+#define poll_dbg
+#else
+#define poll_dbg(...)
+#endif
+#endif
+
+#if defined(_PREFAST_)
+#pragma warning(disable:28719)
+#endif
+
+#define CHECK_INIT_POLLING do {if(!is_polling_set) init_polling();} while(0)
+
+// public fd data
+const struct winfd INVALID_WINFD = {-1, INVALID_HANDLE_VALUE, NULL, NULL, NULL, RW_NONE};
+struct winfd poll_fd[MAX_FDS];
+// internal fd data
+struct {
+ CRITICAL_SECTION mutex; // lock for fds
+ // Additional variables for XP CancelIoEx partial emulation
+ HANDLE original_handle;
+ DWORD thread_id;
+ HANDLE waitEvent;
+} _poll_fd[MAX_FDS];
+
+// globals
+BOOLEAN is_polling_set = FALSE;
+LONG pipe_number = 0;
+static volatile LONG compat_spinlock = 0;
+static HANDLE wait_event;
+
+static VOID CALLBACK cb_wait_event(PVOID lpParameter, BOOLEAN TimerOrWaitFired) {
+ SetEvent(wait_event);
+}
+
+#if !defined(_WIN32_WCE)
+// CancelIoEx, available on Vista and later only, provides the ability to cancel
+// a single transfer (OVERLAPPED) when used. As it may not be part of any of the
+// platform headers, we hook into the Kernel32 system DLL directly to seek it.
+static BOOL (__stdcall *pCancelIoEx)(HANDLE, LPOVERLAPPED) = NULL;
+#define Use_Duplicate_Handles (pCancelIoEx == NULL)
+
+static inline void setup_cancel_io(void)
+{
+ HMODULE hKernel32 = GetModuleHandleA("KERNEL32");
+ if (hKernel32 != NULL) {
+ pCancelIoEx = (BOOL (__stdcall *)(HANDLE,LPOVERLAPPED))
+ GetProcAddress(hKernel32, "CancelIoEx");
+ }
+ usbi_dbg("Will use CancelIo%s for I/O cancellation",
+ Use_Duplicate_Handles?"":"Ex");
+}
+
+static inline BOOL cancel_io(int _index)
+{
+ if ((_index < 0) || (_index >= MAX_FDS)) {
+ return FALSE;
+ }
+
+ if ( (poll_fd[_index].fd < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE)
+ || (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL) ) {
+ return TRUE;
+ }
+ if (poll_fd[_index].itransfer && poll_fd[_index].cancel_fn) {
+ // Cancel outstanding transfer via the specific callback
+ (*poll_fd[_index].cancel_fn)(poll_fd[_index].itransfer);
+ return TRUE;
+ }
+ if (pCancelIoEx != NULL) {
+ return (*pCancelIoEx)(poll_fd[_index].handle, poll_fd[_index].overlapped);
+ }
+ if (_poll_fd[_index].thread_id == GetCurrentThreadId()) {
+ return CancelIo(poll_fd[_index].handle);
+ }
+ usbi_warn(NULL, "Unable to cancel I/O that was started from another thread");
+ return FALSE;
+}
+#else
+#define Use_Duplicate_Handles FALSE
+
+static __inline void setup_cancel_io()
+{
+ // No setup needed on WinCE
+}
+
+static __inline BOOL cancel_io(int _index)
+{
+ if ((_index < 0) || (_index >= MAX_FDS)) {
+ return FALSE;
+ }
+ if ( (poll_fd[_index].fd < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE)
+ || (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL) ) {
+ return TRUE;
+ }
+ if (poll_fd[_index].itransfer && poll_fd[_index].cancel_fn) {
+ // Cancel outstanding transfer via the specific callback
+ (*poll_fd[_index].cancel_fn)(poll_fd[_index].itransfer);
+ }
+ return TRUE;
+}
+#endif
+
+// Init
+void init_polling(void)
+{
+ int i;
+
+ while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) {
+ SleepEx(0, TRUE);
+ }
+ if (!is_polling_set) {
+ setup_cancel_io();
+ for (i=0; ihEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if(overlapped->hEvent == NULL) {
+ free (overlapped);
+ return NULL;
+ }
+ return overlapped;
+}
+
+static void free_overlapped(OVERLAPPED *overlapped)
+{
+ if (overlapped == NULL)
+ return;
+
+ if ( (overlapped->hEvent != 0)
+ && (overlapped->hEvent != INVALID_HANDLE_VALUE) ) {
+ CloseHandle(overlapped->hEvent);
+ }
+ free(overlapped);
+}
+
+void exit_polling(void)
+{
+ int i;
+
+ while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) {
+ SleepEx(0, TRUE);
+ }
+ if (is_polling_set) {
+ is_polling_set = FALSE;
+
+ for (i=0; iInternal = STATUS_PENDING;
+ overlapped->InternalHigh = 0;
+
+ for (i=0; i= 0) {
+ LeaveCriticalSection(&_poll_fd[i].mutex);
+ continue;
+ }
+
+ // Use index as the unique fd number
+ poll_fd[i].fd = i;
+ // Read end of the "pipe"
+ filedes[0] = poll_fd[i].fd;
+ // We can use the same handle for both ends
+ filedes[1] = filedes[0];
+
+ poll_fd[i].handle = DUMMY_HANDLE;
+ poll_fd[i].overlapped = overlapped;
+ // There's no polling on the write end, so we just use READ for our needs
+ poll_fd[i].rw = RW_READ;
+ _poll_fd[i].original_handle = INVALID_HANDLE_VALUE;
+ LeaveCriticalSection(&_poll_fd[i].mutex);
+ return 0;
+ }
+ }
+ free_overlapped(overlapped);
+ return -1;
+}
+
+/*
+ * Create both an fd and an OVERLAPPED from an open Windows handle, so that
+ * it can be used with our polling function
+ * The handle MUST support overlapped transfers (usually requires CreateFile
+ * with FILE_FLAG_OVERLAPPED)
+ * Return a pollable file descriptor struct, or INVALID_WINFD on error
+ *
+ * Note that the fd returned by this function is a per-transfer fd, rather
+ * than a per-session fd and cannot be used for anything else but our
+ * custom functions (the fd itself points to the NUL: device)
+ * if you plan to do R/W on the same handle, you MUST create 2 fds: one for
+ * read and one for write. Using a single R/W fd is unsupported and will
+ * produce unexpected results
+ */
+struct winfd usbi_create_fd(HANDLE handle, int access_mode, struct usbi_transfer *itransfer, cancel_transfer *cancel_fn)
+{
+ int i;
+ struct winfd wfd = INVALID_WINFD;
+ OVERLAPPED* overlapped = NULL;
+
+ CHECK_INIT_POLLING;
+
+ if ((handle == 0) || (handle == INVALID_HANDLE_VALUE)) {
+ return INVALID_WINFD;
+ }
+
+ wfd.itransfer = itransfer;
+ wfd.cancel_fn = cancel_fn;
+
+ if ((access_mode != RW_READ) && (access_mode != RW_WRITE)) {
+ usbi_warn(NULL, "only one of RW_READ or RW_WRITE are supported.\n"
+ "If you want to poll for R/W simultaneously, create multiple fds from the same handle.");
+ return INVALID_WINFD;
+ }
+ if (access_mode == RW_READ) {
+ wfd.rw = RW_READ;
+ } else {
+ wfd.rw = RW_WRITE;
+ }
+
+ overlapped = create_overlapped();
+ if(overlapped == NULL) {
+ return INVALID_WINFD;
+ }
+
+ for (i=0; i= 0) {
+ LeaveCriticalSection(&_poll_fd[i].mutex);
+ continue;
+ }
+ // Use index as the unique fd number
+ wfd.fd = i;
+ // Attempt to emulate some of the CancelIoEx behaviour on platforms
+ // that don't have it
+ if (Use_Duplicate_Handles) {
+ _poll_fd[i].thread_id = GetCurrentThreadId();
+ if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(),
+ &wfd.handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
+ usbi_dbg("could not duplicate handle for CancelIo - using original one");
+ wfd.handle = handle;
+ // Make sure we won't close the original handle on fd deletion then
+ _poll_fd[i].original_handle = INVALID_HANDLE_VALUE;
+ } else {
+ _poll_fd[i].original_handle = handle;
+ }
+ } else {
+ wfd.handle = handle;
+ }
+ wfd.overlapped = overlapped;
+ memcpy(&poll_fd[i], &wfd, sizeof(struct winfd));
+ LeaveCriticalSection(&_poll_fd[i].mutex);
+ return wfd;
+ }
+ }
+ free_overlapped(overlapped);
+ return INVALID_WINFD;
+}
+
+static void _free_index(int _index)
+{
+
+
+
+ // Cancel any async IO (Don't care about the validity of our handles for this)
+ cancel_io(_index);
+ // close the duplicate handle (if we have an actual duplicate)
+ if (Use_Duplicate_Handles) {
+ if (_poll_fd[_index].original_handle != INVALID_HANDLE_VALUE) {
+ CloseHandle(poll_fd[_index].handle);
+ }
+ _poll_fd[_index].original_handle = INVALID_HANDLE_VALUE;
+ _poll_fd[_index].thread_id = 0;
+ }
+
+ if (_poll_fd[_index].waitEvent != INVALID_HANDLE_VALUE) {
+ UnregisterWait(_poll_fd[_index].waitEvent);
+ _poll_fd[_index].waitEvent = INVALID_HANDLE_VALUE;
+ }
+
+ free_overlapped(poll_fd[_index].overlapped);
+ poll_fd[_index] = INVALID_WINFD;
+}
+
+/*
+ * Release a pollable file descriptor.
+ *
+ * Note that the associated Windows handle is not closed by this call
+ */
+void usbi_free_fd(struct winfd *wfd)
+{
+ int _index;
+
+ CHECK_INIT_POLLING;
+
+ _index = _fd_to_index_and_lock(wfd->fd);
+ if (_index < 0) {
+ return;
+ }
+ _free_index(_index);
+ *wfd = INVALID_WINFD;
+ LeaveCriticalSection(&_poll_fd[_index].mutex);
+}
+
+/*
+ * The functions below perform various conversions between fd, handle and OVERLAPPED
+ */
+struct winfd fd_to_winfd(int fd)
+{
+ int i;
+ struct winfd wfd;
+
+ CHECK_INIT_POLLING;
+
+ if (fd < 0)
+ return INVALID_WINFD;
+
+ for (i=0; i= 0) {
+ LeaveCriticalSection(&_poll_fd[_index].mutex);
+ }
+ usbi_warn(NULL, "invalid fd");
+ triggered = -1;
+ goto poll_exit;
+ }
+
+ // IN or OUT must match our fd direction
+ if ((fds[i].events & POLLIN) && (poll_fd[_index].rw != RW_READ)) {
+ fds[i].revents |= POLLNVAL | POLLERR;
+ errno = EBADF;
+ usbi_warn(NULL, "attempted POLLIN on fd without READ access");
+ LeaveCriticalSection(&_poll_fd[_index].mutex);
+ triggered = -1;
+ goto poll_exit;
+ }
+
+ if ((fds[i].events & POLLOUT) && (poll_fd[_index].rw != RW_WRITE)) {
+ fds[i].revents |= POLLNVAL | POLLERR;
+ errno = EBADF;
+ usbi_warn(NULL, "attempted POLLOUT on fd without WRITE access");
+ LeaveCriticalSection(&_poll_fd[_index].mutex);
+ triggered = -1;
+ goto poll_exit;
+ }
+
+ // The following macro only works if overlapped I/O was reported pending
+ if ( (HasOverlappedIoCompleted(poll_fd[_index].overlapped))
+ || (HasOverlappedIoCompletedSync(poll_fd[_index].overlapped)) ) {
+ poll_dbg(" completed");
+ // checks above should ensure this works:
+ fds[i].revents = fds[i].events;
+ triggered++;
+ } else {
+ if (_poll_fd[_index].waitEvent == INVALID_HANDLE_VALUE) {
+ if (!RegisterWaitForSingleObject(&_poll_fd[_index].waitEvent,
+ poll_fd[_index].overlapped->hEvent,
+ cb_wait_event,
+ NULL, INFINITE, WT_EXECUTEONLYONCE)) {
+ LeaveCriticalSection(&_poll_fd[_index].mutex);
+ triggered = -1;
+ goto poll_exit;
+ }
+ }
+
+ handles_to_wait_on[nb_handles_to_wait_on] = poll_fd[_index].overlapped->hEvent;
+ handle_to_index[nb_handles_to_wait_on] = i;
+ nb_handles_to_wait_on++;
+ }
+ LeaveCriticalSection(&_poll_fd[_index].mutex);
+ }
+
+ // If nothing was triggered, wait on all fds that require it
+ if ((timeout != 0) && (triggered == 0) && (nb_handles_to_wait_on != 0)) {
+ if (timeout < 0) {
+ poll_dbg("starting infinite wait for %d handles...", (int)nb_handles_to_wait_on);
+ } else {
+ poll_dbg("starting %d ms wait for %d handles...", timeout, (int)nb_handles_to_wait_on);
+ }
+
+ if ((ret = WaitForSingleObject(wait_event, timeout)) == WAIT_OBJECT_0) {
+ ResetEvent(wait_event);
+ for (object_index = 0; object_index < (int)nb_handles_to_wait_on; object_index++) {
+ if (WaitForSingleObject(handles_to_wait_on[object_index], 0) == WAIT_OBJECT_0) {
+
+ i = handle_to_index[object_index];
+ _index = _fd_to_index_and_lock(fds[i].fd);
+ fds[i].revents = fds[i].events;
+ UnregisterWait(_poll_fd[_index].waitEvent);
+ _poll_fd[_index].waitEvent = INVALID_HANDLE_VALUE;
+
+ triggered++;
+ if (_index >= 0) {
+ LeaveCriticalSection(&_poll_fd[_index].mutex);
+ }
+ }
+ }
+ } else if (ret == WAIT_TIMEOUT) {
+ poll_dbg(" timed out");
+ //triggered = 0; // 0 = timeout
+ } else {
+ DWORD err = GetLastError();
+ errno = EIO;
+ triggered = -1; // error
+ poll_dbg("err = 0x%08X", err);
+ }
+ }
+
+poll_exit:
+ if (handles_to_wait_on != NULL) {
+ free(handles_to_wait_on);
+ }
+ if (handle_to_index != NULL) {
+ free(handle_to_index);
+ }
+ return triggered;
+}
+
+/*
+ * close a fake pipe fd
+ */
+int usbi_close(int fd)
+{
+ int _index;
+ int r = -1;
+
+ CHECK_INIT_POLLING;
+
+ _index = _fd_to_index_and_lock(fd);
+
+ if (_index < 0) {
+ errno = EBADF;
+ } else {
+ free_overlapped(poll_fd[_index].overlapped);
+ poll_fd[_index] = INVALID_WINFD;
+ LeaveCriticalSection(&_poll_fd[_index].mutex);
+ }
+ return r;
+}
+
+/*
+ * synchronous write for fake "pipe" signaling
+ */
+ssize_t usbi_write(int fd, const void *buf, size_t count)
+{
+ int _index;
+ UNUSED(buf);
+
+ CHECK_INIT_POLLING;
+
+ if (count != sizeof(unsigned char)) {
+ usbi_err(NULL, "this function should only used for signaling");
+ return -1;
+ }
+
+ _index = _fd_to_index_and_lock(fd);
+
+ if ( (_index < 0) || (poll_fd[_index].overlapped == NULL) ) {
+ errno = EBADF;
+ if (_index >= 0) {
+ LeaveCriticalSection(&_poll_fd[_index].mutex);
+ }
+ return -1;
+ }
+
+ poll_dbg("set pipe event (fd = %d, thread = %08X)", _index, GetCurrentThreadId());
+ SetEvent(poll_fd[_index].overlapped->hEvent);
+ poll_fd[_index].overlapped->Internal = STATUS_WAIT_0;
+ // If two threads write on the pipe at the same time, we need to
+ // process two separate reads => use the overlapped as a counter
+ poll_fd[_index].overlapped->InternalHigh++;
+
+ LeaveCriticalSection(&_poll_fd[_index].mutex);
+ return sizeof(unsigned char);
+}
+
+/*
+ * synchronous read for fake "pipe" signaling
+ */
+ssize_t usbi_read(int fd, void *buf, size_t count)
+{
+ int _index;
+ ssize_t r = -1;
+ UNUSED(buf);
+
+ CHECK_INIT_POLLING;
+
+ if (count != sizeof(unsigned char)) {
+ usbi_err(NULL, "this function should only used for signaling");
+ return -1;
+ }
+
+ _index = _fd_to_index_and_lock(fd);
+
+ if (_index < 0) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (WaitForSingleObject(poll_fd[_index].overlapped->hEvent, INFINITE) != WAIT_OBJECT_0) {
+ usbi_warn(NULL, "waiting for event failed: %d", (int)GetLastError());
+ errno = EIO;
+ goto out;
+ }
+
+ poll_dbg("clr pipe event (fd = %d, thread = %08X)", _index, GetCurrentThreadId());
+ poll_fd[_index].overlapped->InternalHigh--;
+ // Don't reset unless we don't have any more events to process
+ if (poll_fd[_index].overlapped->InternalHigh <= 0) {
+ ResetEvent(poll_fd[_index].overlapped->hEvent);
+ poll_fd[_index].overlapped->Internal = STATUS_PENDING;
+ }
+
+ r = sizeof(unsigned char);
+
+out:
+ LeaveCriticalSection(&_poll_fd[_index].mutex);
+ return r;
+}
diff --git a/e502/libusb-1.0/libusb-1.0/os/poll_windows.h b/e502/libusb-1.0/libusb-1.0/os/poll_windows.h
new file mode 100644
index 0000000..9163fec
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/poll_windows.h
@@ -0,0 +1,131 @@
+/*
+ * Windows compat: POSIX compatibility wrapper
+ * Copyright © 2012-2013 RealVNC Ltd.
+ * Copyright © 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
+#if defined(_WIN32_WCE)
+// WinCE doesn't have a HasOverlappedIoCompleted() macro, so attempt to emulate it
+#define HasOverlappedIoCompleted(lpOverlapped) (((DWORD)(lpOverlapped)->Internal) != STATUS_PENDING)
+#endif
+#define HasOverlappedIoCompletedSync(lpOverlapped) (((DWORD)(lpOverlapped)->Internal) == STATUS_COMPLETED_SYNCHRONOUSLY)
+
+#define DUMMY_HANDLE ((HANDLE)(LONG_PTR)-2)
+
+/* Windows versions */
+enum windows_version {
+ WINDOWS_CE = -2,
+ WINDOWS_UNDEFINED = -1,
+ WINDOWS_UNSUPPORTED = 0,
+ WINDOWS_XP = 0x51,
+ WINDOWS_2003 = 0x52, // Also XP x64
+ WINDOWS_VISTA = 0x60,
+ WINDOWS_7 = 0x61,
+ WINDOWS_8 = 0x62,
+ WINDOWS_8_1_OR_LATER = 0x63,
+ WINDOWS_MAX
+};
+extern int windows_version;
+
+#define MAX_FDS (256*32)
+
+#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
+typedef int cancel_transfer(struct usbi_transfer *itransfer);
+
+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
+ struct usbi_transfer *itransfer; // Associated transfer, or NULL if completed
+ cancel_transfer *cancel_fn; // Function pointer to cancel transfer API
+ 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,
+ struct usbi_transfer *transfer, cancel_transfer *cancel_fn);
+void usbi_free_fd(struct winfd* winfd);
+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/e502/libusb-1.0/libusb-1.0/os/threads_posix.c b/e502/libusb-1.0/libusb-1.0/os/threads_posix.c
new file mode 100644
index 0000000..95c1027
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/threads_posix.c
@@ -0,0 +1,82 @@
+/*
+ * libusb synchronization using POSIX Threads
+ *
+ * Copyright © 2011 Vitali Lovich
+ * Copyright © 2011 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
+ */
+
+#if defined(__linux__) || defined(__OpenBSD__)
+# if defined(__linux__)
+# define _GNU_SOURCE
+# else
+# define _BSD_SOURCE
+# endif
+# include
+# include
+#elif defined(__APPLE__)
+# include
+#elif defined(__CYGWIN__)
+# include
+#endif
+
+#include "threads_posix.h"
+
+int usbi_mutex_init_recursive(pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
+{
+ int err;
+ pthread_mutexattr_t stack_attr;
+ if (!attr) {
+ attr = &stack_attr;
+ err = pthread_mutexattr_init(&stack_attr);
+ if (err != 0)
+ return err;
+ }
+
+ /* mutexattr_settype requires _GNU_SOURCE or _XOPEN_SOURCE >= 500 on Linux */
+ err = pthread_mutexattr_settype(attr, PTHREAD_MUTEX_RECURSIVE);
+ if (err != 0)
+ goto finish;
+
+ err = pthread_mutex_init(mutex, attr);
+
+finish:
+ if (attr == &stack_attr)
+ pthread_mutexattr_destroy(&stack_attr);
+
+ return err;
+}
+
+int usbi_get_tid(void)
+{
+ int ret = -1;
+#if defined(__ANDROID__)
+ ret = gettid();
+#elif defined(__linux__)
+ ret = syscall(SYS_gettid);
+#elif defined(__OpenBSD__)
+ /* The following only works with OpenBSD > 5.1 as it requires
+ real thread support. For 5.1 and earlier, -1 is returned. */
+ ret = syscall(SYS_getthrid);
+#elif defined(__APPLE__)
+ ret = mach_thread_self();
+ mach_port_deallocate(mach_task_self(), ret);
+#elif defined(__CYGWIN__)
+ ret = GetCurrentThreadId();
+#endif
+/* TODO: NetBSD thread ID support */
+ return ret;
+}
diff --git a/e502/libusb-1.0/libusb-1.0/os/threads_posix.h b/e502/libusb-1.0/libusb-1.0/os/threads_posix.h
new file mode 100644
index 0000000..d7a5d21
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/threads_posix.h
@@ -0,0 +1,50 @@
+/*
+ * libusb synchronization using POSIX Threads
+ *
+ * Copyright © 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);
+
+int usbi_get_tid(void);
+
+#endif /* LIBUSB_THREADS_POSIX_H */
diff --git a/e502/libusb-1.0/libusb-1.0/os/threads_windows.c b/e502/libusb-1.0/libusb-1.0/os/threads_windows.c
new file mode 100644
index 0000000..5537b1f
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/threads_windows.c
@@ -0,0 +1,212 @@
+/*
+ * libusb synchronization on Microsoft Windows
+ *
+ * Copyright © 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
+ */
+
+#include
+#include
+#include
+#include
+
+#include "libusb-1.0/libusbi.h"
+
+extern const uint64_t epoch_time;
+
+int usbi_mutex_init(usbi_mutex_t *mutex,
+ const usbi_mutexattr_t *attr) {
+ UNUSED(attr);
+ if(! mutex) return ((errno=EINVAL));
+ *mutex = CreateMutex(NULL, FALSE, NULL);
+ if(!*mutex) return ((errno=ENOMEM));
+ return 0;
+}
+int usbi_mutex_destroy(usbi_mutex_t *mutex) {
+ // It is not clear if CloseHandle failure is due to failure to unlock.
+ // If so, this should be errno=EBUSY.
+ if(!mutex || !CloseHandle(*mutex)) return ((errno=EINVAL));
+ *mutex = NULL;
+ return 0;
+}
+int usbi_mutex_trylock(usbi_mutex_t *mutex) {
+ DWORD result;
+ if(!mutex) return ((errno=EINVAL));
+ result = WaitForSingleObject(*mutex, 0);
+ if(result == WAIT_OBJECT_0 || result == WAIT_ABANDONED)
+ return 0; // acquired (ToDo: check that abandoned is ok)
+ if(result == WAIT_TIMEOUT)
+ return ((errno=EBUSY));
+ return ((errno=EINVAL)); // don't know how this would happen
+ // so don't know proper errno
+}
+int usbi_mutex_lock(usbi_mutex_t *mutex) {
+ DWORD result;
+ if(!mutex) return ((errno=EINVAL));
+ result = WaitForSingleObject(*mutex, INFINITE);
+ if(result == WAIT_OBJECT_0 || result == WAIT_ABANDONED)
+ return 0; // acquired (ToDo: check that abandoned is ok)
+ return ((errno=EINVAL)); // don't know how this would happen
+ // so don't know proper errno
+}
+int usbi_mutex_unlock(usbi_mutex_t *mutex) {
+ if(!mutex) return ((errno=EINVAL));
+ if(!ReleaseMutex(*mutex)) return ((errno=EPERM ));
+ return 0;
+}
+
+int usbi_mutex_static_lock(usbi_mutex_static_t *mutex) {
+ if(!mutex) return ((errno=EINVAL));
+ while (InterlockedExchange((LONG *)mutex, 1) == 1) {
+ SleepEx(0, TRUE);
+ }
+ return 0;
+}
+int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex) {
+ if(!mutex) return ((errno=EINVAL));
+ *mutex = 0;
+ return 0;
+}
+
+int usbi_cond_init(usbi_cond_t *cond,
+ const usbi_condattr_t *attr) {
+ UNUSED(attr);
+ if(!cond) return ((errno=EINVAL));
+ list_init(&cond->waiters );
+ list_init(&cond->not_waiting);
+ return 0;
+}
+int usbi_cond_destroy(usbi_cond_t *cond) {
+ // This assumes no one is using this anymore. The check MAY NOT BE safe.
+ struct usbi_cond_perthread *pos, *next_pos = NULL;
+ if(!cond) return ((errno=EINVAL));
+ if(!list_empty(&cond->waiters)) return ((errno=EBUSY )); // (!see above!)
+ list_for_each_entry_safe(pos, next_pos, &cond->not_waiting, list, struct usbi_cond_perthread) {
+ CloseHandle(pos->event);
+ list_del(&pos->list);
+ free(pos);
+ }
+
+ return 0;
+}
+
+int usbi_cond_broadcast(usbi_cond_t *cond) {
+ // Assumes mutex is locked; this is not in keeping with POSIX spec, but
+ // libusb does this anyway, so we simplify by not adding more sync
+ // primitives to the CV definition!
+ int fail = 0;
+ struct usbi_cond_perthread *pos;
+ if(!cond) return ((errno=EINVAL));
+ list_for_each_entry(pos, &cond->waiters, list, struct usbi_cond_perthread) {
+ if(!SetEvent(pos->event))
+ fail = 1;
+ }
+ // The wait function will remove its respective item from the list.
+ return fail ? ((errno=EINVAL)) : 0;
+}
+int usbi_cond_signal(usbi_cond_t *cond) {
+ // Assumes mutex is locked; this is not in keeping with POSIX spec, but
+ // libusb does this anyway, so we simplify by not adding more sync
+ // primitives to the CV definition!
+ struct usbi_cond_perthread *pos;
+ if(!cond) return ((errno=EINVAL));
+ if(list_empty(&cond->waiters)) return 0; // no one to wakeup.
+ pos = list_entry(&cond->waiters.next, struct usbi_cond_perthread, list);
+ // The wait function will remove its respective item from the list.
+ return SetEvent(pos->event) ? 0 : ((errno=EINVAL));
+}
+__inline static int usbi_cond_intwait(usbi_cond_t *cond,
+ usbi_mutex_t *mutex,
+ DWORD timeout_ms) {
+ struct usbi_cond_perthread *pos;
+ int found = 0, r;
+ DWORD r2,tid = GetCurrentThreadId();
+ if(!cond || !mutex) return ((errno=EINVAL));
+ list_for_each_entry(pos, &cond->not_waiting, list, struct usbi_cond_perthread) {
+ if(tid == pos->tid) {
+ found = 1;
+ break;
+ }
+ }
+ if(!found) {
+ pos = (struct usbi_cond_perthread*) calloc(1, sizeof(struct usbi_cond_perthread));
+ if(!pos) return ((errno=ENOMEM)); // This errno is not POSIX-allowed.
+ pos->tid = tid;
+ pos->event = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset.
+ if(!pos->event) {
+ free(pos);
+ return ((errno=ENOMEM));
+ }
+ list_add(&pos->list, &cond->not_waiting);
+ }
+
+ list_del(&pos->list); // remove from not_waiting list.
+ list_add(&pos->list, &cond->waiters);
+
+ r = usbi_mutex_unlock(mutex);
+ if(r) return r;
+ r2 = WaitForSingleObject(pos->event, timeout_ms);
+ r = usbi_mutex_lock(mutex);
+ if(r) return r;
+
+ list_del(&pos->list);
+ list_add(&pos->list, &cond->not_waiting);
+
+ if(r2 == WAIT_TIMEOUT) return ((errno=ETIMEDOUT));
+
+ return 0;
+}
+// N.B.: usbi_cond_*wait() can also return ENOMEM, even though pthread_cond_*wait cannot!
+int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex) {
+ return usbi_cond_intwait(cond, mutex, INFINITE);
+}
+int usbi_cond_timedwait(usbi_cond_t *cond,
+ usbi_mutex_t *mutex,
+ const struct timespec *abstime) {
+ FILETIME filetime;
+ ULARGE_INTEGER rtime;
+ struct timeval targ_time, cur_time, delta_time;
+ struct timespec cur_time_ns;
+ DWORD millis;
+
+ // GetSystemTimeAsFileTime() is not available on CE
+ SYSTEMTIME st;
+ GetSystemTime(&st);
+ SystemTimeToFileTime(&st, &filetime);
+ rtime.LowPart = filetime.dwLowDateTime;
+ rtime.HighPart = filetime.dwHighDateTime;
+ rtime.QuadPart -= epoch_time;
+ cur_time_ns.tv_sec = (long)(rtime.QuadPart / 10000000);
+ cur_time_ns.tv_nsec = (long)((rtime.QuadPart % 10000000)*100);
+ TIMESPEC_TO_TIMEVAL(&cur_time, &cur_time_ns);
+
+ TIMESPEC_TO_TIMEVAL(&targ_time, abstime);
+ timersub(&targ_time, &cur_time, &delta_time);
+ if(delta_time.tv_sec < 0) // abstime already passed?
+ millis = 0;
+ else {
+ millis = delta_time.tv_usec/1000;
+ millis += delta_time.tv_sec *1000;
+ if (delta_time.tv_usec % 1000) // round up to next millisecond
+ millis++;
+ }
+
+ return usbi_cond_intwait(cond, mutex, millis);
+}
+
+int usbi_get_tid(void) {
+ return GetCurrentThreadId();
+}
diff --git a/e502/libusb-1.0/libusb-1.0/os/threads_windows.h b/e502/libusb-1.0/libusb-1.0/os/threads_windows.h
new file mode 100644
index 0000000..2b82925
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/threads_windows.h
@@ -0,0 +1,87 @@
+/*
+ * libusb synchronization on Microsoft Windows
+ *
+ * Copyright © 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);
+
+int usbi_get_tid(void);
+
+#endif /* LIBUSB_THREADS_WINDOWS_H */
diff --git a/e502/libusb-1.0/libusb-1.0/os/wince_usb.c b/e502/libusb-1.0/libusb-1.0/os/wince_usb.c
new file mode 100644
index 0000000..4d9b3cc
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/wince_usb.c
@@ -0,0 +1,1032 @@
+/*
+ * Windows CE backend for libusb 1.0
+ * Copyright © 2011-2013 RealVNC Ltd.
+ * Large portions taken from Windows backend, which is
+ * Copyright © 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
+ */
+
+#include
+
+#include
+#include
+#include
+
+#include "wince_usb.h"
+
+// Forward declares
+static int wince_clock_gettime(int clk_id, struct timespec *tp);
+unsigned __stdcall wince_clock_gettime_threaded(void* param);
+
+// Global variables
+uint64_t hires_frequency, hires_ticks_to_ps;
+int errno;
+const uint64_t epoch_time = UINT64_C(116444736000000000); // 1970.01.01 00:00:000 in MS Filetime
+int windows_version = WINDOWS_CE;
+static int concurrent_usage = -1;
+// Timer thread
+// NB: index 0 is for monotonic and 1 is for the thread exit event
+HANDLE timer_thread = NULL;
+HANDLE timer_mutex = NULL;
+struct timespec timer_tp;
+volatile LONG request_count[2] = {0, 1}; // last one must be > 0
+HANDLE timer_request[2] = { NULL, NULL };
+HANDLE timer_response = NULL;
+HANDLE driver_handle = INVALID_HANDLE_VALUE;
+
+/*
+ * Converts a windows error to human readable string
+ * uses retval as errorcode, or, if 0, use GetLastError()
+ */
+#if defined(ENABLE_LOGGING)
+static char* windows_error_str(uint32_t retval)
+{
+ static TCHAR wErr_string[ERR_BUFFER_SIZE];
+ static char err_string[ERR_BUFFER_SIZE];
+
+ DWORD size;
+ size_t i;
+ uint32_t error_code, format_error;
+
+ error_code = retval?retval:GetLastError();
+
+ safe_stprintf(wErr_string, ERR_BUFFER_SIZE, _T("[%d] "), error_code);
+
+ size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &wErr_string[safe_tcslen(wErr_string)],
+ ERR_BUFFER_SIZE - (DWORD)safe_tcslen(wErr_string), NULL);
+ if (size == 0) {
+ format_error = GetLastError();
+ if (format_error)
+ safe_stprintf(wErr_string, ERR_BUFFER_SIZE,
+ _T("Windows error code %u (FormatMessage error code %u)"), error_code, format_error);
+ else
+ safe_stprintf(wErr_string, ERR_BUFFER_SIZE, _T("Unknown error code %u"), error_code);
+ } else {
+ // Remove CR/LF terminators
+ for (i=safe_tcslen(wErr_string)-1; ((wErr_string[i]==0x0A) || (wErr_string[i]==0x0D)); i--) {
+ wErr_string[i] = 0;
+ }
+ }
+ if (WideCharToMultiByte(CP_ACP, 0, wErr_string, -1, err_string, ERR_BUFFER_SIZE, NULL, NULL) < 0)
+ {
+ strcpy(err_string, "Unable to convert error string");
+ }
+ return err_string;
+}
+#endif
+
+static struct wince_device_priv *_device_priv(struct libusb_device *dev)
+{
+ return (struct wince_device_priv *) dev->os_priv;
+}
+
+// ceusbkwrapper to libusb error code mapping
+static int translate_driver_error(int error)
+{
+ switch (error) {
+ case ERROR_INVALID_PARAMETER:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ case ERROR_CALL_NOT_IMPLEMENTED:
+ case ERROR_NOT_SUPPORTED:
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ return LIBUSB_ERROR_NO_MEM;
+ case ERROR_INVALID_HANDLE:
+ return LIBUSB_ERROR_NO_DEVICE;
+ case ERROR_BUSY:
+ return LIBUSB_ERROR_BUSY;
+
+ // Error codes that are either unexpected, or have
+ // no suitable LIBUSB_ERROR equivilant.
+ case ERROR_CANCELLED:
+ case ERROR_INTERNAL_ERROR:
+ default:
+ return LIBUSB_ERROR_OTHER;
+ }
+}
+
+static int init_dllimports()
+{
+ DLL_LOAD(ceusbkwrapper.dll, UkwOpenDriver, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwGetDeviceList, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwReleaseDeviceList, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwGetDeviceAddress, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwGetDeviceDescriptor, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwGetConfigDescriptor, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwCloseDriver, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwCancelTransfer, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwIssueControlTransfer, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwClaimInterface, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwReleaseInterface, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwSetInterfaceAlternateSetting, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwClearHaltHost, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwClearHaltDevice, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwGetConfig, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwSetConfig, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwResetDevice, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwKernelDriverActive, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwAttachKernelDriver, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwDetachKernelDriver, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwIssueBulkTransfer, TRUE);
+ DLL_LOAD(ceusbkwrapper.dll, UkwIsPipeHalted, TRUE);
+ return LIBUSB_SUCCESS;
+}
+
+static int init_device(struct libusb_device *dev, UKW_DEVICE drv_dev,
+ unsigned char bus_addr, unsigned char dev_addr)
+{
+ struct wince_device_priv *priv = _device_priv(dev);
+ int r = LIBUSB_SUCCESS;
+
+ dev->bus_number = bus_addr;
+ dev->device_address = dev_addr;
+ priv->dev = drv_dev;
+
+ if (!UkwGetDeviceDescriptor(priv->dev, &(priv->desc))) {
+ r = translate_driver_error(GetLastError());
+ }
+ return r;
+}
+
+// Internal API functions
+static int wince_init(struct libusb_context *ctx)
+{
+ int i, r = LIBUSB_ERROR_OTHER;
+ HANDLE semaphore;
+ TCHAR sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID)
+
+ _stprintf(sem_name, _T("libusb_init%08X"), (unsigned int)GetCurrentProcessId()&0xFFFFFFFF);
+ semaphore = CreateSemaphore(NULL, 1, 1, sem_name);
+ if (semaphore == NULL) {
+ usbi_err(ctx, "could not create semaphore: %s", windows_error_str(0));
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ // A successful wait brings our semaphore count to 0 (unsignaled)
+ // => any concurent wait stalls until the semaphore's release
+ if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) {
+ usbi_err(ctx, "failure to access semaphore: %s", windows_error_str(0));
+ CloseHandle(semaphore);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ // NB: concurrent usage supposes that init calls are equally balanced with
+ // exit calls. If init is called more than exit, we will not exit properly
+ if ( ++concurrent_usage == 0 ) { // First init?
+ // Initialize pollable file descriptors
+ init_polling();
+
+ // Load DLL imports
+ if (init_dllimports() != LIBUSB_SUCCESS) {
+ usbi_err(ctx, "could not resolve DLL functions");
+ r = LIBUSB_ERROR_NOT_SUPPORTED;
+ goto init_exit;
+ }
+
+ // try to open a handle to the driver
+ driver_handle = UkwOpenDriver();
+ if (driver_handle == INVALID_HANDLE_VALUE) {
+ usbi_err(ctx, "could not connect to driver");
+ r = LIBUSB_ERROR_NOT_SUPPORTED;
+ goto init_exit;
+ }
+
+ // Windows CE doesn't have a way of specifying thread affinity, so this code
+ // just has to hope QueryPerformanceCounter doesn't report different values when
+ // running on different cores.
+ r = LIBUSB_ERROR_NO_MEM;
+ for (i = 0; i < 2; i++) {
+ timer_request[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (timer_request[i] == NULL) {
+ usbi_err(ctx, "could not create timer request event %d - aborting", i);
+ goto init_exit;
+ }
+ }
+ timer_response = CreateSemaphore(NULL, 0, MAX_TIMER_SEMAPHORES, NULL);
+ if (timer_response == NULL) {
+ usbi_err(ctx, "could not create timer response semaphore - aborting");
+ goto init_exit;
+ }
+ timer_mutex = CreateMutex(NULL, FALSE, NULL);
+ if (timer_mutex == NULL) {
+ usbi_err(ctx, "could not create timer mutex - aborting");
+ goto init_exit;
+ }
+ timer_thread = CreateThread(NULL, 0, wince_clock_gettime_threaded, NULL, 0, NULL);
+ if (timer_thread == NULL) {
+ usbi_err(ctx, "Unable to create timer thread - aborting");
+ goto init_exit;
+ }
+
+ // Wait for timer thread to init before continuing.
+ if (WaitForSingleObject(timer_response, INFINITE) != WAIT_OBJECT_0) {
+ usbi_err(ctx, "Failed to wait for timer thread to become ready - aborting");
+ goto init_exit;
+ }
+ }
+ // At this stage, either we went through full init successfully, or didn't need to
+ r = LIBUSB_SUCCESS;
+
+init_exit: // Holds semaphore here.
+ if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
+ if (driver_handle != INVALID_HANDLE_VALUE) {
+ UkwCloseDriver(driver_handle);
+ driver_handle = INVALID_HANDLE_VALUE;
+ }
+ if (timer_thread) {
+ SetEvent(timer_request[1]); // actually the signal to quit the thread.
+ if (WAIT_OBJECT_0 != WaitForSingleObject(timer_thread, INFINITE)) {
+ usbi_warn(ctx, "could not wait for timer thread to quit");
+ TerminateThread(timer_thread, 1); // shouldn't happen, but we're destroying
+ // all objects it might have held anyway.
+ }
+ CloseHandle(timer_thread);
+ timer_thread = NULL;
+ }
+ for (i = 0; i < 2; i++) {
+ if (timer_request[i]) {
+ CloseHandle(timer_request[i]);
+ timer_request[i] = NULL;
+ }
+ }
+ if (timer_response) {
+ CloseHandle(timer_response);
+ timer_response = NULL;
+ }
+ if (timer_mutex) {
+ CloseHandle(timer_mutex);
+ timer_mutex = NULL;
+ }
+ }
+
+ if (r != LIBUSB_SUCCESS)
+ --concurrent_usage; // Not expected to call libusb_exit if we failed.
+
+ ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
+ CloseHandle(semaphore);
+ return r;
+}
+
+static void wince_exit(void)
+{
+ int i;
+ HANDLE semaphore;
+ TCHAR sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID)
+
+ _stprintf(sem_name, _T("libusb_init%08X"), (unsigned int)GetCurrentProcessId()&0xFFFFFFFF);
+ semaphore = CreateSemaphore(NULL, 1, 1, sem_name);
+ if (semaphore == NULL) {
+ return;
+ }
+
+ // A successful wait brings our semaphore count to 0 (unsignaled)
+ // => any concurent wait stalls until the semaphore release
+ if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) {
+ CloseHandle(semaphore);
+ return;
+ }
+
+ // Only works if exits and inits are balanced exactly
+ if (--concurrent_usage < 0) { // Last exit
+ exit_polling();
+
+ if (timer_thread) {
+ SetEvent(timer_request[1]); // actually the signal to quit the thread.
+ if (WAIT_OBJECT_0 != WaitForSingleObject(timer_thread, INFINITE)) {
+ usbi_dbg("could not wait for timer thread to quit");
+ TerminateThread(timer_thread, 1);
+ }
+ CloseHandle(timer_thread);
+ timer_thread = NULL;
+ }
+ for (i = 0; i < 2; i++) {
+ if (timer_request[i]) {
+ CloseHandle(timer_request[i]);
+ timer_request[i] = NULL;
+ }
+ }
+ if (timer_response) {
+ CloseHandle(timer_response);
+ timer_response = NULL;
+ }
+ if (timer_mutex) {
+ CloseHandle(timer_mutex);
+ timer_mutex = NULL;
+ }
+ if (driver_handle != INVALID_HANDLE_VALUE) {
+ UkwCloseDriver(driver_handle);
+ driver_handle = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
+ CloseHandle(semaphore);
+}
+
+static int wince_get_device_list(
+ struct libusb_context *ctx,
+ struct discovered_devs **discdevs)
+{
+ UKW_DEVICE devices[MAX_DEVICE_COUNT];
+ struct discovered_devs * new_devices = *discdevs;
+ DWORD count = 0, i;
+ struct libusb_device *dev = NULL;
+ unsigned char bus_addr, dev_addr;
+ unsigned long session_id;
+ BOOL success;
+ DWORD release_list_offset = 0;
+ int r = LIBUSB_SUCCESS;
+
+ success = UkwGetDeviceList(driver_handle, devices, MAX_DEVICE_COUNT, &count);
+ if (!success) {
+ int libusbErr = translate_driver_error(GetLastError());
+ usbi_err(ctx, "could not get devices: %s", windows_error_str(0));
+ return libusbErr;
+ }
+ for(i = 0; i < count; ++i) {
+ release_list_offset = i;
+ success = UkwGetDeviceAddress(devices[i], &bus_addr, &dev_addr, &session_id);
+ if (!success) {
+ r = translate_driver_error(GetLastError());
+ usbi_err(ctx, "could not get device address for %d: %s", i, windows_error_str(0));
+ goto err_out;
+ }
+ dev = usbi_get_device_by_session_id(ctx, session_id);
+ if (dev) {
+ usbi_dbg("using existing device for %d/%d (session %ld)",
+ bus_addr, dev_addr, session_id);
+ // Release just this element in the device list (as we already hold a
+ // reference to it).
+ UkwReleaseDeviceList(driver_handle, &devices[i], 1);
+ release_list_offset++;
+ } else {
+ usbi_dbg("allocating new device for %d/%d (session %ld)",
+ bus_addr, dev_addr, session_id);
+ dev = usbi_alloc_device(ctx, session_id);
+ if (!dev) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err_out;
+ }
+ r = init_device(dev, devices[i], bus_addr, dev_addr);
+ if (r < 0)
+ goto err_out;
+ r = usbi_sanitize_device(dev);
+ if (r < 0)
+ goto err_out;
+ }
+ new_devices = discovered_devs_append(new_devices, dev);
+ if (!discdevs) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err_out;
+ }
+ safe_unref_device(dev);
+ }
+ *discdevs = new_devices;
+ return r;
+err_out:
+ *discdevs = new_devices;
+ safe_unref_device(dev);
+ // Release the remainder of the unprocessed device list.
+ // The devices added to new_devices already will still be passed up to libusb,
+ // which can dispose of them at its leisure.
+ UkwReleaseDeviceList(driver_handle, &devices[release_list_offset], count - release_list_offset);
+ return r;
+}
+
+static int wince_open(struct libusb_device_handle *handle)
+{
+ // Nothing to do to open devices as a handle to it has
+ // been retrieved by wince_get_device_list
+ return LIBUSB_SUCCESS;
+}
+
+static void wince_close(struct libusb_device_handle *handle)
+{
+ // Nothing to do as wince_open does nothing.
+}
+
+static int wince_get_device_descriptor(
+ struct libusb_device *device,
+ unsigned char *buffer, int *host_endian)
+{
+ struct wince_device_priv *priv = _device_priv(device);
+
+ *host_endian = 1;
+ memcpy(buffer, &priv->desc, DEVICE_DESC_LENGTH);
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_get_active_config_descriptor(
+ struct libusb_device *device,
+ unsigned char *buffer, size_t len, int *host_endian)
+{
+ struct wince_device_priv *priv = _device_priv(device);
+ DWORD actualSize = len;
+ *host_endian = 0;
+ if (!UkwGetConfigDescriptor(priv->dev, UKW_ACTIVE_CONFIGURATION, buffer, len, &actualSize)) {
+ return translate_driver_error(GetLastError());
+ }
+ return actualSize;
+}
+
+static int wince_get_config_descriptor(
+ struct libusb_device *device,
+ uint8_t config_index,
+ unsigned char *buffer, size_t len, int *host_endian)
+{
+ struct wince_device_priv *priv = _device_priv(device);
+ DWORD actualSize = len;
+ *host_endian = 0;
+ if (!UkwGetConfigDescriptor(priv->dev, config_index, buffer, len, &actualSize)) {
+ return translate_driver_error(GetLastError());
+ }
+ return actualSize;
+}
+
+static int wince_get_configuration(
+ struct libusb_device_handle *handle,
+ int *config)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ UCHAR cv = 0;
+ if (!UkwGetConfig(priv->dev, &cv)) {
+ return translate_driver_error(GetLastError());
+ }
+ (*config) = cv;
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_set_configuration(
+ struct libusb_device_handle *handle,
+ int config)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ // Setting configuration 0 places the device in Address state.
+ // This should correspond to the "unconfigured state" required by
+ // libusb when the specified configuration is -1.
+ UCHAR cv = (config < 0) ? 0 : config;
+ if (!UkwSetConfig(priv->dev, cv)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_claim_interface(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ if (!UkwClaimInterface(priv->dev, interface_number)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_release_interface(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ if (!UkwSetInterfaceAlternateSetting(priv->dev, interface_number, 0)) {
+ return translate_driver_error(GetLastError());
+ }
+ if (!UkwReleaseInterface(priv->dev, interface_number)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_set_interface_altsetting(
+ struct libusb_device_handle *handle,
+ int interface_number, int altsetting)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ if (!UkwSetInterfaceAlternateSetting(priv->dev, interface_number, altsetting)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_clear_halt(
+ struct libusb_device_handle *handle,
+ unsigned char endpoint)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ if (!UkwClearHaltHost(priv->dev, endpoint)) {
+ return translate_driver_error(GetLastError());
+ }
+ if (!UkwClearHaltDevice(priv->dev, endpoint)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_reset_device(
+ struct libusb_device_handle *handle)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ if (!UkwResetDevice(priv->dev)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_kernel_driver_active(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ BOOL result = FALSE;
+ if (!UkwKernelDriverActive(priv->dev, interface_number, &result)) {
+ return translate_driver_error(GetLastError());
+ }
+ return result ? 1 : 0;
+}
+
+static int wince_detach_kernel_driver(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ if (!UkwDetachKernelDriver(priv->dev, interface_number)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_attach_kernel_driver(
+ struct libusb_device_handle *handle,
+ int interface_number)
+{
+ struct wince_device_priv *priv = _device_priv(handle->dev);
+ if (!UkwAttachKernelDriver(priv->dev, interface_number)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static void wince_destroy_device(
+ struct libusb_device *dev)
+{
+ struct wince_device_priv *priv = _device_priv(dev);
+ UkwReleaseDeviceList(driver_handle, &priv->dev, 1);
+}
+
+static void wince_clear_transfer_priv(
+ struct usbi_transfer *itransfer)
+{
+ struct wince_transfer_priv *transfer_priv = (struct wince_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
+ struct winfd wfd = fd_to_winfd(transfer_priv->pollable_fd.fd);
+ // No need to cancel transfer as it is either complete or abandoned
+ wfd.itransfer = NULL;
+ CloseHandle(wfd.handle);
+ usbi_free_fd(&transfer_priv->pollable_fd);
+}
+
+static int wince_cancel_transfer(
+ struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ struct wince_transfer_priv *transfer_priv = (struct wince_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
+
+ if (!UkwCancelTransfer(priv->dev, transfer_priv->pollable_fd.overlapped, UKW_TF_NO_WAIT)) {
+ return translate_driver_error(GetLastError());
+ }
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_submit_control_or_bulk_transfer(struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+ struct wince_transfer_priv *transfer_priv = (struct wince_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
+ struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ BOOL direction_in, ret;
+ struct winfd wfd;
+ DWORD flags;
+ HANDLE eventHandle;
+ PUKW_CONTROL_HEADER setup = NULL;
+ const BOOL control_transfer = transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL;
+
+ transfer_priv->pollable_fd = INVALID_WINFD;
+ if (control_transfer) {
+ setup = (PUKW_CONTROL_HEADER) transfer->buffer;
+ direction_in = setup->bmRequestType & LIBUSB_ENDPOINT_IN;
+ } else {
+ direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN;
+ }
+ flags = direction_in ? UKW_TF_IN_TRANSFER : UKW_TF_OUT_TRANSFER;
+ flags |= UKW_TF_SHORT_TRANSFER_OK;
+
+ eventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (eventHandle == NULL) {
+ usbi_err(ctx, "Failed to create event for async transfer");
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ wfd = usbi_create_fd(eventHandle, direction_in ? RW_READ : RW_WRITE, itransfer, &wince_cancel_transfer);
+ if (wfd.fd < 0) {
+ CloseHandle(eventHandle);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ transfer_priv->pollable_fd = wfd;
+ if (control_transfer) {
+ // Split out control setup header and data buffer
+ DWORD bufLen = transfer->length - sizeof(UKW_CONTROL_HEADER);
+ PVOID buf = (PVOID) &transfer->buffer[sizeof(UKW_CONTROL_HEADER)];
+
+ ret = UkwIssueControlTransfer(priv->dev, flags, setup, buf, bufLen, &transfer->actual_length, wfd.overlapped);
+ } else {
+ ret = UkwIssueBulkTransfer(priv->dev, flags, transfer->endpoint, transfer->buffer,
+ transfer->length, &transfer->actual_length, wfd.overlapped);
+ }
+ if (!ret) {
+ int libusbErr = translate_driver_error(GetLastError());
+ usbi_err(ctx, "UkwIssue%sTransfer failed: error %d",
+ control_transfer ? "Control" : "Bulk", GetLastError());
+ wince_clear_transfer_priv(itransfer);
+ return libusbErr;
+ }
+ usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, direction_in ? POLLIN : POLLOUT);
+ itransfer->flags |= USBI_TRANSFER_UPDATED_FDS;
+
+ return LIBUSB_SUCCESS;
+}
+
+static int wince_submit_iso_transfer(struct usbi_transfer *itransfer)
+{
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+static int wince_submit_transfer(
+ struct usbi_transfer *itransfer)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ return wince_submit_control_or_bulk_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return wince_submit_iso_transfer(itransfer);
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ default:
+ usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+static void wince_transfer_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct wince_transfer_priv *transfer_priv = (struct wince_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
+ struct wince_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+ int status;
+
+ usbi_dbg("handling I/O completion with errcode %d", io_result);
+
+ if (io_result == ERROR_NOT_SUPPORTED &&
+ transfer->type != LIBUSB_TRANSFER_TYPE_CONTROL) {
+ /* For functional stalls, the WinCE USB layer (and therefore the USB Kernel Wrapper
+ * Driver) will report USB_ERROR_STALL/ERROR_NOT_SUPPORTED in situations where the
+ * endpoint isn't actually stalled.
+ *
+ * One example of this is that some devices will occasionally fail to reply to an IN
+ * token. The WinCE USB layer carries on with the transaction until it is completed
+ * (or cancelled) but then completes it with USB_ERROR_STALL.
+ *
+ * This code therefore needs to confirm that there really is a stall error, by both
+ * checking the pipe status and requesting the endpoint status from the device.
+ */
+ BOOL halted = FALSE;
+ usbi_dbg("checking I/O completion with errcode ERROR_NOT_SUPPORTED is really a stall");
+ if (UkwIsPipeHalted(priv->dev, transfer->endpoint, &halted)) {
+ /* Pipe status retrieved, so now request endpoint status by sending a GET_STATUS
+ * control request to the device. This is done synchronously, which is a bit
+ * naughty, but this is a special corner case.
+ */
+ WORD wStatus = 0;
+ DWORD written = 0;
+ UKW_CONTROL_HEADER ctrlHeader;
+ ctrlHeader.bmRequestType = LIBUSB_REQUEST_TYPE_STANDARD |
+ LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_ENDPOINT;
+ ctrlHeader.bRequest = LIBUSB_REQUEST_GET_STATUS;
+ ctrlHeader.wValue = 0;
+ ctrlHeader.wIndex = transfer->endpoint;
+ ctrlHeader.wLength = sizeof(wStatus);
+ if (UkwIssueControlTransfer(priv->dev,
+ UKW_TF_IN_TRANSFER | UKW_TF_SEND_TO_ENDPOINT,
+ &ctrlHeader, &wStatus, sizeof(wStatus), &written, NULL)) {
+ if (written == sizeof(wStatus) &&
+ (wStatus & STATUS_HALT_FLAG) == 0) {
+ if (!halted || UkwClearHaltHost(priv->dev, transfer->endpoint)) {
+ usbi_dbg("Endpoint doesn't appear to be stalled, overriding error with success");
+ io_result = ERROR_SUCCESS;
+ } else {
+ usbi_dbg("Endpoint doesn't appear to be stalled, but the host is halted, changing error");
+ io_result = ERROR_IO_DEVICE;
+ }
+ }
+ }
+ }
+ }
+
+ switch(io_result) {
+ case ERROR_SUCCESS:
+ itransfer->transferred += io_size;
+ status = LIBUSB_TRANSFER_COMPLETED;
+ break;
+ case ERROR_CANCELLED:
+ usbi_dbg("detected transfer cancel");
+ status = LIBUSB_TRANSFER_CANCELLED;
+ break;
+ case ERROR_NOT_SUPPORTED:
+ case ERROR_GEN_FAILURE:
+ usbi_dbg("detected endpoint stall");
+ status = LIBUSB_TRANSFER_STALL;
+ break;
+ case ERROR_SEM_TIMEOUT:
+ usbi_dbg("detected semaphore timeout");
+ status = LIBUSB_TRANSFER_TIMED_OUT;
+ break;
+ case ERROR_OPERATION_ABORTED:
+ if (itransfer->flags & USBI_TRANSFER_TIMED_OUT) {
+ usbi_dbg("detected timeout");
+ status = LIBUSB_TRANSFER_TIMED_OUT;
+ } else {
+ usbi_dbg("detected operation aborted");
+ status = LIBUSB_TRANSFER_CANCELLED;
+ }
+ break;
+ default:
+ usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error: %s", windows_error_str(io_result));
+ status = LIBUSB_TRANSFER_ERROR;
+ break;
+ }
+ wince_clear_transfer_priv(itransfer);
+ if (status == LIBUSB_TRANSFER_CANCELLED) {
+ usbi_handle_transfer_cancellation(itransfer);
+ } else {
+ usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status);
+ }
+}
+
+static void wince_handle_callback (struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
+{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ wince_transfer_callback (itransfer, io_result, io_size);
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ default:
+ usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type);
+ }
+}
+
+static int wince_handle_events(
+ struct libusb_context *ctx,
+ struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready)
+{
+ struct wince_transfer_priv* transfer_priv = NULL;
+ POLL_NFDS_TYPE i = 0;
+ BOOL found = FALSE;
+ struct usbi_transfer *transfer;
+ DWORD io_size, io_result;
+
+ usbi_mutex_lock(&ctx->open_devs_lock);
+ for (i = 0; i < nfds && num_ready > 0; i++) {
+
+ usbi_dbg("checking fd %d with revents = %04x", fds[i].fd, fds[i].revents);
+
+ if (!fds[i].revents) {
+ continue;
+ }
+
+ num_ready--;
+
+ // Because a Windows OVERLAPPED is used for poll emulation,
+ // a pollable fd is created and stored with each transfer
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
+ list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
+ transfer_priv = usbi_transfer_get_os_priv(transfer);
+ if (transfer_priv->pollable_fd.fd == fds[i].fd) {
+ found = TRUE;
+ break;
+ }
+ }
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
+ if (found && HasOverlappedIoCompleted(transfer_priv->pollable_fd.overlapped)) {
+ io_result = (DWORD)transfer_priv->pollable_fd.overlapped->Internal;
+ io_size = (DWORD)transfer_priv->pollable_fd.overlapped->InternalHigh;
+ usbi_remove_pollfd(ctx, transfer_priv->pollable_fd.fd);
+ // let handle_callback free the event using the transfer wfd
+ // If you don't use the transfer wfd, you run a risk of trying to free a
+ // newly allocated wfd that took the place of the one from the transfer.
+ wince_handle_callback(transfer, io_result, io_size);
+ } else if (found) {
+ usbi_err(ctx, "matching transfer for fd %x has not completed", fds[i]);
+ return LIBUSB_ERROR_OTHER;
+ } else {
+ usbi_err(ctx, "could not find a matching transfer for fd %x", fds[i]);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+ }
+
+ usbi_mutex_unlock(&ctx->open_devs_lock);
+ return LIBUSB_SUCCESS;
+}
+
+/*
+ * Monotonic and real time functions
+ */
+unsigned __stdcall wince_clock_gettime_threaded(void* param)
+{
+ LARGE_INTEGER hires_counter, li_frequency;
+ LONG nb_responses;
+ int timer_index;
+
+ // Init - find out if we have access to a monotonic (hires) timer
+ if (!QueryPerformanceFrequency(&li_frequency)) {
+ usbi_dbg("no hires timer available on this platform");
+ hires_frequency = 0;
+ hires_ticks_to_ps = UINT64_C(0);
+ } else {
+ hires_frequency = li_frequency.QuadPart;
+ // The hires frequency can go as high as 4 GHz, so we'll use a conversion
+ // to picoseconds to compute the tv_nsecs part in clock_gettime
+ hires_ticks_to_ps = UINT64_C(1000000000000) / hires_frequency;
+ usbi_dbg("hires timer available (Frequency: %"PRIu64" Hz)", hires_frequency);
+ }
+
+ // Signal wince_init() that we're ready to service requests
+ if (ReleaseSemaphore(timer_response, 1, NULL) == 0) {
+ usbi_dbg("unable to release timer semaphore: %s", windows_error_str(0));
+ }
+
+ // Main loop - wait for requests
+ while (1) {
+ timer_index = WaitForMultipleObjects(2, timer_request, FALSE, INFINITE) - WAIT_OBJECT_0;
+ if ( (timer_index != 0) && (timer_index != 1) ) {
+ usbi_dbg("failure to wait on requests: %s", windows_error_str(0));
+ continue;
+ }
+ if (request_count[timer_index] == 0) {
+ // Request already handled
+ ResetEvent(timer_request[timer_index]);
+ // There's still a possiblity that a thread sends a request between the
+ // time we test request_count[] == 0 and we reset the event, in which case
+ // the request would be ignored. The simple solution to that is to test
+ // request_count again and process requests if non zero.
+ if (request_count[timer_index] == 0)
+ continue;
+ }
+ switch (timer_index) {
+ case 0:
+ WaitForSingleObject(timer_mutex, INFINITE);
+ // Requests to this thread are for hires always
+ if (QueryPerformanceCounter(&hires_counter) != 0) {
+ timer_tp.tv_sec = (long)(hires_counter.QuadPart / hires_frequency);
+ timer_tp.tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency)/1000) * hires_ticks_to_ps);
+ } else {
+ // Fallback to real-time if we can't get monotonic value
+ // Note that real-time clock does not wait on the mutex or this thread.
+ wince_clock_gettime(USBI_CLOCK_REALTIME, &timer_tp);
+ }
+ ReleaseMutex(timer_mutex);
+
+ nb_responses = InterlockedExchange((LONG*)&request_count[0], 0);
+ if ( (nb_responses)
+ && (ReleaseSemaphore(timer_response, nb_responses, NULL) == 0) ) {
+ usbi_dbg("unable to release timer semaphore: %s", windows_error_str(0));
+ }
+ continue;
+ case 1: // time to quit
+ usbi_dbg("timer thread quitting");
+ return 0;
+ }
+ }
+ usbi_dbg("ERROR: broken timer thread");
+ return 1;
+}
+
+static int wince_clock_gettime(int clk_id, struct timespec *tp)
+{
+ FILETIME filetime;
+ ULARGE_INTEGER rtime;
+ DWORD r;
+ SYSTEMTIME st;
+ switch(clk_id) {
+ case USBI_CLOCK_MONOTONIC:
+ if (hires_frequency != 0) {
+ while (1) {
+ InterlockedIncrement((LONG*)&request_count[0]);
+ SetEvent(timer_request[0]);
+ r = WaitForSingleObject(timer_response, TIMER_REQUEST_RETRY_MS);
+ switch(r) {
+ case WAIT_OBJECT_0:
+ WaitForSingleObject(timer_mutex, INFINITE);
+ *tp = timer_tp;
+ ReleaseMutex(timer_mutex);
+ return LIBUSB_SUCCESS;
+ case WAIT_TIMEOUT:
+ usbi_dbg("could not obtain a timer value within reasonable timeframe - too much load?");
+ break; // Retry until successful
+ default:
+ usbi_dbg("WaitForSingleObject failed: %s", windows_error_str(0));
+ return LIBUSB_ERROR_OTHER;
+ }
+ }
+ }
+ // Fall through and return real-time if monotonic was not detected @ timer init
+ case USBI_CLOCK_REALTIME:
+ // We follow http://msdn.microsoft.com/en-us/library/ms724928%28VS.85%29.aspx
+ // with a predef epoch_time to have an epoch that starts at 1970.01.01 00:00
+ // Note however that our resolution is bounded by the Windows system time
+ // functions and is at best of the order of 1 ms (or, usually, worse)
+ GetSystemTime(&st);
+ SystemTimeToFileTime(&st, &filetime);
+ rtime.LowPart = filetime.dwLowDateTime;
+ rtime.HighPart = filetime.dwHighDateTime;
+ rtime.QuadPart -= epoch_time;
+ tp->tv_sec = (long)(rtime.QuadPart / 10000000);
+ tp->tv_nsec = (long)((rtime.QuadPart % 10000000)*100);
+ return LIBUSB_SUCCESS;
+ default:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+}
+
+const struct usbi_os_backend wince_backend = {
+ "Windows CE",
+ 0,
+ wince_init,
+ wince_exit,
+
+ wince_get_device_list,
+ NULL, /* hotplug_poll */
+ wince_open,
+ wince_close,
+
+ wince_get_device_descriptor,
+ wince_get_active_config_descriptor,
+ wince_get_config_descriptor,
+ NULL, /* get_config_descriptor_by_value() */
+
+ wince_get_configuration,
+ wince_set_configuration,
+ wince_claim_interface,
+ wince_release_interface,
+
+ wince_set_interface_altsetting,
+ wince_clear_halt,
+ wince_reset_device,
+
+ NULL, /* alloc_streams */
+ NULL, /* free_streams */
+
+ wince_kernel_driver_active,
+ wince_detach_kernel_driver,
+ wince_attach_kernel_driver,
+
+ wince_destroy_device,
+
+ wince_submit_transfer,
+ wince_cancel_transfer,
+ wince_clear_transfer_priv,
+
+ wince_handle_events,
+
+ wince_clock_gettime,
+ sizeof(struct wince_device_priv),
+ sizeof(struct wince_device_handle_priv),
+ sizeof(struct wince_transfer_priv),
+ 0,
+};
diff --git a/e502/libusb-1.0/libusb-1.0/os/wince_usb.h b/e502/libusb-1.0/libusb-1.0/os/wince_usb.h
new file mode 100644
index 0000000..bc61b5c
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/wince_usb.h
@@ -0,0 +1,131 @@
+/*
+ * Windows CE backend for libusb 1.0
+ * Copyright © 2011-2013 RealVNC Ltd.
+ * Portions taken from Windows backend, which is
+ * Copyright © 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
+
+#include "windows_common.h"
+
+#include
+#include "poll_windows.h"
+
+#define MAX_DEVICE_COUNT 256
+
+// This is a modified dump of the types in the ceusbkwrapper.h library header
+// with functions transformed into extern pointers.
+//
+// This backend dynamically loads ceusbkwrapper.dll and doesn't include
+// ceusbkwrapper.h directly to simplify the build process. The kernel
+// side wrapper driver is built using the platform image build tools,
+// which makes it difficult to reference directly from the libusb build
+// system.
+struct UKW_DEVICE_PRIV;
+typedef struct UKW_DEVICE_PRIV *UKW_DEVICE;
+typedef UKW_DEVICE *PUKW_DEVICE, *LPUKW_DEVICE;
+
+typedef struct {
+ UINT8 bLength;
+ UINT8 bDescriptorType;
+ UINT16 bcdUSB;
+ UINT8 bDeviceClass;
+ UINT8 bDeviceSubClass;
+ UINT8 bDeviceProtocol;
+ UINT8 bMaxPacketSize0;
+ UINT16 idVendor;
+ UINT16 idProduct;
+ UINT16 bcdDevice;
+ UINT8 iManufacturer;
+ UINT8 iProduct;
+ UINT8 iSerialNumber;
+ UINT8 bNumConfigurations;
+} UKW_DEVICE_DESCRIPTOR, *PUKW_DEVICE_DESCRIPTOR, *LPUKW_DEVICE_DESCRIPTOR;
+
+typedef struct {
+ UINT8 bmRequestType;
+ UINT8 bRequest;
+ UINT16 wValue;
+ UINT16 wIndex;
+ UINT16 wLength;
+} UKW_CONTROL_HEADER, *PUKW_CONTROL_HEADER, *LPUKW_CONTROL_HEADER;
+
+// Collection of flags which can be used when issuing transfer requests
+/* Indicates that the transfer direction is 'in' */
+#define UKW_TF_IN_TRANSFER 0x00000001
+/* Indicates that the transfer direction is 'out' */
+#define UKW_TF_OUT_TRANSFER 0x00000000
+/* Specifies that the transfer should complete as soon as possible,
+ * even if no OVERLAPPED structure has been provided. */
+#define UKW_TF_NO_WAIT 0x00000100
+/* Indicates that transfers shorter than the buffer are ok */
+#define UKW_TF_SHORT_TRANSFER_OK 0x00000200
+#define UKW_TF_SEND_TO_DEVICE 0x00010000
+#define UKW_TF_SEND_TO_INTERFACE 0x00020000
+#define UKW_TF_SEND_TO_ENDPOINT 0x00040000
+/* Don't block when waiting for memory allocations */
+#define UKW_TF_DONT_BLOCK_FOR_MEM 0x00080000
+
+/* Value to use when dealing with configuration values, such as UkwGetConfigDescriptor,
+ * to specify the currently active configuration for the device. */
+#define UKW_ACTIVE_CONFIGURATION -1
+
+DLL_DECLARE(WINAPI, HANDLE, UkwOpenDriver, ());
+DLL_DECLARE(WINAPI, BOOL, UkwGetDeviceList, (HANDLE, LPUKW_DEVICE, DWORD, LPDWORD));
+DLL_DECLARE(WINAPI, void, UkwReleaseDeviceList, (HANDLE, LPUKW_DEVICE, DWORD));
+DLL_DECLARE(WINAPI, BOOL, UkwGetDeviceAddress, (UKW_DEVICE, unsigned char*, unsigned char*, unsigned long*));
+DLL_DECLARE(WINAPI, BOOL, UkwGetDeviceDescriptor, (UKW_DEVICE, LPUKW_DEVICE_DESCRIPTOR));
+DLL_DECLARE(WINAPI, BOOL, UkwGetConfigDescriptor, (UKW_DEVICE, DWORD, LPVOID, DWORD, LPDWORD));
+DLL_DECLARE(WINAPI, void, UkwCloseDriver, (HANDLE));
+DLL_DECLARE(WINAPI, BOOL, UkwCancelTransfer, (UKW_DEVICE, LPOVERLAPPED, DWORD));
+DLL_DECLARE(WINAPI, BOOL, UkwIssueControlTransfer, (UKW_DEVICE, DWORD, LPUKW_CONTROL_HEADER, LPVOID, DWORD, LPDWORD, LPOVERLAPPED));
+DLL_DECLARE(WINAPI, BOOL, UkwClaimInterface, (UKW_DEVICE, DWORD));
+DLL_DECLARE(WINAPI, BOOL, UkwReleaseInterface, (UKW_DEVICE, DWORD));
+DLL_DECLARE(WINAPI, BOOL, UkwSetInterfaceAlternateSetting, (UKW_DEVICE, DWORD, DWORD));
+DLL_DECLARE(WINAPI, BOOL, UkwClearHaltHost, (UKW_DEVICE, UCHAR));
+DLL_DECLARE(WINAPI, BOOL, UkwClearHaltDevice, (UKW_DEVICE, UCHAR));
+DLL_DECLARE(WINAPI, BOOL, UkwGetConfig, (UKW_DEVICE, PUCHAR));
+DLL_DECLARE(WINAPI, BOOL, UkwSetConfig, (UKW_DEVICE, UCHAR));
+DLL_DECLARE(WINAPI, BOOL, UkwResetDevice, (UKW_DEVICE));
+DLL_DECLARE(WINAPI, BOOL, UkwKernelDriverActive, (UKW_DEVICE, DWORD, PBOOL));
+DLL_DECLARE(WINAPI, BOOL, UkwAttachKernelDriver, (UKW_DEVICE, DWORD));
+DLL_DECLARE(WINAPI, BOOL, UkwDetachKernelDriver, (UKW_DEVICE, DWORD));
+DLL_DECLARE(WINAPI, BOOL, UkwIssueBulkTransfer, (UKW_DEVICE, DWORD, UCHAR, LPVOID, DWORD, LPDWORD, LPOVERLAPPED));
+DLL_DECLARE(WINAPI, BOOL, UkwIsPipeHalted, (UKW_DEVICE, UCHAR, LPBOOL));
+
+// Used to determine if an endpoint status really is halted on a failed transfer.
+#define STATUS_HALT_FLAG 0x1
+
+struct wince_device_priv {
+ UKW_DEVICE dev;
+ UKW_DEVICE_DESCRIPTOR desc;
+};
+
+struct wince_device_handle_priv {
+ // This member isn't used, but only exists to avoid an empty structure
+ // for private data for the device handle.
+ int reserved;
+};
+
+struct wince_transfer_priv {
+ struct winfd pollable_fd;
+ uint8_t interface_number;
+};
+
diff --git a/e502/libusb-1.0/libusb-1.0/os/windows_common.h b/e502/libusb-1.0/libusb-1.0/os/windows_common.h
new file mode 100644
index 0000000..fe95d79
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/windows_common.h
@@ -0,0 +1,108 @@
+/*
+ * Windows backend common header for libusb 1.0
+ *
+ * This file brings together header code common between
+ * the desktop Windows and Windows CE backends.
+ * Copyright © 2012-2013 RealVNC Ltd.
+ * Copyright © 2009-2012 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
+
+// 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
+
+#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_stricmp(str1, str2) _stricmp(((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(dst, count, ...) do {_snprintf(dst, count, __VA_ARGS__); (dst)[(count)-1] = 0; } while(0)
+#define safe_stprintf _sntprintf
+#define safe_tcslen(str) ((str==NULL)?0:_tcslen(str))
+#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)
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+#define ERR_BUFFER_SIZE 256
+#define TIMER_REQUEST_RETRY_MS 100
+#define MAX_TIMER_SEMAPHORES 128
+
+
+/*
+ * 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
+
+#ifndef _WIN32_WCE
+#define DLL_STRINGIFY(dll) #dll
+#define DLL_GET_MODULE_HANDLE(dll) GetModuleHandleA(DLL_STRINGIFY(dll))
+#define DLL_LOAD_LIBRARY(dll) LoadLibraryA(DLL_STRINGIFY(dll))
+#else
+#define DLL_STRINGIFY(dll) L#dll
+#define DLL_GET_MODULE_HANDLE(dll) GetModuleHandle(DLL_STRINGIFY(dll))
+#define DLL_LOAD_LIBRARY(dll) LoadLibrary(DLL_STRINGIFY(dll))
+#endif
+
+#define DLL_LOAD_PREFIXNAME(dll, prefixname, name, ret_on_failure) \
+ do { \
+ HMODULE h = DLL_GET_MODULE_HANDLE(dll); \
+ if (!h) \
+ h = DLL_LOAD_LIBRARY(dll); \
+ if (!h) { \
+ if (ret_on_failure) { return LIBUSB_ERROR_NOT_FOUND; } \
+ else { break; } \
+ } \
+ prefixname = (__dll_##name##_t)GetProcAddress(h, \
+ DLL_STRINGIFY(name)); \
+ if (prefixname) break; \
+ prefixname = (__dll_##name##_t)GetProcAddress(h, \
+ DLL_STRINGIFY(name) DLL_STRINGIFY(A)); \
+ if (prefixname) break; \
+ prefixname = (__dll_##name##_t)GetProcAddress(h, \
+ DLL_STRINGIFY(name) DLL_STRINGIFY(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)
diff --git a/e502/libusb-1.0/libusb-1.0/os/windows_usb.c b/e502/libusb-1.0/libusb-1.0/os/windows_usb.c
new file mode 100644
index 0000000..9880f96
--- /dev/null
+++ b/e502/libusb-1.0/libusb-1.0/os/windows_usb.c
@@ -0,0 +1,4578 @@
+/*
+ * windows backend for libusb 1.0
+ * Copyright © 2009-2012 Pete Batard
+ * With contributions from Michael Plante, Orin Eman et al.
+ * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer
+ * HID Reports IOCTLs inspired from HIDAPI by Alan Ott, Signal 11 Software
+ * Hash table functions adapted from glibc, by Ulrich Drepper et al.
+ * 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
+ */
+
+#include "config.h"
+#include