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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libusb-1.0/libusbi.h" +#include "poll_windows.h" +#include "windows_usb.h" + +// The 2 macros below are used in conjunction with safe loops. +#define LOOP_CHECK(fcall) { r=fcall; if (r != LIBUSB_SUCCESS) continue; } +#define LOOP_BREAK(err) { r=err; continue; } + +// Helper prototypes +static int windows_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian); +static int windows_clock_gettime(int clk_id, struct timespec *tp); +unsigned __stdcall windows_clock_gettime_threaded(void* param); +// Common calls +static int common_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface); + +// WinUSB-like API prototypes +static int winusbx_init(int sub_api, struct libusb_context *ctx); +static int winusbx_exit(int sub_api); +static int winusbx_open(int sub_api, struct libusb_device_handle *dev_handle); +static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle); +static int winusbx_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting); +static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int winusbx_abort_transfers(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_abort_control(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_handle); +static int winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size); +// HID API prototypes +static int hid_init(int sub_api, struct libusb_context *ctx); +static int hid_exit(int sub_api); +static int hid_open(int sub_api, struct libusb_device_handle *dev_handle); +static void hid_close(int sub_api, struct libusb_device_handle *dev_handle); +static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int hid_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting); +static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer); +static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer); +static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int hid_abort_transfers(int sub_api, struct usbi_transfer *itransfer); +static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle); +static int hid_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size); +// Composite API prototypes +static int composite_init(int sub_api, struct libusb_context *ctx); +static int composite_exit(int sub_api); +static int composite_open(int sub_api, struct libusb_device_handle *dev_handle); +static void composite_close(int sub_api, struct libusb_device_handle *dev_handle); +static int composite_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int composite_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting); +static int composite_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int composite_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer); +static int composite_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer); +static int composite_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer); +static int composite_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfer); +static int composite_abort_control(int sub_api, struct usbi_transfer *itransfer); +static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle); +static int composite_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size); + + +// Global variables +uint64_t hires_frequency, hires_ticks_to_ps; +const uint64_t epoch_time = UINT64_C(116444736000000000); // 1970.01.01 00:00:000 in MS Filetime +int windows_version = WINDOWS_UNDEFINED; +static char windows_version_str[128] = "Windows Undefined"; +// Concurrency +static int concurrent_usage = -1; +usbi_mutex_t autoclaim_lock; +// 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; +// API globals +#define CHECK_WINUSBX_AVAILABLE(sub_api) do { if (sub_api == SUB_API_NOTSET) sub_api = priv->sub_api; \ + if (!WinUSBX[sub_api].initialized) return LIBUSB_ERROR_ACCESS; } while(0) +static struct winusb_interface WinUSBX[SUB_API_MAX]; +const char* sub_api_name[SUB_API_MAX] = WINUSBX_DRV_NAMES; +bool api_hid_available = false; +#define CHECK_HID_AVAILABLE do { if (!api_hid_available) return LIBUSB_ERROR_ACCESS; } while (0) + +static inline BOOLEAN guid_eq(const GUID *guid1, const GUID *guid2) { + if ((guid1 != NULL) && (guid2 != NULL)) { + return (memcmp(guid1, guid2, sizeof(GUID)) == 0); + } + return false; +} + +#if defined(ENABLE_LOGGING) +static char* guid_to_string(const GUID* guid) +{ + static char guid_string[MAX_GUID_STRING_LENGTH]; + + if (guid == NULL) return NULL; + sprintf(guid_string, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + (unsigned int)guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); + return guid_string; +} +#endif + +/* + * 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 char err_string[ERR_BUFFER_SIZE]; + + DWORD size; + ssize_t i; + uint32_t error_code, format_error; + + error_code = retval?retval:GetLastError(); + + safe_sprintf(err_string, ERR_BUFFER_SIZE, "[%u] ", error_code); + + // Translate codes returned by SetupAPI. The ones we are dealing with are either + // in 0x0000xxxx or 0xE000xxxx and can be distinguished from standard error codes. + // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff545011.aspx + switch (error_code & 0xE0000000) { + case 0: + error_code = HRESULT_FROM_WIN32(error_code); // Still leaves ERROR_SUCCESS unmodified + break; + case 0xE0000000: + error_code = 0x80000000 | (FACILITY_SETUPAPI << 16) | (error_code & 0x0000FFFF); + break; + default: + break; + } + + size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &err_string[safe_strlen(err_string)], + ERR_BUFFER_SIZE - (DWORD)safe_strlen(err_string), NULL); + if (size == 0) { + format_error = GetLastError(); + if (format_error) + safe_sprintf(err_string, ERR_BUFFER_SIZE, + "Windows error code %u (FormatMessage error code %u)", error_code, format_error); + else + safe_sprintf(err_string, ERR_BUFFER_SIZE, "Unknown error code %u", error_code); + } else { + // Remove CR/LF terminators + for (i=safe_strlen(err_string)-1; (i>=0) && ((err_string[i]==0x0A) || (err_string[i]==0x0D)); i--) { + err_string[i] = 0; + } + } + return err_string; +} +#endif + +/* + * Sanitize Microsoft's paths: convert to uppercase, add prefix and fix backslashes. + * Return an allocated sanitized string or NULL on error. + */ +static char* sanitize_path(const char* path) +{ + const char root_prefix[] = "\\\\.\\"; + size_t j, size, root_size; + char* ret_path = NULL; + size_t add_root = 0; + + if (path == NULL) + return NULL; + + size = safe_strlen(path)+1; + root_size = sizeof(root_prefix)-1; + + // Microsoft indiscriminately uses '\\?\', '\\.\', '##?#" or "##.#" for root prefixes. + if (!((size > 3) && (((path[0] == '\\') && (path[1] == '\\') && (path[3] == '\\')) || + ((path[0] == '#') && (path[1] == '#') && (path[3] == '#'))))) { + add_root = root_size; + size += add_root; + } + + if ((ret_path = (char*) calloc(size, 1)) == NULL) + return NULL; + + safe_strcpy(&ret_path[add_root], size-add_root, path); + + // Ensure consistency with root prefix + for (j=0; jcbSize = sizeof(SP_DEVINFO_DATA); + if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + usbi_err(ctx, "Could not obtain device info data for index %u: %s", + _index, windows_error_str(0)); + } + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return false; + } + return true; +} + +/* + * enumerate interfaces for a specific GUID + * + * Parameters: + * dev_info: a pointer to a dev_info list + * dev_info_data: a pointer to an SP_DEVINFO_DATA to be filled (or NULL if not needed) + * guid: the GUID for which to retrieve interface details + * index: zero based index of the interface in the device info list + * + * Note: it is the responsibility of the caller to free the DEVICE_INTERFACE_DETAIL_DATA + * structure returned and call this function repeatedly using the same guid (with an + * incremented index starting at zero) until all interfaces have been returned. + */ +static SP_DEVICE_INTERFACE_DETAIL_DATA_A *get_interface_details(struct libusb_context *ctx, + HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const GUID* guid, unsigned _index) +{ + SP_DEVICE_INTERFACE_DATA dev_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL; + DWORD size; + + if (_index <= 0) { + *dev_info = pSetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); + } + + if (dev_info_data != NULL) { + dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA); + if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + usbi_err(ctx, "Could not obtain device info data for index %u: %s", + _index, windows_error_str(0)); + } + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; + } + } + + dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + if (!pSetupDiEnumDeviceInterfaces(*dev_info, NULL, guid, _index, &dev_interface_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + usbi_err(ctx, "Could not obtain interface data for index %u: %s", + _index, windows_error_str(0)); + } + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; + } + + // Read interface data (dummy + actual) to access the device path + if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) { + // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + usbi_err(ctx, "could not access interface data (dummy) for index %u: %s", + _index, windows_error_str(0)); + goto err_exit; + } + } else { + usbi_err(ctx, "program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong."); + goto err_exit; + } + + if ((dev_interface_details = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) calloc(size, 1)) == NULL) { + usbi_err(ctx, "could not allocate interface data for index %u.", _index); + goto err_exit; + } + + dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, + dev_interface_details, size, &size, NULL)) { + usbi_err(ctx, "could not access interface data (actual) for index %u: %s", + _index, windows_error_str(0)); + } + + return dev_interface_details; + +err_exit: + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; +} + +/* For libusb0 filter */ +static SP_DEVICE_INTERFACE_DETAIL_DATA_A *get_interface_details_filter(struct libusb_context *ctx, + HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const GUID* guid, unsigned _index, char* filter_path){ + SP_DEVICE_INTERFACE_DATA dev_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL; + DWORD size; + if (_index <= 0) { + *dev_info = pSetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); + } + if (dev_info_data != NULL) { + dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA); + if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + usbi_err(ctx, "Could not obtain device info data for index %u: %s", + _index, windows_error_str(0)); + } + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; + } + } + dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + if (!pSetupDiEnumDeviceInterfaces(*dev_info, NULL, guid, _index, &dev_interface_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + usbi_err(ctx, "Could not obtain interface data for index %u: %s", + _index, windows_error_str(0)); + } + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; + } + // Read interface data (dummy + actual) to access the device path + if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) { + // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + usbi_err(ctx, "could not access interface data (dummy) for index %u: %s", + _index, windows_error_str(0)); + goto err_exit; + } + } else { + usbi_err(ctx, "program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong."); + goto err_exit; + } + if ((dev_interface_details = malloc(size)) == NULL) { + usbi_err(ctx, "could not allocate interface data for index %u.", _index); + goto err_exit; + } + dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, + dev_interface_details, size, &size, NULL)) { + usbi_err(ctx, "could not access interface data (actual) for index %u: %s", + _index, windows_error_str(0)); + } + // [trobinso] lookup the libusb0 symbolic index. + if (dev_interface_details) { + HKEY hkey_device_interface=pSetupDiOpenDeviceInterfaceRegKey(*dev_info,&dev_interface_data,0,KEY_READ); + if (hkey_device_interface != INVALID_HANDLE_VALUE) { + DWORD libusb0_symboliclink_index=0; + DWORD value_length=sizeof(DWORD); + DWORD value_type=0; + LONG status; + status = pRegQueryValueExW(hkey_device_interface, L"LUsb0", NULL, &value_type, + (LPBYTE) &libusb0_symboliclink_index, &value_length); + if (status == ERROR_SUCCESS) { + if (libusb0_symboliclink_index < 256) { + // libusb0.sys is connected to this device instance. + // If the the device interface guid is {F9F3FF14-AE21-48A0-8A25-8011A7A931D9} then it's a filter. + safe_sprintf(filter_path, sizeof("\\\\.\\libusb0-0000"), "\\\\.\\libusb0-%04d", libusb0_symboliclink_index); + usbi_dbg("assigned libusb0 symbolic link %s", filter_path); + } else { + // libusb0.sys was connected to this device instance at one time; but not anymore. + } + } + pRegCloseKey(hkey_device_interface); + } + } + return dev_interface_details; +err_exit: + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL;} + +/* Hash table functions - modified From glibc 2.3.2: + [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986 + [Knuth] The Art of Computer Programming, part 3 (6.4) */ +typedef struct htab_entry { + unsigned long used; + char* str; +} htab_entry; +htab_entry* htab_table = NULL; +usbi_mutex_t htab_write_mutex = NULL; +unsigned long htab_size, htab_filled; + +/* For the used double hash method the table size has to be a prime. To + correct the user given table size we need a prime test. This trivial + algorithm is adequate because the code is called only during init and + the number is likely to be small */ +static int isprime(unsigned long number) +{ + // no even number will be passed + unsigned int divider = 3; + + while((divider * divider < number) && (number % divider != 0)) + divider += 2; + + return (number % divider != 0); +} + +/* Before using the hash table we must allocate memory for it. + We allocate one element more as the found prime number says. + This is done for more effective indexing as explained in the + comment for the hash function. */ +static int htab_create(struct libusb_context *ctx, unsigned long nel) +{ + if (htab_table != NULL) { + usbi_err(ctx, "hash table already allocated"); + } + + // Create a mutex + usbi_mutex_init(&htab_write_mutex, NULL); + + // Change nel to the first prime number not smaller as nel. + nel |= 1; + while(!isprime(nel)) + nel += 2; + + htab_size = nel; + usbi_dbg("using %d entries hash table", nel); + htab_filled = 0; + + // allocate memory and zero out. + htab_table = (htab_entry*) calloc(htab_size + 1, sizeof(htab_entry)); + if (htab_table == NULL) { + usbi_err(ctx, "could not allocate space for hash table"); + return 0; + } + + return 1; +} + +/* After using the hash table it has to be destroyed. */ +static void htab_destroy(void) +{ + size_t i; + if (htab_table == NULL) { + return; + } + + for (i=0; i New entry + + // If the table is full return an error + if (htab_filled >= htab_size) { + usbi_err(NULL, "hash table is full (%d entries)", htab_size); + return 0; + } + + // Concurrent threads might be storing the same entry at the same time + // (eg. "simultaneous" enums from different threads) => use a mutex + usbi_mutex_lock(&htab_write_mutex); + // Just free any previously allocated string (which should be the same as + // new one). The possibility of concurrent threads storing a collision + // string (same hash, different string) at the same time is extremely low + safe_free(htab_table[idx].str); + htab_table[idx].used = hval; + htab_table[idx].str = (char*) malloc(safe_strlen(str)+1); + if (htab_table[idx].str == NULL) { + usbi_err(NULL, "could not duplicate string for hash table"); + usbi_mutex_unlock(&htab_write_mutex); + return 0; + } + memcpy(htab_table[idx].str, str, safe_strlen(str)+1); + ++htab_filled; + usbi_mutex_unlock(&htab_write_mutex); + + return idx; +} + +/* + * Returns the session ID of a device's nth level ancestor + * If there's no device at the nth level, return 0 + */ +static unsigned long get_ancestor_session_id(DWORD devinst, unsigned level) +{ + DWORD parent_devinst; + unsigned long session_id = 0; + char* sanitized_path = NULL; + char path[MAX_PATH_LENGTH]; + unsigned i; + + if (level < 1) return 0; + for (i = 0; idev); + struct libusb_config_descriptor *conf_desc; + const struct libusb_interface_descriptor *if_desc; + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + + r = libusb_get_config_descriptor(dev_handle->dev, 0, &conf_desc); + if (r != LIBUSB_SUCCESS) { + usbi_warn(ctx, "could not read config descriptor: error %d", r); + return r; + } + + if_desc = &conf_desc->interface[iface].altsetting[altsetting]; + safe_free(priv->usb_interface[iface].endpoint); + + if (if_desc->bNumEndpoints == 0) { + usbi_dbg("no endpoints found for interface %d", iface); + return LIBUSB_SUCCESS; + } + + priv->usb_interface[iface].endpoint = (uint8_t*) malloc(if_desc->bNumEndpoints); + if (priv->usb_interface[iface].endpoint == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + + priv->usb_interface[iface].nb_endpoints = if_desc->bNumEndpoints; + for (i=0; ibNumEndpoints; i++) { + priv->usb_interface[iface].endpoint[i] = if_desc->endpoint[i].bEndpointAddress; + usbi_dbg("(re)assigned endpoint %02X to interface %d", priv->usb_interface[iface].endpoint[i], iface); + } + libusb_free_config_descriptor(conf_desc); + + // Extra init may be required to configure endpoints + return priv->apib->configure_endpoints(SUB_API_NOTSET, dev_handle, iface); +} + +// Lookup for a match in the list of API driver names +// return -1 if not found, driver match number otherwise +static int get_sub_api(char* driver, int api){ + int i; + const char sep_str[2] = {LIST_SEPARATOR, 0}; + char *tok, *tmp_str; + size_t len = safe_strlen(driver); + + if (len == 0) return SUB_API_NOTSET; + tmp_str = (char*) calloc(len+1, 1); + if (tmp_str == NULL) return SUB_API_NOTSET; + memcpy(tmp_str, driver, len+1); + tok = strtok(tmp_str, sep_str); + while (tok != NULL) { + for (i=0; idev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv( + transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int current_interface = *interface_number; + int r = LIBUSB_SUCCESS; + + switch(api_type) { + case USB_API_WINUSBX: + case USB_API_HID: + break; + default: + return LIBUSB_ERROR_INVALID_PARAM; + } + + usbi_mutex_lock(&autoclaim_lock); + if (current_interface < 0) // No serviceable interface was found + { + for (current_interface=0; current_interfaceusb_interface[current_interface].apib->id == api_type) + && (libusb_claim_interface(transfer->dev_handle, current_interface) == LIBUSB_SUCCESS) ) { + usbi_dbg("auto-claimed interface %d for control request", current_interface); + if (handle_priv->autoclaim_count[current_interface] != 0) { + usbi_warn(ctx, "program assertion failed - autoclaim_count was nonzero"); + } + handle_priv->autoclaim_count[current_interface]++; + break; + } + } + if (current_interface == USB_MAXINTERFACES) { + usbi_err(ctx, "could not auto-claim any interface"); + r = LIBUSB_ERROR_NOT_FOUND; + } + } else { + // If we have a valid interface that was autoclaimed, we must increment + // its autoclaim count so that we can prevent an early release. + if (handle_priv->autoclaim_count[current_interface] != 0) { + handle_priv->autoclaim_count[current_interface]++; + } + } + usbi_mutex_unlock(&autoclaim_lock); + + *interface_number = current_interface; + return r; + +} + +static void auto_release(struct usbi_transfer *itransfer) +{ + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + libusb_device_handle *dev_handle = transfer->dev_handle; + struct windows_device_handle_priv* handle_priv = _device_handle_priv(dev_handle); + int r; + + usbi_mutex_lock(&autoclaim_lock); + if (handle_priv->autoclaim_count[transfer_priv->interface_number] > 0) { + handle_priv->autoclaim_count[transfer_priv->interface_number]--; + if (handle_priv->autoclaim_count[transfer_priv->interface_number] == 0) { + r = libusb_release_interface(dev_handle, transfer_priv->interface_number); + if (r == LIBUSB_SUCCESS) { + usbi_dbg("auto-released interface %d", transfer_priv->interface_number); + } else { + usbi_dbg("failed to auto-release interface %d (%s)", + transfer_priv->interface_number, libusb_error_name((enum libusb_error)r)); + } + } + } + usbi_mutex_unlock(&autoclaim_lock); +} + +/* Windows version dtection */ +static BOOL is_x64(void) +{ + BOOL ret = FALSE; + // Detect if we're running a 32 or 64 bit system + if (sizeof(uintptr_t) < 8) { + DLL_LOAD_PREFIXED(Kernel32.dll, p, IsWow64Process, FALSE); + if (pIsWow64Process != NULL) { + (*pIsWow64Process)(GetCurrentProcess(), &ret); + } + } else { + ret = TRUE; + } + return ret; +} + +static void get_windows_version(void) +{ + OSVERSIONINFOEXA vi, vi2; + const char* w = 0; + const char* w64 = "32 bit"; + char* vptr; + size_t vlen; + unsigned major, minor; + ULONGLONG major_equal, minor_equal; + BOOL ws; + + memset(&vi, 0, sizeof(vi)); + vi.dwOSVersionInfoSize = sizeof(vi); + if (!GetVersionExA((OSVERSIONINFOA *)&vi)) { + memset(&vi, 0, sizeof(vi)); + vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + if (!GetVersionExA((OSVERSIONINFOA *)&vi)) + return; + } + + if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) { + + if (vi.dwMajorVersion > 6 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion >= 2)) { + // Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version + // See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx + + major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL); + for (major = vi.dwMajorVersion; major <= 9; major++) { + memset(&vi2, 0, sizeof(vi2)); + vi2.dwOSVersionInfoSize = sizeof(vi2); vi2.dwMajorVersion = major; + if (!VerifyVersionInfoA(&vi2, VER_MAJORVERSION, major_equal)) + continue; + if (vi.dwMajorVersion < major) { + vi.dwMajorVersion = major; vi.dwMinorVersion = 0; + } + + minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL); + for (minor = vi.dwMinorVersion; minor <= 9; minor++) { + memset(&vi2, 0, sizeof(vi2)); vi2.dwOSVersionInfoSize = sizeof(vi2); + vi2.dwMinorVersion = minor; + if (!VerifyVersionInfoA(&vi2, VER_MINORVERSION, minor_equal)) + continue; + vi.dwMinorVersion = minor; + break; + } + + break; + } + } + + if (vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) { + ws = (vi.wProductType <= VER_NT_WORKSTATION); + windows_version = vi.dwMajorVersion << 4 | vi.dwMinorVersion; + switch (windows_version) { + case 0x50: w = "2000"; + break; + case 0x51: w = "XP"; + break; + case 0x52: w = ("2003"); + break; + case 0x60: w = (ws?"Vista":"2008"); + break; + case 0x61: w = (ws?"7":"2008_R2"); + break; + case 0x62: w = (ws?"8":"2012"); + break; + case 0x63: w = (ws?"8.1":"2012_R2"); + break; + case 0x64: w = (ws?"8.2":"2012_R3"); + break; + default: + if (windows_version < 0x50) + windows_version = WINDOWS_UNSUPPORTED; + else + w = "9 or later"; + break; + } + } + } + + if (is_x64()) + w64 = "64-bit"; + + vptr = &windows_version_str[sizeof("Windows ") - 1]; + vlen = sizeof(windows_version_str) - sizeof("Windows ") - 1; + if (!w) + safe_sprintf(vptr, vlen, "%s %u.%u %s", (vi.dwPlatformId==VER_PLATFORM_WIN32_NT?"NT":"??"), + (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64); + else if (vi.wServicePackMinor) + safe_sprintf(vptr, vlen, "%s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, w64); + else if (vi.wServicePackMajor) + safe_sprintf(vptr, vlen, "%s SP%u %s", w, vi.wServicePackMajor, w64); + else + safe_sprintf(vptr, vlen, "%s %s", w, w64); +} + +/* + * init: libusb backend init function + * + * This function enumerates the HCDs (Host Controller Drivers) and populates our private HCD list + * In our implementation, we equate Windows' "HCD" to libusb's "bus". Note that bus is zero indexed. + * HCDs are not expected to change after init (might not hold true for hot pluggable USB PCI card?) + */ +static int windows_init(struct libusb_context *ctx) +{ + int i, r = LIBUSB_ERROR_OTHER; + HANDLE semaphore; + char sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID) + + sprintf(sem_name, "libusb_init%08X", (unsigned int)GetCurrentProcessId()&0xFFFFFFFF); + semaphore = CreateSemaphoreA(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? + get_windows_version(); + usbi_dbg(windows_version_str); + if (windows_version == WINDOWS_UNSUPPORTED) { + usbi_err(ctx, "This version of Windows is NOT supported"); + r = LIBUSB_ERROR_NOT_SUPPORTED; + goto init_exit; + } + + // We need a lock for proper auto-release + usbi_mutex_init(&autoclaim_lock, NULL); + + // Initialize pollable file descriptors + init_polling(); + + // Load DLL imports + if (init_dlls() != LIBUSB_SUCCESS) { + usbi_err(ctx, "could not resolve DLL functions"); + return LIBUSB_ERROR_NOT_FOUND; + } + + // Initialize the low level APIs (we don't care about errors at this stage) + for (i=0; inum_configurations = 1; + priv->dev_descriptor.bLength = sizeof(USB_DEVICE_DESCRIPTOR); + priv->dev_descriptor.bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE; + priv->dev_descriptor.bNumConfigurations = 1; + priv->active_config = 1; + + if (priv->parent_dev == NULL) { + usbi_err(ctx, "program assertion failed - HCD hub has no parent"); + return LIBUSB_ERROR_NO_DEVICE; + } + parent_priv = _device_priv(priv->parent_dev); + if (sscanf(parent_priv->path, "\\\\.\\PCI#VEN_%04x&DEV_%04x%*s", &vid, &pid) == 2) { + priv->dev_descriptor.idVendor = (uint16_t)vid; + priv->dev_descriptor.idProduct = (uint16_t)pid; + } else { + usbi_warn(ctx, "could not infer VID/PID of HCD hub from '%s'", parent_priv->path); + priv->dev_descriptor.idVendor = 0x1d6b; // Linux Foundation root hub + priv->dev_descriptor.idProduct = 1; + } + return LIBUSB_SUCCESS; +} + +/* + * fetch and cache all the config descriptors through I/O + */ +static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle, char* device_id) +{ + DWORD size, ret_size; + struct libusb_context *ctx = DEVICE_CTX(dev); + struct windows_device_priv *priv = _device_priv(dev); + int r; + uint8_t i; + + USB_CONFIGURATION_DESCRIPTOR_SHORT cd_buf_short; // dummy request + PUSB_DESCRIPTOR_REQUEST cd_buf_actual = NULL; // actual request + PUSB_CONFIGURATION_DESCRIPTOR cd_data = NULL; + + if (dev->num_configurations == 0) + return LIBUSB_ERROR_INVALID_PARAM; + + priv->config_descriptor = (unsigned char**) calloc(dev->num_configurations, sizeof(unsigned char*)); + if (priv->config_descriptor == NULL) + return LIBUSB_ERROR_NO_MEM; + for (i=0; inum_configurations; i++) + priv->config_descriptor[i] = NULL; + + for (i=0, r=LIBUSB_SUCCESS; ; i++) + { + // safe loop: release all dynamic resources + safe_free(cd_buf_actual); + + // safe loop: end of loop condition + if ((i >= dev->num_configurations) || (r != LIBUSB_SUCCESS)) + break; + + size = sizeof(USB_CONFIGURATION_DESCRIPTOR_SHORT); + memset(&cd_buf_short, 0, size); + + cd_buf_short.req.ConnectionIndex = (ULONG)priv->port; + cd_buf_short.req.SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN; + cd_buf_short.req.SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR; + cd_buf_short.req.SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i; + cd_buf_short.req.SetupPacket.wIndex = i; + cd_buf_short.req.SetupPacket.wLength = (USHORT)(size - sizeof(USB_DESCRIPTOR_REQUEST)); + + // Dummy call to get the required data size. Initial failures are reported as info rather + // than error as they can occur for non-penalizing situations, such as with some hubs. + // coverity[tainted_data_argument] + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, &cd_buf_short, size, + &cd_buf_short, size, &ret_size, NULL)) { + usbi_info(ctx, "could not access configuration descriptor (dummy) for '%s': %s", device_id, windows_error_str(0)); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + if ((ret_size != size) || (cd_buf_short.data.wTotalLength < sizeof(USB_CONFIGURATION_DESCRIPTOR))) { + usbi_info(ctx, "unexpected configuration descriptor size (dummy) for '%s'.", device_id); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + size = sizeof(USB_DESCRIPTOR_REQUEST) + cd_buf_short.data.wTotalLength; + if ((cd_buf_actual = (PUSB_DESCRIPTOR_REQUEST) calloc(1, size)) == NULL) { + usbi_err(ctx, "could not allocate configuration descriptor buffer for '%s'.", device_id); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + memset(cd_buf_actual, 0, size); + + // Actual call + cd_buf_actual->ConnectionIndex = (ULONG)priv->port; + cd_buf_actual->SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN; + cd_buf_actual->SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR; + cd_buf_actual->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i; + cd_buf_actual->SetupPacket.wIndex = i; + cd_buf_actual->SetupPacket.wLength = (USHORT)(size - sizeof(USB_DESCRIPTOR_REQUEST)); + + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, cd_buf_actual, size, + cd_buf_actual, size, &ret_size, NULL)) { + usbi_err(ctx, "could not access configuration descriptor (actual) for '%s': %s", device_id, windows_error_str(0)); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + cd_data = (PUSB_CONFIGURATION_DESCRIPTOR)((UCHAR*)cd_buf_actual+sizeof(USB_DESCRIPTOR_REQUEST)); + + if ((size != ret_size) || (cd_data->wTotalLength != cd_buf_short.data.wTotalLength)) { + usbi_err(ctx, "unexpected configuration descriptor size (actual) for '%s'.", device_id); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + if (cd_data->bDescriptorType != USB_CONFIGURATION_DESCRIPTOR_TYPE) { + usbi_err(ctx, "not a configuration descriptor for '%s'", device_id); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + usbi_dbg("cached config descriptor %d (bConfigurationValue=%d, %d bytes)", + i, cd_data->bConfigurationValue, cd_data->wTotalLength); + + // Cache the descriptor + priv->config_descriptor[i] = (unsigned char*) malloc(cd_data->wTotalLength); + if (priv->config_descriptor[i] == NULL) + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + memcpy(priv->config_descriptor[i], cd_data, cd_data->wTotalLength); + } + return LIBUSB_SUCCESS; +} + +/* + * Populate a libusb device structure + */ +static int init_device(struct libusb_device* dev, struct libusb_device* parent_dev, + uint8_t port_number, char* device_id, DWORD devinst) +{ + HANDLE handle; + DWORD size; + USB_NODE_CONNECTION_INFORMATION_EX conn_info; + USB_NODE_CONNECTION_INFORMATION_EX_V2 conn_info_v2; + struct windows_device_priv *priv, *parent_priv; + struct libusb_context *ctx; + struct libusb_device* tmp_dev; + unsigned i; + + if ((dev == NULL) || (parent_dev == NULL)) { + return LIBUSB_ERROR_NOT_FOUND; + } + ctx = DEVICE_CTX(dev); + priv = _device_priv(dev); + parent_priv = _device_priv(parent_dev); + if (parent_priv->apib->id != USB_API_HUB) { + usbi_warn(ctx, "parent for device '%s' is not a hub", device_id); + return LIBUSB_ERROR_NOT_FOUND; + } + + // It is possible for the parent hub not to have been initialized yet + // If that's the case, lookup the ancestors to set the bus number + if (parent_dev->bus_number == 0) { + for (i=2; ; i++) { + tmp_dev = usbi_get_device_by_session_id(ctx, get_ancestor_session_id(devinst, i)); + if (tmp_dev == NULL) break; + if (tmp_dev->bus_number != 0) { + usbi_dbg("got bus number from ancestor #%d", i); + parent_dev->bus_number = tmp_dev->bus_number; + libusb_unref_device(tmp_dev); + break; + } + libusb_unref_device(tmp_dev); + } + } + if (parent_dev->bus_number == 0) { + usbi_err(ctx, "program assertion failed: unable to find ancestor bus number for '%s'", device_id); + return LIBUSB_ERROR_NOT_FOUND; + } + dev->bus_number = parent_dev->bus_number; + priv->port = port_number; + dev->port_number = port_number; + priv->depth = parent_priv->depth + 1; + priv->parent_dev = parent_dev; + dev->parent_dev = parent_dev; + + // If the device address is already set, we can stop here + if (dev->device_address != 0) { + return LIBUSB_SUCCESS; + } + memset(&conn_info, 0, sizeof(conn_info)); + if (priv->depth != 0) { // Not a HCD hub + handle = CreateFileA(parent_priv->path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + if (handle == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "could not open hub %s: %s", parent_priv->path, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + size = sizeof(conn_info); + conn_info.ConnectionIndex = (ULONG)port_number; + // coverity[tainted_data_argument] + if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, &conn_info, size, + &conn_info, size, &size, NULL)) { + usbi_warn(ctx, "could not get node connection information for device '%s': %s", + device_id, windows_error_str(0)); + safe_closehandle(handle); + return LIBUSB_ERROR_NO_DEVICE; + } + if (conn_info.ConnectionStatus == NoDeviceConnected) { + usbi_err(ctx, "device '%s' is no longer connected!", device_id); + safe_closehandle(handle); + return LIBUSB_ERROR_NO_DEVICE; + } + memcpy(&priv->dev_descriptor, &(conn_info.DeviceDescriptor), sizeof(USB_DEVICE_DESCRIPTOR)); + dev->num_configurations = priv->dev_descriptor.bNumConfigurations; + priv->active_config = conn_info.CurrentConfigurationValue; + usbi_dbg("found %d configurations (active conf: %d)", dev->num_configurations, priv->active_config); + // If we can't read the config descriptors, just set the number of confs to zero + if (cache_config_descriptors(dev, handle, device_id) != LIBUSB_SUCCESS) { + dev->num_configurations = 0; + priv->dev_descriptor.bNumConfigurations = 0; + } + + // In their great wisdom, Microsoft decided to BREAK the USB speed report between Windows 7 and Windows 8 + if (windows_version >= WINDOWS_8) { + memset(&conn_info_v2, 0, sizeof(conn_info_v2)); + size = sizeof(conn_info_v2); + conn_info_v2.ConnectionIndex = (ULONG)port_number; + conn_info_v2.Length = size; + conn_info_v2.SupportedUsbProtocols.Usb300 = 1; + if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, + &conn_info_v2, size, &conn_info_v2, size, &size, NULL)) { + usbi_warn(ctx, "could not get node connection information (V2) for device '%s': %s", + device_id, windows_error_str(0)); + } else if (conn_info_v2.Flags.DeviceIsOperatingAtSuperSpeedOrHigher) { + conn_info.Speed = 3; + } + } + + safe_closehandle(handle); + + if (conn_info.DeviceAddress > UINT8_MAX) { + usbi_err(ctx, "program assertion failed: device address overflow"); + } + dev->device_address = (uint8_t)conn_info.DeviceAddress + 1; + if (dev->device_address == 1) { + usbi_err(ctx, "program assertion failed: device address collision with root hub"); + } + switch (conn_info.Speed) { + case 0: dev->speed = LIBUSB_SPEED_LOW; break; + case 1: dev->speed = LIBUSB_SPEED_FULL; break; + case 2: dev->speed = LIBUSB_SPEED_HIGH; break; + case 3: dev->speed = LIBUSB_SPEED_SUPER; break; + default: + usbi_warn(ctx, "Got unknown device speed %d", conn_info.Speed); + break; + } + } else { + dev->device_address = 1; // root hubs are set to use device number 1 + force_hcd_device_descriptor(dev); + } + + usbi_sanitize_device(dev); + + usbi_dbg("(bus: %d, addr: %d, depth: %d, port: %d): '%s'", + dev->bus_number, dev->device_address, priv->depth, priv->port, device_id); + + return LIBUSB_SUCCESS; +} + +// Returns the api type, or 0 if not found/unsupported +static void get_api_type(struct libusb_context *ctx, HDEVINFO *dev_info, + SP_DEVINFO_DATA *dev_info_data, int *api, int *sub_api) +{ + // Precedence for filter drivers vs driver is in the order of this array + struct driver_lookup lookup[3] = { + {"\0\0", SPDRP_SERVICE, "driver"}, + {"\0\0", SPDRP_UPPERFILTERS, "upper filter driver"}, + {"\0\0", SPDRP_LOWERFILTERS, "lower filter driver"} + }; + DWORD size, reg_type; + unsigned k, l; + int i, j; + + *api = USB_API_UNSUPPORTED; + *sub_api = SUB_API_NOTSET; + // Check the service & filter names to know the API we should use + for (k=0; k<3; k++) { + if (pSetupDiGetDeviceRegistryPropertyA(*dev_info, dev_info_data, lookup[k].reg_prop, + ®_type, (BYTE*)lookup[k].list, MAX_KEY_LENGTH, &size)) { + // Turn the REG_SZ SPDRP_SERVICE into REG_MULTI_SZ + if (lookup[k].reg_prop == SPDRP_SERVICE) { + // our buffers are MAX_KEY_LENGTH+1 so we can overflow if needed + lookup[k].list[safe_strlen(lookup[k].list)+1] = 0; + } + // MULTI_SZ is a pain to work with. Turn it into something much more manageable + // NB: none of the driver names we check against contain LIST_SEPARATOR, + // (currently ';'), so even if an unsuported one does, it's not an issue + for (l=0; (lookup[k].list[l] != 0) || (lookup[k].list[l+1] != 0); l++) { + if (lookup[k].list[l] == 0) { + lookup[k].list[l] = LIST_SEPARATOR; + } + } + usbi_dbg("%s(s): %s", lookup[k].designation, lookup[k].list); + } else { + if (GetLastError() != ERROR_INVALID_DATA) { + usbi_dbg("could not access %s: %s", lookup[k].designation, windows_error_str(0)); + } + lookup[k].list[0] = 0; + } + } + + for (i=1; i= 0) { + usbi_dbg("matched %s name against %s", + lookup[k].designation, (i!=USB_API_WINUSBX)?usb_api_backend[i].designation:sub_api_name[j]); + *api = i; + *sub_api = j; + return; + } + } + } +} + +static int set_composite_interface(struct libusb_context* ctx, struct libusb_device* dev, + char* dev_interface_path, char* device_id, int api, int sub_api) +{ + unsigned i; + struct windows_device_priv *priv = _device_priv(dev); + int interface_number; + + if (priv->apib->id != USB_API_COMPOSITE) { + usbi_err(ctx, "program assertion failed: '%s' is not composite", device_id); + return LIBUSB_ERROR_NO_DEVICE; + } + + // Because MI_## are not necessarily in sequential order (some composite + // devices will have only MI_00 & MI_03 for instance), we retrieve the actual + // interface number from the path's MI value + interface_number = 0; + for (i=0; device_id[i] != 0; ) { + if ( (device_id[i++] == 'M') && (device_id[i++] == 'I') + && (device_id[i++] == '_') ) { + interface_number = (device_id[i++] - '0')*10; + interface_number += device_id[i] - '0'; + break; + } + } + + if (device_id[i] == 0) { + usbi_warn(ctx, "failure to read interface number for %s. Using default value %d", + device_id, interface_number); + } + + if (priv->usb_interface[interface_number].path != NULL) { + if (api == USB_API_HID) { + // HID devices can have multiple collections (COL##) for each MI_## interface + usbi_dbg("interface[%d] already set - ignoring HID collection: %s", + interface_number, device_id); + return LIBUSB_ERROR_ACCESS; + } + // In other cases, just use the latest data + safe_free(priv->usb_interface[interface_number].path); + } + + usbi_dbg("interface[%d] = %s", interface_number, dev_interface_path); + priv->usb_interface[interface_number].path = dev_interface_path; + priv->usb_interface[interface_number].apib = &usb_api_backend[api]; + priv->usb_interface[interface_number].sub_api = sub_api; + if ((api == USB_API_HID) && (priv->hid == NULL)) { + priv->hid = (struct hid_device_priv*) calloc(1, sizeof(struct hid_device_priv)); + if (priv->hid == NULL) + return LIBUSB_ERROR_NO_MEM; + } + + return LIBUSB_SUCCESS; +} + +static int set_hid_interface(struct libusb_context* ctx, struct libusb_device* dev, + char* dev_interface_path) +{ + int i; + struct windows_device_priv *priv = _device_priv(dev); + + if (priv->hid == NULL) { + usbi_err(ctx, "program assertion failed: parent is not HID"); + return LIBUSB_ERROR_NO_DEVICE; + } + if (priv->hid->nb_interfaces == USB_MAXINTERFACES) { + usbi_err(ctx, "program assertion failed: max USB interfaces reached for HID device"); + return LIBUSB_ERROR_NO_DEVICE; + } + for (i=0; ihid->nb_interfaces; i++) { + if (safe_strcmp(priv->usb_interface[i].path, dev_interface_path) == 0) { + usbi_dbg("interface[%d] already set to %s", i, dev_interface_path); + return LIBUSB_SUCCESS; + } + } + + priv->usb_interface[priv->hid->nb_interfaces].path = dev_interface_path; + priv->usb_interface[priv->hid->nb_interfaces].apib = &usb_api_backend[USB_API_HID]; + usbi_dbg("interface[%d] = %s", priv->hid->nb_interfaces, dev_interface_path); + priv->hid->nb_interfaces++; + return LIBUSB_SUCCESS; +} + +/* + * get_device_list: libusb backend device enumeration function + */ +static int windows_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) +{ + struct discovered_devs *discdevs; + HDEVINFO dev_info = { 0 }; + const char* usb_class[] = {"USB", "NUSB3", "IUSB3"}; + SP_DEVINFO_DATA dev_info_data = { 0 }; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL; + GUID hid_guid; +#define MAX_ENUM_GUIDS 64 + const GUID* guid[MAX_ENUM_GUIDS]; +#define HCD_PASS 0 +#define HUB_PASS 1 +#define GEN_PASS 2 +#define DEV_PASS 3 +#define HID_PASS 4 + int r = LIBUSB_SUCCESS; + int api, sub_api; + size_t class_index = 0; + unsigned int nb_guids, pass, i, j, ancestor; + char path[MAX_PATH_LENGTH]; + char strbuf[MAX_PATH_LENGTH]; + struct libusb_device *dev, *parent_dev; + struct windows_device_priv *priv, *parent_priv; + char* dev_interface_path = NULL; + char* dev_id_path = NULL; + unsigned long session_id; + DWORD size, reg_type, port_nr, install_state; + HKEY key; + WCHAR guid_string_w[MAX_GUID_STRING_LENGTH]; + GUID* if_guid; + LONG s; + // Keep a list of newly allocated devs to unref + libusb_device** unref_list; + unsigned int unref_size = 64; + unsigned int unref_cur = 0; + + // PASS 1 : (re)enumerate HCDs (allows for HCD hotplug) + // PASS 2 : (re)enumerate HUBS + // PASS 3 : (re)enumerate generic USB devices (including driverless) + // and list additional USB device interface GUIDs to explore + // PASS 4 : (re)enumerate master USB devices that have a device interface + // PASS 5+: (re)enumerate device interfaced GUIDs (including HID) and + // set the device interfaces. + + // Init the GUID table + guid[HCD_PASS] = &GUID_DEVINTERFACE_USB_HOST_CONTROLLER; + guid[HUB_PASS] = &GUID_DEVINTERFACE_USB_HUB; + guid[GEN_PASS] = NULL; + guid[DEV_PASS] = &GUID_DEVINTERFACE_USB_DEVICE; + HidD_GetHidGuid(&hid_guid); + guid[HID_PASS] = &hid_guid; + nb_guids = HID_PASS+1; + + unref_list = (libusb_device**) calloc(unref_size, sizeof(libusb_device*)); + if (unref_list == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + + for (pass = 0; ((pass < nb_guids) && (r == LIBUSB_SUCCESS)); pass++) { +//#define ENUM_DEBUG +#ifdef ENUM_DEBUG + const char *passname[] = { "HCD", "HUB", "GEN", "DEV", "HID", "EXT" }; + usbi_dbg("\n#### PROCESSING %ss %s", passname[(pass<=HID_PASS)?pass:HID_PASS+1], + (pass!=GEN_PASS)?guid_to_string(guid[pass]):""); +#endif + for (i = 0; ; i++) { + // safe loop: free up any (unprotected) dynamic resource + // NB: this is always executed before breaking the loop + safe_free(dev_interface_details); + safe_free(dev_interface_path); + safe_free(dev_id_path); + priv = parent_priv = NULL; + dev = parent_dev = NULL; + + // Safe loop: end of loop conditions + if (r != LIBUSB_SUCCESS) { + break; + } + if ((pass == HCD_PASS) && (i == UINT8_MAX)) { + usbi_warn(ctx, "program assertion failed - found more than %d buses, skipping the rest.", UINT8_MAX); + break; + } + if (pass != GEN_PASS) { + // Except for GEN, all passes deal with device interfaces + dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid[pass], i); + if (dev_interface_details == NULL) { + break; + } else { + dev_interface_path = sanitize_path(dev_interface_details->DevicePath); + if (dev_interface_path == NULL) { + usbi_warn(ctx, "could not sanitize device interface path for '%s'", dev_interface_details->DevicePath); + continue; + } + } + } else { + // Workaround for a Nec/Renesas USB 3.0 driver bug where root hubs are + // being listed under the "NUSB3" PnP Symbolic Name rather than "USB". + // The Intel USB 3.0 driver behaves similar, but uses "IUSB3" + for (; class_index < ARRAYSIZE(usb_class); class_index++) { + if (get_devinfo_data(ctx, &dev_info, &dev_info_data, usb_class[class_index], i)) + break; + i = 0; + } + if (class_index >= ARRAYSIZE(usb_class)) + break; + } + + // Read the Device ID path. This is what we'll use as UID + // Note that if the device is plugged in a different port or hub, the Device ID changes + if (CM_Get_Device_IDA(dev_info_data.DevInst, path, sizeof(path), 0) != CR_SUCCESS) { + usbi_warn(ctx, "could not read the device id path for devinst %X, skipping", + dev_info_data.DevInst); + continue; + } + dev_id_path = sanitize_path(path); + if (dev_id_path == NULL) { + usbi_warn(ctx, "could not sanitize device id path for devinst %X, skipping", + dev_info_data.DevInst); + continue; + } +#ifdef ENUM_DEBUG + usbi_dbg("PRO: %s", dev_id_path); +#endif + + // The SPDRP_ADDRESS for USB devices is the device port number on the hub + port_nr = 0; + if ((pass >= HUB_PASS) && (pass <= GEN_PASS)) { + if ( (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ADDRESS, + ®_type, (BYTE*)&port_nr, 4, &size)) + || (size != 4) ) { + usbi_warn(ctx, "could not retrieve port number for device '%s', skipping: %s", + dev_id_path, windows_error_str(0)); + continue; + } + } + + // Set API to use or get additional data from generic pass + api = USB_API_UNSUPPORTED; + sub_api = SUB_API_NOTSET; + switch (pass) { + case HCD_PASS: + break; + case GEN_PASS: + // We use the GEN pass to detect driverless devices... + size = sizeof(strbuf); + if (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_DRIVER, + ®_type, (BYTE*)strbuf, size, &size)) { + usbi_info(ctx, "The following device has no driver: '%s'", dev_id_path); + usbi_info(ctx, "libusb will not be able to access it."); + } + // ...and to add the additional device interface GUIDs + key = pSetupDiOpenDevRegKey(dev_info, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); + if (key != INVALID_HANDLE_VALUE) { + size = sizeof(guid_string_w); + s = pRegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, ®_type, + (BYTE*)guid_string_w, &size); + pRegCloseKey(key); + if (s == ERROR_SUCCESS) { + if (nb_guids >= MAX_ENUM_GUIDS) { + // If this assert is ever reported, grow a GUID table dynamically + usbi_err(ctx, "program assertion failed: too many GUIDs"); + LOOP_BREAK(LIBUSB_ERROR_OVERFLOW); + } + if_guid = (GUID*) calloc(1, sizeof(GUID)); + pCLSIDFromString(guid_string_w, if_guid); + guid[nb_guids++] = if_guid; + usbi_dbg("extra GUID: %s", guid_to_string(if_guid)); + } + } + break; + case HID_PASS: + api = USB_API_HID; + break; + default: + // Get the API type (after checking that the driver installation is OK) + if ( (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_INSTALL_STATE, + ®_type, (BYTE*)&install_state, 4, &size)) + || (size != 4) ){ + usbi_warn(ctx, "could not detect installation state of driver for '%s': %s", + dev_id_path, windows_error_str(0)); + } else if (install_state != 0) { + usbi_warn(ctx, "driver for device '%s' is reporting an issue (code: %d) - skipping", + dev_id_path, install_state); + continue; + } + get_api_type(ctx, &dev_info, &dev_info_data, &api, &sub_api); + break; + } + + // Find parent device (for the passes that need it) + switch (pass) { + case HCD_PASS: + case DEV_PASS: + case HUB_PASS: + break; + default: + // Go through the ancestors until we see a face we recognize + parent_dev = NULL; + for (ancestor = 1; parent_dev == NULL; ancestor++) { + session_id = get_ancestor_session_id(dev_info_data.DevInst, ancestor); + if (session_id == 0) { + break; + } + parent_dev = usbi_get_device_by_session_id(ctx, session_id); + } + if (parent_dev == NULL) { + usbi_dbg("unlisted ancestor for '%s' (non USB HID, newly connected, etc.) - ignoring", dev_id_path); + continue; + } + parent_priv = _device_priv(parent_dev); + // virtual USB devices are also listed during GEN - don't process these yet + if ( (pass == GEN_PASS) && (parent_priv->apib->id != USB_API_HUB) ) { + libusb_unref_device(parent_dev); + continue; + } + break; + } + + // Create new or match existing device, using the (hashed) device_id as session id + if (pass <= DEV_PASS) { // For subsequent passes, we'll lookup the parent + // These are the passes that create "new" devices + session_id = htab_hash(dev_id_path); + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev == NULL) { + if (pass == DEV_PASS) { + // This can occur if the OS only reports a newly plugged device after we started enum + usbi_warn(ctx, "'%s' was only detected in late pass (newly connected device?)" + " - ignoring", dev_id_path); + continue; + } + usbi_dbg("allocating new device for session [%X]", session_id); + if ((dev = usbi_alloc_device(ctx, session_id)) == NULL) { + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + windows_device_priv_init(dev); + } else { + usbi_dbg("found existing device for session [%X] (%d.%d)", + session_id, dev->bus_number, dev->device_address); + } + // Keep track of devices that need unref + unref_list[unref_cur++] = dev; + if (unref_cur >= unref_size) { + unref_size += 64; + unref_list = usbi_reallocf(unref_list, unref_size*sizeof(libusb_device*)); + if (unref_list == NULL) { + usbi_err(ctx, "could not realloc list for unref - aborting."); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + } + priv = _device_priv(dev); + } + + // Setup device + switch (pass) { + case HCD_PASS: + dev->bus_number = (uint8_t)(i + 1); // bus 0 is reserved for disconnected + dev->device_address = 0; + dev->num_configurations = 0; + priv->apib = &usb_api_backend[USB_API_HUB]; + priv->sub_api = SUB_API_NOTSET; + priv->depth = UINT8_MAX; // Overflow to 0 for HCD Hubs + priv->path = dev_interface_path; dev_interface_path = NULL; + break; + case HUB_PASS: + case DEV_PASS: + // If the device has already been setup, don't do it again + if (priv->path != NULL) + break; + // Take care of API initialization + priv->path = dev_interface_path; dev_interface_path = NULL; + priv->apib = &usb_api_backend[api]; + priv->sub_api = sub_api; + switch(api) { + case USB_API_COMPOSITE: + case USB_API_HUB: + break; + case USB_API_HID: + priv->hid = calloc(1, sizeof(struct hid_device_priv)); + if (priv->hid == NULL) { + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + priv->hid->nb_interfaces = 0; + break; + default: + // For other devices, the first interface is the same as the device + priv->usb_interface[0].path = (char*) calloc(safe_strlen(priv->path)+1, 1); + if (priv->usb_interface[0].path != NULL) { + safe_strcpy(priv->usb_interface[0].path, safe_strlen(priv->path)+1, priv->path); + } else { + usbi_warn(ctx, "could not duplicate interface path '%s'", priv->path); + } + // The following is needed if we want API calls to work for both simple + // and composite devices. + for(j=0; jusb_interface[j].apib = &usb_api_backend[api]; + } + break; + } + break; + case GEN_PASS: + r = init_device(dev, parent_dev, (uint8_t)port_nr, dev_id_path, dev_info_data.DevInst); + if (r == LIBUSB_SUCCESS) { + // Append device to the list of discovered devices + discdevs = discovered_devs_append(*_discdevs, dev); + if (!discdevs) { + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + *_discdevs = discdevs; + } else if (r == LIBUSB_ERROR_NO_DEVICE) { + // This can occur if the device was disconnected but Windows hasn't + // refreshed its enumeration yet - in that case, we ignore the device + r = LIBUSB_SUCCESS; + } + break; + default: // HID_PASS and later + if (parent_priv->apib->id == USB_API_HID) { + usbi_dbg("setting HID interface for [%lX]:", parent_dev->session_data); + r = set_hid_interface(ctx, parent_dev, dev_interface_path); + if (r != LIBUSB_SUCCESS) LOOP_BREAK(r); + dev_interface_path = NULL; + } else if (parent_priv->apib->id == USB_API_COMPOSITE) { + usbi_dbg("setting composite interface for [%lX]:", parent_dev->session_data); + switch (set_composite_interface(ctx, parent_dev, dev_interface_path, dev_id_path, api, sub_api)) { + case LIBUSB_SUCCESS: + dev_interface_path = NULL; + break; + case LIBUSB_ERROR_ACCESS: + // interface has already been set => make sure dev_interface_path is freed then + break; + default: + LOOP_BREAK(r); + break; + } + } + libusb_unref_device(parent_dev); + break; + } + } + } + + // Free any additional GUIDs + for (pass = HID_PASS+1; pass < nb_guids; pass++) { + safe_free(guid[pass]); + } + + // Unref newly allocated devs + if (unref_list != NULL) { + for (i=0; i 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 + for (i=0; idev_descriptor), DEVICE_DESC_LENGTH); + *host_endian = 0; + + return LIBUSB_SUCCESS; +} + +static int windows_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) +{ + struct windows_device_priv *priv = _device_priv(dev); + PUSB_CONFIGURATION_DESCRIPTOR config_header; + size_t size; + + // config index is zero based + if (config_index >= dev->num_configurations) + return LIBUSB_ERROR_INVALID_PARAM; + + if ((priv->config_descriptor == NULL) || (priv->config_descriptor[config_index] == NULL)) + return LIBUSB_ERROR_NOT_FOUND; + + config_header = (PUSB_CONFIGURATION_DESCRIPTOR)priv->config_descriptor[config_index]; + + size = min(config_header->wTotalLength, len); + memcpy(buffer, priv->config_descriptor[config_index], size); + *host_endian = 0; + + return (int)size; +} + +/* + * return the cached copy of the active config descriptor + */ +static int windows_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) +{ + struct windows_device_priv *priv = _device_priv(dev); + + if (priv->active_config == 0) + return LIBUSB_ERROR_NOT_FOUND; + + // config index is zero based + return windows_get_config_descriptor(dev, (uint8_t)(priv->active_config-1), buffer, len, host_endian); +} + +static int windows_open(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + + if (priv->apib == NULL) { + usbi_err(ctx, "program assertion failed - device is not initialized"); + return LIBUSB_ERROR_NO_DEVICE; + } + + return priv->apib->open(SUB_API_NOTSET, dev_handle); +} + +static void windows_close(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + priv->apib->close(SUB_API_NOTSET, dev_handle); +} + +static int windows_get_configuration(struct libusb_device_handle *dev_handle, int *config) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + if (priv->active_config == 0) { + *config = 0; + return LIBUSB_ERROR_NOT_FOUND; + } + + *config = priv->active_config; + return LIBUSB_SUCCESS; +} + +/* + * from http://msdn.microsoft.com/en-us/library/ms793522.aspx: "The port driver + * does not currently expose a service that allows higher-level drivers to set + * the configuration." + */ +static int windows_set_configuration(struct libusb_device_handle *dev_handle, int config) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int r = LIBUSB_SUCCESS; + + if (config >= USB_MAXCONFIG) + return LIBUSB_ERROR_INVALID_PARAM; + + r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_OUT | + LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE, + LIBUSB_REQUEST_SET_CONFIGURATION, (uint16_t)config, + 0, NULL, 0, 1000); + + if (r == LIBUSB_SUCCESS) { + priv->active_config = (uint8_t)config; + } + return r; +} + +static int windows_claim_interface(struct libusb_device_handle *dev_handle, int iface) +{ + int r = LIBUSB_SUCCESS; + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + if (iface >= USB_MAXINTERFACES) + return LIBUSB_ERROR_INVALID_PARAM; + + safe_free(priv->usb_interface[iface].endpoint); + priv->usb_interface[iface].nb_endpoints= 0; + + r = priv->apib->claim_interface(SUB_API_NOTSET, dev_handle, iface); + + if (r == LIBUSB_SUCCESS) { + r = windows_assign_endpoints(dev_handle, iface, 0); + } + + return r; +} + +static int windows_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + int r = LIBUSB_SUCCESS; + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + safe_free(priv->usb_interface[iface].endpoint); + priv->usb_interface[iface].nb_endpoints= 0; + + r = priv->apib->set_interface_altsetting(SUB_API_NOTSET, dev_handle, iface, altsetting); + + if (r == LIBUSB_SUCCESS) { + r = windows_assign_endpoints(dev_handle, iface, altsetting); + } + + return r; +} + +static int windows_release_interface(struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + return priv->apib->release_interface(SUB_API_NOTSET, dev_handle, iface); +} + +static int windows_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + return priv->apib->clear_halt(SUB_API_NOTSET, dev_handle, endpoint); +} + +static int windows_reset_device(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + return priv->apib->reset_device(SUB_API_NOTSET, dev_handle); +} + +// The 3 functions below are unlikely to ever get supported on Windows +static int windows_kernel_driver_active(struct libusb_device_handle *dev_handle, int iface) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int windows_attach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int windows_detach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static void windows_destroy_device(struct libusb_device *dev) +{ + windows_device_priv_release(dev); +} + +static void windows_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + + usbi_free_fd(&transfer_priv->pollable_fd); + safe_free(transfer_priv->hid_buffer); + // When auto claim is in use, attempt to release the auto-claimed interface + auto_release(itransfer); +} + +static int submit_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 windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int r; + + r = priv->apib->submit_bulk_transfer(SUB_API_NOTSET, itransfer); + if (r != LIBUSB_SUCCESS) { + return r; + } + + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, + (short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT)); + + itransfer->flags |= USBI_TRANSFER_UPDATED_FDS; + return LIBUSB_SUCCESS; +} + +static int submit_iso_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 windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int r; + + r = priv->apib->submit_iso_transfer(SUB_API_NOTSET, itransfer); + if (r != LIBUSB_SUCCESS) { + return r; + } + + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, + (short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT)); + + itransfer->flags |= USBI_TRANSFER_UPDATED_FDS; + return LIBUSB_SUCCESS; +} + +static int submit_control_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 windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int r; + + r = priv->apib->submit_control_transfer(SUB_API_NOTSET, itransfer); + if (r != LIBUSB_SUCCESS) { + return r; + } + + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN); + + itransfer->flags |= USBI_TRANSFER_UPDATED_FDS; + return LIBUSB_SUCCESS; + +} + +static int windows_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: + if (IS_XFEROUT(transfer) && + transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) + return LIBUSB_ERROR_NOT_SUPPORTED; + return submit_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return 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 int windows_abort_control(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + + return priv->apib->abort_control(SUB_API_NOTSET, itransfer); +} + +static int windows_abort_transfers(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + + return priv->apib->abort_transfers(SUB_API_NOTSET, itransfer); +} + +static int windows_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 windows_abort_control(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return windows_abort_transfers(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + return LIBUSB_ERROR_NOT_SUPPORTED; + default: + usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static void windows_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 windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int status, istatus; + + usbi_dbg("handling I/O completion with errcode %d, size %d", io_result, io_size); + + switch(io_result) { + case NO_ERROR: + status = priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, io_size); + break; + 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: + istatus = priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, io_size); + if (istatus != LIBUSB_TRANSFER_COMPLETED) { + usbi_dbg("Failed to copy partial data in aborted operation: %d", istatus); + } + 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 %d: %s", io_result, windows_error_str(io_result)); + status = LIBUSB_TRANSFER_ERROR; + break; + } + windows_clear_transfer_priv(itransfer); // Cancel polling + usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status); +} + +static void windows_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: + windows_transfer_callback (itransfer, io_result, io_size); + break; + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + usbi_warn(ITRANSFER_CTX(itransfer), "bulk stream transfers are not yet supported on this platform"); + break; + default: + usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); + } +} + +static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready) +{ + struct windows_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) { + // Handle async requests that completed synchronously first + if (HasOverlappedIoCompletedSync(transfer_priv->pollable_fd.overlapped)) { + io_result = NO_ERROR; + io_size = (DWORD)transfer_priv->pollable_fd.overlapped->InternalHigh; + // Regular async overlapped + } else if (GetOverlappedResult(transfer_priv->pollable_fd.handle, + transfer_priv->pollable_fd.overlapped, &io_size, false)) { + io_result = NO_ERROR; + } else { + io_result = GetLastError(); + } + 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. + windows_handle_callback(transfer, io_result, io_size); + } 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 windows_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 windows_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) && (hires_frequency != 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. + windows_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; + } + } +} + +static int windows_clock_gettime(int clk_id, struct timespec *tp) +{ + FILETIME filetime; + ULARGE_INTEGER rtime; + DWORD r; + 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) + GetSystemTimeAsFileTime(&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; + } +} + + +// NB: MSVC6 does not support named initializers. +const struct usbi_os_backend windows_backend = { + "Windows", + USBI_CAP_HAS_HID_ACCESS, + windows_init, + windows_exit, + + windows_get_device_list, + NULL, /* hotplug_poll */ + windows_open, + windows_close, + + windows_get_device_descriptor, + windows_get_active_config_descriptor, + windows_get_config_descriptor, + NULL, /* get_config_descriptor_by_value() */ + + windows_get_configuration, + windows_set_configuration, + windows_claim_interface, + windows_release_interface, + + windows_set_interface_altsetting, + windows_clear_halt, + windows_reset_device, + + NULL, /* alloc_streams */ + NULL, /* free_streams */ + + windows_kernel_driver_active, + windows_detach_kernel_driver, + windows_attach_kernel_driver, + + windows_destroy_device, + + windows_submit_transfer, + windows_cancel_transfer, + windows_clear_transfer_priv, + + windows_handle_events, + + windows_clock_gettime, +#if defined(USBI_TIMERFD_AVAILABLE) + NULL, +#endif + sizeof(struct windows_device_priv), + sizeof(struct windows_device_handle_priv), + sizeof(struct windows_transfer_priv), + 0, +}; + + +/* + * USB API backends + */ +static int unsupported_init(int sub_api, struct libusb_context *ctx) { + return LIBUSB_SUCCESS; +} +static int unsupported_exit(int sub_api) { + return LIBUSB_SUCCESS; +} +static int unsupported_open(int sub_api, struct libusb_device_handle *dev_handle) { + PRINT_UNSUPPORTED_API(open); +} +static void unsupported_close(int sub_api, struct libusb_device_handle *dev_handle) { + usbi_dbg("unsupported API call for 'close'"); +} +static int unsupported_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface) { + PRINT_UNSUPPORTED_API(configure_endpoints); +} +static int unsupported_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) { + PRINT_UNSUPPORTED_API(claim_interface); +} +static int unsupported_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting) { + PRINT_UNSUPPORTED_API(set_interface_altsetting); +} +static int unsupported_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) { + PRINT_UNSUPPORTED_API(release_interface); +} +static int unsupported_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) { + PRINT_UNSUPPORTED_API(clear_halt); +} +static int unsupported_reset_device(int sub_api, struct libusb_device_handle *dev_handle) { + PRINT_UNSUPPORTED_API(reset_device); +} +static int unsupported_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(submit_bulk_transfer); +} +static int unsupported_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(submit_iso_transfer); +} +static int unsupported_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(submit_control_transfer); +} +static int unsupported_abort_control(int sub_api, struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(abort_control); +} +static int unsupported_abort_transfers(int sub_api, struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(abort_transfers); +} +static int unsupported_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size) { + PRINT_UNSUPPORTED_API(copy_transfer_data); +} +static int common_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface) { + return LIBUSB_SUCCESS; +} +// These names must be uppercase +const char* hub_driver_names[] = {"USBHUB", "USBHUB3", "USB3HUB", "NUSB3HUB", "RUSB3HUB", "FLXHCIH", "TIHUB3", "ETRONHUB3", "VIAHUB3", "ASMTHUB3", "IUSB3HUB", "VUSB3HUB", "AMDHUB30"}; +const char* composite_driver_names[] = {"USBCCGP"}; +const char* winusbx_driver_names[] = WINUSBX_DRV_NAMES; +const char* hid_driver_names[] = {"HIDUSB", "MOUHID", "KBDHID"}; +const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { + { + USB_API_UNSUPPORTED, + "Unsupported API", + NULL, + 0, + unsupported_init, + unsupported_exit, + unsupported_open, + unsupported_close, + unsupported_configure_endpoints, + unsupported_claim_interface, + unsupported_set_interface_altsetting, + unsupported_release_interface, + unsupported_clear_halt, + unsupported_reset_device, + unsupported_submit_bulk_transfer, + unsupported_submit_iso_transfer, + unsupported_submit_control_transfer, + unsupported_abort_control, + unsupported_abort_transfers, + unsupported_copy_transfer_data, + }, { + USB_API_HUB, + "HUB API", + hub_driver_names, + ARRAYSIZE(hub_driver_names), + unsupported_init, + unsupported_exit, + unsupported_open, + unsupported_close, + unsupported_configure_endpoints, + unsupported_claim_interface, + unsupported_set_interface_altsetting, + unsupported_release_interface, + unsupported_clear_halt, + unsupported_reset_device, + unsupported_submit_bulk_transfer, + unsupported_submit_iso_transfer, + unsupported_submit_control_transfer, + unsupported_abort_control, + unsupported_abort_transfers, + unsupported_copy_transfer_data, + }, { + USB_API_COMPOSITE, + "Composite API", + composite_driver_names, + ARRAYSIZE(composite_driver_names), + composite_init, + composite_exit, + composite_open, + composite_close, + common_configure_endpoints, + composite_claim_interface, + composite_set_interface_altsetting, + composite_release_interface, + composite_clear_halt, + composite_reset_device, + composite_submit_bulk_transfer, + composite_submit_iso_transfer, + composite_submit_control_transfer, + composite_abort_control, + composite_abort_transfers, + composite_copy_transfer_data, + }, { + USB_API_WINUSBX, + "WinUSB-like APIs", + winusbx_driver_names, + ARRAYSIZE(winusbx_driver_names), + winusbx_init, + winusbx_exit, + winusbx_open, + winusbx_close, + winusbx_configure_endpoints, + winusbx_claim_interface, + winusbx_set_interface_altsetting, + winusbx_release_interface, + winusbx_clear_halt, + winusbx_reset_device, + winusbx_submit_bulk_transfer, + unsupported_submit_iso_transfer, + winusbx_submit_control_transfer, + winusbx_abort_control, + winusbx_abort_transfers, + winusbx_copy_transfer_data, + }, { + USB_API_HID, + "HID API", + hid_driver_names, + ARRAYSIZE(hid_driver_names), + hid_init, + hid_exit, + hid_open, + hid_close, + common_configure_endpoints, + hid_claim_interface, + hid_set_interface_altsetting, + hid_release_interface, + hid_clear_halt, + hid_reset_device, + hid_submit_bulk_transfer, + unsupported_submit_iso_transfer, + hid_submit_control_transfer, + hid_abort_transfers, + hid_abort_transfers, + hid_copy_transfer_data, + }, +}; + + +/* + * WinUSB-like (WinUSB, libusb0/libusbK through libusbk DLL) API functions + */ +#define WinUSBX_Set(fn) do { if (native_winusb) WinUSBX[i].fn = (WinUsb_##fn##_t) GetProcAddress(h, "WinUsb_" #fn); \ + else pLibK_GetProcAddress((PVOID*)&WinUSBX[i].fn, i, KUSB_FNID_##fn); } while (0) + +static int winusbx_init(int sub_api, struct libusb_context *ctx) +{ + HMODULE h = NULL; + bool native_winusb = false; + int i; + KLIB_VERSION LibK_Version; + LibK_GetProcAddress_t pLibK_GetProcAddress = NULL; + LibK_GetVersion_t pLibK_GetVersion = NULL; + + h = GetModuleHandleA("libusbK"); + if (h == NULL) { + h = LoadLibraryA("libusbK"); + } + if (h == NULL) { + usbi_info(ctx, "libusbK DLL is not available, will use native WinUSB"); + h = GetModuleHandleA("WinUSB"); + if (h == NULL) { + h = LoadLibraryA("WinUSB"); + } if (h == NULL) { + usbi_warn(ctx, "WinUSB DLL is not available either,\n" + "you will not be able to access devices outside of enumeration"); + return LIBUSB_ERROR_NOT_FOUND; + } + } else { + usbi_dbg("using libusbK DLL for universal access"); + pLibK_GetVersion = (LibK_GetVersion_t) GetProcAddress(h, "LibK_GetVersion"); + if (pLibK_GetVersion != NULL) { + pLibK_GetVersion(&LibK_Version); + usbi_dbg("libusbK version: %d.%d.%d.%d", LibK_Version.Major, LibK_Version.Minor, + LibK_Version.Micro, LibK_Version.Nano); + } + pLibK_GetProcAddress = (LibK_GetProcAddress_t) GetProcAddress(h, "LibK_GetProcAddress"); + if (pLibK_GetProcAddress == NULL) { + usbi_err(ctx, "LibK_GetProcAddress() not found in libusbK DLL"); + return LIBUSB_ERROR_NOT_FOUND; + } + } + native_winusb = (pLibK_GetProcAddress == NULL); + for (i=SUB_API_LIBUSBK; idev); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + + HANDLE file_handle; + int i; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // WinUSB requires a seperate handle for each interface + for (i = 0; i < USB_MAXINTERFACES; i++) { + if ( (priv->usb_interface[i].path != NULL) + && (priv->usb_interface[i].apib->id == USB_API_WINUSBX) ) { + file_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + if (file_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not open device %s (interface %d): %s", priv->usb_interface[i].path, i, windows_error_str(0)); + switch(GetLastError()) { + case ERROR_FILE_NOT_FOUND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ACCESS_DENIED: + return LIBUSB_ERROR_ACCESS; + default: + return LIBUSB_ERROR_IO; + } + } + handle_priv->interface_handle[i].dev_handle = file_handle; + } + } + + return LIBUSB_SUCCESS; +} + +static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE file_handle; + int i; + + if (sub_api == SUB_API_NOTSET) + sub_api = priv->sub_api; + if (!WinUSBX[sub_api].initialized) + return; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].apib->id == USB_API_WINUSBX) { + file_handle = handle_priv->interface_handle[i].dev_handle; + if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) { + CloseHandle(file_handle); + } + } + } +} + +static int winusbx_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE winusb_handle = handle_priv->interface_handle[iface].api_handle; + UCHAR policy; + ULONG timeout = 0; + uint8_t endpoint_address; + int i; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // With handle and enpoints set (in parent), we can setup the default pipe properties + // see http://download.microsoft.com/download/D/1/D/D1DD7745-426B-4CC3-A269-ABBBE427C0EF/DVC-T705_DDC08.pptx + for (i=-1; iusb_interface[iface].nb_endpoints; i++) { + endpoint_address =(i==-1)?0:priv->usb_interface[iface].endpoint[i]; + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout)) { + usbi_dbg("failed to set PIPE_TRANSFER_TIMEOUT for control endpoint %02X", endpoint_address); + } + if ((i == -1) || (sub_api == SUB_API_LIBUSB0)) { + continue; // Other policies don't apply to control endpoint or libusb0 + } + policy = false; + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + SHORT_PACKET_TERMINATE, sizeof(UCHAR), &policy)) { + usbi_dbg("failed to disable SHORT_PACKET_TERMINATE for endpoint %02X", endpoint_address); + } + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + IGNORE_SHORT_PACKETS, sizeof(UCHAR), &policy)) { + usbi_dbg("failed to disable IGNORE_SHORT_PACKETS for endpoint %02X", endpoint_address); + } + policy = true; + /* ALLOW_PARTIAL_READS must be enabled due to likely libusbK bug. See: + https://sourceforge.net/mailarchive/message.php?msg_id=29736015 */ + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + ALLOW_PARTIAL_READS, sizeof(UCHAR), &policy)) { + usbi_dbg("failed to enable ALLOW_PARTIAL_READS for endpoint %02X", endpoint_address); + } + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + AUTO_CLEAR_STALL, sizeof(UCHAR), &policy)) { + usbi_dbg("failed to enable AUTO_CLEAR_STALL for endpoint %02X", endpoint_address); + } + } + + return LIBUSB_SUCCESS; +} + +static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + bool is_using_usbccgp = (priv->apib->id == USB_API_COMPOSITE); + HANDLE file_handle, winusb_handle; + DWORD err; + int i; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL; + HDEVINFO dev_info = INVALID_HANDLE_VALUE; + SP_DEVINFO_DATA dev_info_data; + char* dev_path_no_guid = NULL; + char filter_path[] = "\\\\.\\libusb0-0000"; + bool found_filter = false; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // If the device is composite, but using the default Windows composite parent driver (usbccgp) + // or if it's the first WinUSB-like interface, we get a handle through Initialize(). + if ((is_using_usbccgp) || (iface == 0)) { + // composite device (independent interfaces) or interface 0 + file_handle = handle_priv->interface_handle[iface].dev_handle; + if ((file_handle == 0) || (file_handle == INVALID_HANDLE_VALUE)) { + return LIBUSB_ERROR_NOT_FOUND; + } + + if (!WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) { + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + err = GetLastError(); + switch(err) { + case ERROR_BAD_COMMAND: + // The device was disconnected + usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + default: + // it may be that we're using the libusb0 filter driver. + // TODO: can we move this whole business into the K/0 DLL? + for (i = 0; ; i++) { + safe_free(dev_interface_details); + safe_free(dev_path_no_guid); + dev_interface_details = get_interface_details_filter(ctx, &dev_info, &dev_info_data, &GUID_DEVINTERFACE_LIBUSB0_FILTER, i, filter_path); + if ((found_filter) || (dev_interface_details == NULL)) { + break; + } + // ignore GUID part + dev_path_no_guid = sanitize_path(strtok(dev_interface_details->DevicePath, "{")); + if (safe_strncmp(dev_path_no_guid, priv->usb_interface[iface].path, safe_strlen(dev_path_no_guid)) == 0) { + file_handle = CreateFileA(filter_path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + if (file_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not open device %s: %s", filter_path, windows_error_str(0)); + } else { + WinUSBX[sub_api].Free(winusb_handle); + if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) + found_filter = true; + else + usbi_err(ctx, "could not initialize filter driver for %s", filter_path); + } + } + } + if (!found_filter) { + usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(err)); + return LIBUSB_ERROR_ACCESS; + } + } + } + handle_priv->interface_handle[iface].api_handle = winusb_handle; + } else { + // For all other interfaces, use GetAssociatedInterface() + winusb_handle = handle_priv->interface_handle[0].api_handle; + // It is a requirement for multiple interface devices on Windows that, to you + // must first claim the first interface before you claim the others + if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) { + file_handle = handle_priv->interface_handle[0].dev_handle; + if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) { + handle_priv->interface_handle[0].api_handle = winusb_handle; + usbi_warn(ctx, "auto-claimed interface 0 (required to claim %d with WinUSB)", iface); + } else { + usbi_warn(ctx, "failed to auto-claim interface 0 (required to claim %d with WinUSB): %s", iface, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + } + if (!WinUSBX[sub_api].GetAssociatedInterface(winusb_handle, (UCHAR)(iface-1), + &handle_priv->interface_handle[iface].api_handle)) { + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + switch(GetLastError()) { + case ERROR_NO_MORE_ITEMS: // invalid iface + return LIBUSB_ERROR_NOT_FOUND; + case ERROR_BAD_COMMAND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ALREADY_EXISTS: // already claimed + return LIBUSB_ERROR_BUSY; + default: + usbi_err(ctx, "could not claim interface %d: %s", iface, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + } + } + usbi_dbg("claimed interface %d", iface); + handle_priv->active_interface = iface; + + return LIBUSB_SUCCESS; +} + +static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE winusb_handle; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + winusb_handle = handle_priv->interface_handle[iface].api_handle; + if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) { + return LIBUSB_ERROR_NOT_FOUND; + } + + WinUSBX[sub_api].Free(winusb_handle); + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + + return LIBUSB_SUCCESS; +} + +/* + * Return the first valid interface (of the same API type), for control transfers + */ +static int get_valid_interface(struct libusb_device_handle *dev_handle, int api_id) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int i; + + if ((api_id < USB_API_WINUSBX) || (api_id > USB_API_HID)) { + usbi_dbg("unsupported API ID"); + return -1; + } + + for (i=0; iinterface_handle[i].dev_handle != 0) + && (handle_priv->interface_handle[i].dev_handle != INVALID_HANDLE_VALUE) + && (handle_priv->interface_handle[i].api_handle != 0) + && (handle_priv->interface_handle[i].api_handle != INVALID_HANDLE_VALUE) + && (priv->usb_interface[i].apib->id == api_id) ) { + return i; + } + } + return -1; +} + +/* + * Lookup interface by endpoint address. -1 if not found + */ +static int interface_by_endpoint(struct windows_device_priv *priv, + struct windows_device_handle_priv *handle_priv, uint8_t endpoint_address) +{ + int i, j; + for (i=0; iinterface_handle[i].api_handle == INVALID_HANDLE_VALUE) + continue; + if (handle_priv->interface_handle[i].api_handle == 0) + continue; + if (priv->usb_interface[i].endpoint == NULL) + continue; + for (j=0; jusb_interface[i].nb_endpoints; j++) { + if (priv->usb_interface[i].endpoint[j] == endpoint_address) { + return i; + } + } + } + return -1; +} + +static int winusbx_submit_control_transfer(int sub_api, 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 windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = _device_handle_priv( + transfer->dev_handle); + WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *) transfer->buffer; + ULONG size; + HANDLE winusb_handle; + int current_interface; + struct winfd wfd; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + transfer_priv->pollable_fd = INVALID_WINFD; + size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + if (size > MAX_CTRL_BUFFER_LENGTH) + return LIBUSB_ERROR_INVALID_PARAM; + + current_interface = get_valid_interface(transfer->dev_handle, USB_API_WINUSBX); + if (current_interface < 0) { + if (auto_claim(transfer, ¤t_interface, USB_API_WINUSBX) != LIBUSB_SUCCESS) { + return LIBUSB_ERROR_NOT_FOUND; + } + } + + usbi_dbg("will use interface %d", current_interface); + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + wfd = usbi_create_fd(winusb_handle, RW_READ, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) { + return LIBUSB_ERROR_NO_MEM; + } + + // Sending of set configuration control requests from WinUSB creates issues + if ( ((setup->request_type & (0x03 << 5)) == LIBUSB_REQUEST_TYPE_STANDARD) + && (setup->request == LIBUSB_REQUEST_SET_CONFIGURATION) ) { + if (setup->value != priv->active_config) { + usbi_warn(ctx, "cannot set configuration other than the default one"); + usbi_free_fd(&wfd); + return LIBUSB_ERROR_INVALID_PARAM; + } + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = 0; + } else { + ULONG timeout = 0; + WinUSBX[sub_api].SetPipePolicy(wfd.handle, 0, PIPE_TRANSFER_TIMEOUT, + sizeof(ULONG), &timeout); + if (!WinUSBX[sub_api].ControlTransfer(wfd.handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, wfd.overlapped)) { + if(GetLastError() != ERROR_IO_PENDING) { + usbi_warn(ctx, "ControlTransfer failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + return LIBUSB_ERROR_IO; + } + } else { + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = (DWORD)size; + } + } + + // Use priv_transfer to store data needed for async polling + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + + return LIBUSB_SUCCESS; +} + +static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE winusb_handle; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + if (altsetting > 255) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + winusb_handle = handle_priv->interface_handle[iface].api_handle; + if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) { + usbi_err(ctx, "interface must be claimed first"); + return LIBUSB_ERROR_NOT_FOUND; + } + + if (!WinUSBX[sub_api].SetCurrentAlternateSetting(winusb_handle, (UCHAR)altsetting)) { + usbi_err(ctx, "SetCurrentAlternateSetting failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + + return LIBUSB_SUCCESS; +} + +static int winusbx_submit_bulk_transfer(int sub_api, 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 windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + HANDLE winusb_handle; + bool ret; + int current_interface; + struct winfd wfd; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + transfer_priv->pollable_fd = INVALID_WINFD; + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface); + + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + wfd = usbi_create_fd(winusb_handle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) { + return LIBUSB_ERROR_NO_MEM; + } + + if (IS_XFERIN(transfer)) { + usbi_dbg("reading %d bytes", transfer->length); + ret = WinUSBX[sub_api].ReadPipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped); + } else { + usbi_dbg("writing %d bytes", transfer->length); + ret = WinUSBX[sub_api].WritePipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped); + } + if (!ret) { + if(GetLastError() != ERROR_IO_PENDING) { + usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + return LIBUSB_ERROR_IO; + } + } else { + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = (DWORD)transfer->length; + } + + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + + return LIBUSB_SUCCESS; +} + +static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE winusb_handle; + int current_interface; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface); + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + if (!WinUSBX[sub_api].ResetPipe(winusb_handle, endpoint)) { + usbi_err(ctx, "ResetPipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +/* + * from http://www.winvistatips.com/winusb-bugchecks-t335323.html (confirmed + * through testing as well): + * "You can not call WinUsb_AbortPipe on control pipe. You can possibly cancel + * the control transfer using CancelIo" + */ +static int winusbx_abort_control(int sub_api, struct usbi_transfer *itransfer) +{ + // Cancelling of the I/O is done in the parent + return LIBUSB_SUCCESS; +} + +static int winusbx_abort_transfers(int sub_api, 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 windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + HANDLE winusb_handle; + int current_interface; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + current_interface = transfer_priv->interface_number; + if ((current_interface < 0) || (current_interface >= USB_MAXINTERFACES)) { + usbi_err(ctx, "program assertion failed: invalid interface_number"); + return LIBUSB_ERROR_NOT_FOUND; + } + usbi_dbg("will use interface %d", current_interface); + + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + if (!WinUSBX[sub_api].AbortPipe(winusb_handle, transfer->endpoint)) { + usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0)); + + + libusb_lock_events(ctx); + usbi_mutex_lock(&ctx->open_devs_lock); + usbi_mutex_lock(&ctx->flying_transfers_lock); + + + + usbi_remove_pollfd(ctx, transfer_priv->pollable_fd.fd); + list_del(&itransfer->list); + + + usbi_mutex_unlock(&ctx->flying_transfers_lock); + usbi_mutex_unlock(&ctx->open_devs_lock); + + libusb_unlock_events(ctx); + + + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +/* + * from the "How to Use WinUSB to Communicate with a USB Device" Microsoft white paper + * (http://www.microsoft.com/whdc/connect/usb/winusb_howto.mspx): + * "WinUSB does not support host-initiated reset port and cycle port operations" and + * IOCTL_INTERNAL_USB_CYCLE_PORT is only available in kernel mode and the + * IOCTL_USB_HUB_CYCLE_PORT ioctl was removed from Vista => the best we can do is + * cycle the pipes (and even then, the control pipe can not be reset using WinUSB) + */ +// TODO: (post hotplug): see if we can force eject the device and redetect it (reuse hotplug?) +static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct winfd wfd; + HANDLE winusb_handle; + int i, j; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // Reset any available pipe (except control) + for (i=0; iinterface_handle[i].api_handle; + for (wfd = handle_to_winfd(winusb_handle); wfd.fd > 0;) + { + // Cancel any pollable I/O + usbi_remove_pollfd(ctx, wfd.fd); + usbi_free_fd(&wfd); + wfd = handle_to_winfd(winusb_handle); + } + + if ( (winusb_handle != 0) && (winusb_handle != INVALID_HANDLE_VALUE)) { + for (j=0; jusb_interface[i].nb_endpoints; j++) { + usbi_dbg("resetting ep %02X", priv->usb_interface[i].endpoint[j]); + if (!WinUSBX[sub_api].AbortPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) { + usbi_err(ctx, "AbortPipe (pipe address %02X) failed: %s", + priv->usb_interface[i].endpoint[j], windows_error_str(0)); + } + // FlushPipe seems to fail on OUT pipes + if (IS_EPIN(priv->usb_interface[i].endpoint[j]) + && (!WinUSBX[sub_api].FlushPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) ) { + usbi_err(ctx, "FlushPipe (pipe address %02X) failed: %s", + priv->usb_interface[i].endpoint[j], windows_error_str(0)); + } + if (!WinUSBX[sub_api].ResetPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) { + usbi_err(ctx, "ResetPipe (pipe address %02X) failed: %s", + priv->usb_interface[i].endpoint[j], windows_error_str(0)); + } + } + } + } + + // libusbK & libusb0 have the ability to issue an actual device reset + if (WinUSBX[sub_api].ResetDevice != NULL) { + winusb_handle = handle_priv->interface_handle[0].api_handle; + if ( (winusb_handle != 0) && (winusb_handle != INVALID_HANDLE_VALUE)) { + WinUSBX[sub_api].ResetDevice(winusb_handle); + } + } + return LIBUSB_SUCCESS; +} + +static int winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size) +{ + itransfer->transferred += io_size; + return LIBUSB_TRANSFER_COMPLETED; +} + +/* + * Internal HID Support functions (from libusb-win32) + * Note that functions that complete data transfer synchronously must return + * LIBUSB_COMPLETED instead of LIBUSB_SUCCESS + */ +static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size); +static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size); + +static int _hid_wcslen(WCHAR *str) +{ + int i = 0; + while (str[i] && (str[i] != 0x409)) { + i++; + } + return i; +} + +static int _hid_get_device_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + struct libusb_device_descriptor d; + + d.bLength = LIBUSB_DT_DEVICE_SIZE; + d.bDescriptorType = LIBUSB_DT_DEVICE; + d.bcdUSB = 0x0200; /* 2.00 */ + d.bDeviceClass = 0; + d.bDeviceSubClass = 0; + d.bDeviceProtocol = 0; + d.bMaxPacketSize0 = 64; /* fix this! */ + d.idVendor = (uint16_t)dev->vid; + d.idProduct = (uint16_t)dev->pid; + d.bcdDevice = 0x0100; + d.iManufacturer = dev->string_index[0]; + d.iProduct = dev->string_index[1]; + d.iSerialNumber = dev->string_index[2]; + d.bNumConfigurations = 1; + + if (*size > LIBUSB_DT_DEVICE_SIZE) + *size = LIBUSB_DT_DEVICE_SIZE; + memcpy(data, &d, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_config_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + char num_endpoints = 0; + size_t config_total_len = 0; + char tmp[HID_MAX_CONFIG_DESC_SIZE]; + struct libusb_config_descriptor *cd; + struct libusb_interface_descriptor *id; + struct libusb_hid_descriptor *hd; + struct libusb_endpoint_descriptor *ed; + size_t tmp_size; + + if (dev->input_report_size) + num_endpoints++; + if (dev->output_report_size) + num_endpoints++; + + config_total_len = LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE + + LIBUSB_DT_HID_SIZE + num_endpoints * LIBUSB_DT_ENDPOINT_SIZE; + + + cd = (struct libusb_config_descriptor *)tmp; + id = (struct libusb_interface_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE); + hd = (struct libusb_hid_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE + + LIBUSB_DT_INTERFACE_SIZE); + ed = (struct libusb_endpoint_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE + + LIBUSB_DT_INTERFACE_SIZE + + LIBUSB_DT_HID_SIZE); + + cd->bLength = LIBUSB_DT_CONFIG_SIZE; + cd->bDescriptorType = LIBUSB_DT_CONFIG; + cd->wTotalLength = (uint16_t) config_total_len; + cd->bNumInterfaces = 1; + cd->bConfigurationValue = 1; + cd->iConfiguration = 0; + cd->bmAttributes = 1 << 7; /* bus powered */ + cd->MaxPower = 50; + + id->bLength = LIBUSB_DT_INTERFACE_SIZE; + id->bDescriptorType = LIBUSB_DT_INTERFACE; + id->bInterfaceNumber = 0; + id->bAlternateSetting = 0; + id->bNumEndpoints = num_endpoints; + id->bInterfaceClass = 3; + id->bInterfaceSubClass = 0; + id->bInterfaceProtocol = 0; + id->iInterface = 0; + + tmp_size = LIBUSB_DT_HID_SIZE; + _hid_get_hid_descriptor(dev, hd, &tmp_size); + + if (dev->input_report_size) { + ed->bLength = LIBUSB_DT_ENDPOINT_SIZE; + ed->bDescriptorType = LIBUSB_DT_ENDPOINT; + ed->bEndpointAddress = HID_IN_EP; + ed->bmAttributes = 3; + ed->wMaxPacketSize = dev->input_report_size - 1; + ed->bInterval = 10; + ed = (struct libusb_endpoint_descriptor *)((char*)ed + LIBUSB_DT_ENDPOINT_SIZE); + } + + if (dev->output_report_size) { + ed->bLength = LIBUSB_DT_ENDPOINT_SIZE; + ed->bDescriptorType = LIBUSB_DT_ENDPOINT; + ed->bEndpointAddress = HID_OUT_EP; + ed->bmAttributes = 3; + ed->wMaxPacketSize = dev->output_report_size - 1; + ed->bInterval = 10; + } + + if (*size > config_total_len) + *size = config_total_len; + memcpy(data, tmp, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_string_descriptor(struct hid_device_priv* dev, int _index, + void *data, size_t *size) +{ + void *tmp = NULL; + size_t tmp_size = 0; + int i; + + /* language ID, EN-US */ + char string_langid[] = { + 0x09, + 0x04 + }; + + if ((*size < 2) || (*size > 255)) { + return LIBUSB_ERROR_OVERFLOW; + } + + if (_index == 0) { + tmp = string_langid; + tmp_size = sizeof(string_langid)+2; + } else { + for (i=0; i<3; i++) { + if (_index == (dev->string_index[i])) { + tmp = dev->string[i]; + tmp_size = (_hid_wcslen(dev->string[i])+1) * sizeof(WCHAR); + break; + } + } + if (i == 3) { // not found + return LIBUSB_ERROR_INVALID_PARAM; + } + } + + if(!tmp_size) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (tmp_size < *size) { + *size = tmp_size; + } + // 2 byte header + ((uint8_t*)data)[0] = (uint8_t)*size; + ((uint8_t*)data)[1] = LIBUSB_DT_STRING; + memcpy((uint8_t*)data+2, tmp, *size-2); + return LIBUSB_COMPLETED; +} + +static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + struct libusb_hid_descriptor d; + uint8_t tmp[MAX_HID_DESCRIPTOR_SIZE]; + size_t report_len = MAX_HID_DESCRIPTOR_SIZE; + + _hid_get_report_descriptor(dev, tmp, &report_len); + + d.bLength = LIBUSB_DT_HID_SIZE; + d.bDescriptorType = LIBUSB_DT_HID; + d.bcdHID = 0x0110; /* 1.10 */ + d.bCountryCode = 0; + d.bNumDescriptors = 1; + d.bClassDescriptorType = LIBUSB_DT_REPORT; + d.wClassDescriptorLength = (uint16_t)report_len; + + if (*size > LIBUSB_DT_HID_SIZE) + *size = LIBUSB_DT_HID_SIZE; + memcpy(data, &d, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + uint8_t d[MAX_HID_DESCRIPTOR_SIZE]; + size_t i = 0; + + /* usage page (0xFFA0 == vendor defined) */ + d[i++] = 0x06; d[i++] = 0xA0; d[i++] = 0xFF; + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x01; + /* start collection (application) */ + d[i++] = 0xA1; d[i++] = 0x01; + /* input report */ + if (dev->input_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x01; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->input_report_size - 1; + /* input (data, variable, absolute) */ + d[i++] = 0x81; d[i++] = 0x00; + } + /* output report */ + if (dev->output_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x02; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->output_report_size - 1; + /* output (data, variable, absolute) */ + d[i++] = 0x91; d[i++] = 0x00; + } + /* feature report */ + if (dev->feature_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x03; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->feature_report_size - 1; + /* feature (data, variable, absolute) */ + d[i++] = 0xb2; d[i++] = 0x02; d[i++] = 0x01; + } + + /* end collection */ + d[i++] = 0xC0; + + if (*size > i) + *size = i; + memcpy(data, d, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_descriptor(struct hid_device_priv* dev, HANDLE hid_handle, int recipient, + int type, int _index, void *data, size_t *size) +{ + switch(type) { + case LIBUSB_DT_DEVICE: + usbi_dbg("LIBUSB_DT_DEVICE"); + return _hid_get_device_descriptor(dev, data, size); + case LIBUSB_DT_CONFIG: + usbi_dbg("LIBUSB_DT_CONFIG"); + if (!_index) + return _hid_get_config_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_STRING: + usbi_dbg("LIBUSB_DT_STRING"); + return _hid_get_string_descriptor(dev, _index, data, size); + case LIBUSB_DT_HID: + usbi_dbg("LIBUSB_DT_HID"); + if (!_index) + return _hid_get_hid_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_REPORT: + usbi_dbg("LIBUSB_DT_REPORT"); + if (!_index) + return _hid_get_report_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_PHYSICAL: + usbi_dbg("LIBUSB_DT_PHYSICAL"); + if (HidD_GetPhysicalDescriptor(hid_handle, data, (ULONG)*size)) + return LIBUSB_COMPLETED; + return LIBUSB_ERROR_OTHER; + } + usbi_dbg("unsupported"); + return LIBUSB_ERROR_INVALID_PARAM; +} + +static int _hid_get_report(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data, + struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped, + int report_type) +{ + uint8_t *buf; + DWORD ioctl_code, read_size, expected_size = (DWORD)*size; + int r = LIBUSB_SUCCESS; + + if (tp->hid_buffer != NULL) { + usbi_dbg("program assertion failed: hid_buffer is not NULL"); + } + + if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) { + usbi_dbg("invalid size (%d)", *size); + return LIBUSB_ERROR_INVALID_PARAM; + } + + switch (report_type) { + case HID_REPORT_TYPE_INPUT: + ioctl_code = IOCTL_HID_GET_INPUT_REPORT; + break; + case HID_REPORT_TYPE_FEATURE: + ioctl_code = IOCTL_HID_GET_FEATURE; + break; + default: + usbi_dbg("unknown HID report type %d", report_type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + // Add a trailing byte to detect overflows + buf = (uint8_t*)calloc(expected_size+1, 1); + if (buf == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + buf[0] = (uint8_t)id; // Must be set always + usbi_dbg("report ID: 0x%02X", buf[0]); + + tp->hid_expected_size = expected_size; + read_size = expected_size; + + // NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0) + if (!DeviceIoControl(hid_handle, ioctl_code, buf, expected_size+1, + buf, expected_size+1, &read_size, overlapped)) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_dbg("Failed to Read HID Report: %s", windows_error_str(0)); + safe_free(buf); + return LIBUSB_ERROR_IO; + } + // Asynchronous wait + tp->hid_buffer = buf; + tp->hid_dest = (uint8_t*)data; // copy dest, as not necessarily the start of the transfer buffer + return LIBUSB_SUCCESS; + } + + // Transfer completed synchronously => copy and discard extra buffer + if (read_size == 0) { + usbi_warn(NULL, "program assertion failed - read completed synchronously, but no data was read"); + *size = 0; + } else { + if (buf[0] != id) { + usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id); + } + if ((size_t)read_size > expected_size) { + r = LIBUSB_ERROR_OVERFLOW; + usbi_dbg("OVERFLOW!"); + } else { + r = LIBUSB_COMPLETED; + } + + *size = MIN((size_t)read_size, *size); + if (id == 0) { + // Discard report ID + memcpy(data, buf+1, *size); + } else { + memcpy(data, buf, *size); + } + } + safe_free(buf); + return r; +} + +static int _hid_set_report(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data, + struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped, + int report_type) +{ + uint8_t *buf = NULL; + DWORD ioctl_code, write_size= (DWORD)*size; + + if (tp->hid_buffer != NULL) { + usbi_dbg("program assertion failed: hid_buffer is not NULL"); + } + + if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) { + usbi_dbg("invalid size (%d)", *size); + return LIBUSB_ERROR_INVALID_PARAM; + } + + switch (report_type) { + case HID_REPORT_TYPE_OUTPUT: + ioctl_code = IOCTL_HID_SET_OUTPUT_REPORT; + break; + case HID_REPORT_TYPE_FEATURE: + ioctl_code = IOCTL_HID_SET_FEATURE; + break; + default: + usbi_dbg("unknown HID report type %d", report_type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + usbi_dbg("report ID: 0x%02X", id); + // When report IDs are not used (i.e. when id == 0), we must add + // a null report ID. Otherwise, we just use original data buffer + if (id == 0) { + write_size++; + } + buf = (uint8_t*) malloc(write_size); + if (buf == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + if (id == 0) { + buf[0] = 0; + memcpy(buf + 1, data, *size); + } else { + // This seems like a waste, but if we don't duplicate the + // data, we'll get issues when freeing hid_buffer + memcpy(buf, data, *size); + if (buf[0] != id) { + usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id); + } + } + + // NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0) + if (!DeviceIoControl(hid_handle, ioctl_code, buf, write_size, + buf, write_size, &write_size, overlapped)) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_dbg("Failed to Write HID Output Report: %s", windows_error_str(0)); + safe_free(buf); + return LIBUSB_ERROR_IO; + } + tp->hid_buffer = buf; + tp->hid_dest = NULL; + return LIBUSB_SUCCESS; + } + + // Transfer completed synchronously + *size = write_size; + if (write_size == 0) { + usbi_dbg("program assertion failed - write completed synchronously, but no data was written"); + } + safe_free(buf); + return LIBUSB_COMPLETED; +} + +static int _hid_class_request(struct hid_device_priv* dev, HANDLE hid_handle, int request_type, + int request, int value, int _index, void *data, struct windows_transfer_priv *tp, + size_t *size, OVERLAPPED* overlapped) +{ + int report_type = (value >> 8) & 0xFF; + int report_id = value & 0xFF; + + if ( (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_INTERFACE) + && (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_DEVICE) ) + return LIBUSB_ERROR_INVALID_PARAM; + + if (LIBUSB_REQ_OUT(request_type) && request == HID_REQ_SET_REPORT) + return _hid_set_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type); + + if (LIBUSB_REQ_IN(request_type) && request == HID_REQ_GET_REPORT) + return _hid_get_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type); + + return LIBUSB_ERROR_INVALID_PARAM; +} + + +/* + * HID API functions + */ +static int hid_init(int sub_api, struct libusb_context *ctx) +{ + DLL_LOAD(hid.dll, HidD_GetAttributes, TRUE); + DLL_LOAD(hid.dll, HidD_GetHidGuid, TRUE); + DLL_LOAD(hid.dll, HidD_GetPreparsedData, TRUE); + DLL_LOAD(hid.dll, HidD_FreePreparsedData, TRUE); + DLL_LOAD(hid.dll, HidD_GetManufacturerString, TRUE); + DLL_LOAD(hid.dll, HidD_GetProductString, TRUE); + DLL_LOAD(hid.dll, HidD_GetSerialNumberString, TRUE); + DLL_LOAD(hid.dll, HidP_GetCaps, TRUE); + DLL_LOAD(hid.dll, HidD_SetNumInputBuffers, TRUE); + DLL_LOAD(hid.dll, HidD_SetFeature, TRUE); + DLL_LOAD(hid.dll, HidD_GetFeature, TRUE); + DLL_LOAD(hid.dll, HidD_GetPhysicalDescriptor, TRUE); + DLL_LOAD(hid.dll, HidD_GetInputReport, FALSE); + DLL_LOAD(hid.dll, HidD_SetOutputReport, FALSE); + DLL_LOAD(hid.dll, HidD_FlushQueue, TRUE); + DLL_LOAD(hid.dll, HidP_GetValueCaps, TRUE); + + api_hid_available = true; + return LIBUSB_SUCCESS; +} + +static int hid_exit(int sub_api) +{ + return LIBUSB_SUCCESS; +} + +// NB: open and close must ensure that they only handle interface of +// the right API type, as these functions can be called wholesale from +// composite_open(), with interfaces belonging to different APIs +static int hid_open(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + + HIDD_ATTRIBUTES hid_attributes; + PHIDP_PREPARSED_DATA preparsed_data = NULL; + HIDP_CAPS capabilities; + HIDP_VALUE_CAPS *value_caps; + + HANDLE hid_handle = INVALID_HANDLE_VALUE; + int i, j; + // report IDs handling + ULONG size[3]; + const char* type[3] = {"input", "output", "feature"}; + int nb_ids[2]; // zero and nonzero report IDs + + CHECK_HID_AVAILABLE; + if (priv->hid == NULL) { + usbi_err(ctx, "program assertion failed - private HID structure is unitialized"); + return LIBUSB_ERROR_NOT_FOUND; + } + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if ( (priv->usb_interface[i].path != NULL) + && (priv->usb_interface[i].apib->id == USB_API_HID) ) { + hid_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + /* + * http://www.lvr.com/hidfaq.htm: Why do I receive "Access denied" when attempting to access my HID? + * "Windows 2000 and later have exclusive read/write access to HIDs that are configured as a system + * keyboards or mice. An application can obtain a handle to a system keyboard or mouse by not + * requesting READ or WRITE access with CreateFile. Applications can then use HidD_SetFeature and + * HidD_GetFeature (if the device supports Feature reports)." + */ + if (hid_handle == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "could not open HID device in R/W mode (keyboard or mouse?) - trying without"); + hid_handle = CreateFileA(priv->usb_interface[i].path, 0, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + if (hid_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not open device %s (interface %d): %s", priv->path, i, windows_error_str(0)); + switch(GetLastError()) { + case ERROR_FILE_NOT_FOUND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ACCESS_DENIED: + return LIBUSB_ERROR_ACCESS; + default: + return LIBUSB_ERROR_IO; + } + } + priv->usb_interface[i].restricted_functionality = true; + } + handle_priv->interface_handle[i].api_handle = hid_handle; + } + } + + hid_attributes.Size = sizeof(hid_attributes); + do { + if (!HidD_GetAttributes(hid_handle, &hid_attributes)) { + usbi_err(ctx, "could not gain access to HID top collection (HidD_GetAttributes)"); + break; + } + + priv->hid->vid = hid_attributes.VendorID; + priv->hid->pid = hid_attributes.ProductID; + + // Set the maximum available input buffer size + for (i=32; HidD_SetNumInputBuffers(hid_handle, i); i*=2); + usbi_dbg("set maximum input buffer size to %d", i/2); + + // Get the maximum input and output report size + if (!HidD_GetPreparsedData(hid_handle, &preparsed_data) || !preparsed_data) { + usbi_err(ctx, "could not read HID preparsed data (HidD_GetPreparsedData)"); + break; + } + if (HidP_GetCaps(preparsed_data, &capabilities) != HIDP_STATUS_SUCCESS) { + usbi_err(ctx, "could not parse HID capabilities (HidP_GetCaps)"); + break; + } + + // Find out if interrupt will need report IDs + size[0] = capabilities.NumberInputValueCaps; + size[1] = capabilities.NumberOutputValueCaps; + size[2] = capabilities.NumberFeatureValueCaps; + for (j=HidP_Input; j<=HidP_Feature; j++) { + usbi_dbg("%d HID %s report value(s) found", size[j], type[j]); + priv->hid->uses_report_ids[j] = false; + if (size[j] > 0) { + value_caps = (HIDP_VALUE_CAPS*) calloc(size[j], sizeof(HIDP_VALUE_CAPS)); + if ( (value_caps != NULL) + && (HidP_GetValueCaps((HIDP_REPORT_TYPE)j, value_caps, &size[j], preparsed_data) == HIDP_STATUS_SUCCESS) + && (size[j] >= 1) ) { + nb_ids[0] = 0; + nb_ids[1] = 0; + for (i=0; i<(int)size[j]; i++) { + usbi_dbg(" Report ID: 0x%02X", value_caps[i].ReportID); + if (value_caps[i].ReportID != 0) { + nb_ids[1]++; + } else { + nb_ids[0]++; + } + } + if (nb_ids[1] != 0) { + if (nb_ids[0] != 0) { + usbi_warn(ctx, "program assertion failed: zero and nonzero report IDs used for %s", + type[j]); + } + priv->hid->uses_report_ids[j] = true; + } + } else { + usbi_warn(ctx, " could not process %s report IDs", type[j]); + } + safe_free(value_caps); + } + } + + // Set the report sizes + priv->hid->input_report_size = capabilities.InputReportByteLength; + priv->hid->output_report_size = capabilities.OutputReportByteLength; + priv->hid->feature_report_size = capabilities.FeatureReportByteLength; + + // Fetch string descriptors + priv->hid->string_index[0] = priv->dev_descriptor.iManufacturer; + if (priv->hid->string_index[0] != 0) { + HidD_GetManufacturerString(hid_handle, priv->hid->string[0], + sizeof(priv->hid->string[0])); + } else { + priv->hid->string[0][0] = 0; + } + priv->hid->string_index[1] = priv->dev_descriptor.iProduct; + if (priv->hid->string_index[1] != 0) { + HidD_GetProductString(hid_handle, priv->hid->string[1], + sizeof(priv->hid->string[1])); + } else { + priv->hid->string[1][0] = 0; + } + priv->hid->string_index[2] = priv->dev_descriptor.iSerialNumber; + if (priv->hid->string_index[2] != 0) { + HidD_GetSerialNumberString(hid_handle, priv->hid->string[2], + sizeof(priv->hid->string[2])); + } else { + priv->hid->string[2][0] = 0; + } + } while(0); + + if (preparsed_data) { + HidD_FreePreparsedData(preparsed_data); + } + + return LIBUSB_SUCCESS; +} + +static void hid_close(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + HANDLE file_handle; + int i; + + if (!api_hid_available) + return; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].apib->id == USB_API_HID) { + file_handle = handle_priv->interface_handle[i].api_handle; + if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) { + CloseHandle(file_handle); + } + } + } +} + +static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + // NB: Disconnection detection is not possible in this function + if (priv->usb_interface[iface].path == NULL) { + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + } + + // We use dev_handle as a flag for interface claimed + if (handle_priv->interface_handle[iface].dev_handle == INTERFACE_CLAIMED) { + return LIBUSB_ERROR_BUSY; // already claimed + } + + handle_priv->interface_handle[iface].dev_handle = INTERFACE_CLAIMED; + + usbi_dbg("claimed interface %d", iface); + handle_priv->active_interface = iface; + + return LIBUSB_SUCCESS; +} + +static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + if (priv->usb_interface[iface].path == NULL) { + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + } + + if (handle_priv->interface_handle[iface].dev_handle != INTERFACE_CLAIMED) { + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + } + + handle_priv->interface_handle[iface].dev_handle = INVALID_HANDLE_VALUE; + + return LIBUSB_SUCCESS; +} + +static int hid_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + if (altsetting > 255) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (altsetting != 0) { + usbi_err(ctx, "set interface altsetting not supported for altsetting >0"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + return LIBUSB_SUCCESS; +} + +static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *) transfer->buffer; + HANDLE hid_handle; + struct winfd wfd; + int current_interface, config; + size_t size; + int r = LIBUSB_ERROR_INVALID_PARAM; + + CHECK_HID_AVAILABLE; + + transfer_priv->pollable_fd = INVALID_WINFD; + safe_free(transfer_priv->hid_buffer); + transfer_priv->hid_dest = NULL; + size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + if (size > MAX_CTRL_BUFFER_LENGTH) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + current_interface = get_valid_interface(transfer->dev_handle, USB_API_HID); + if (current_interface < 0) { + if (auto_claim(transfer, ¤t_interface, USB_API_HID) != LIBUSB_SUCCESS) { + return LIBUSB_ERROR_NOT_FOUND; + } + } + + usbi_dbg("will use interface %d", current_interface); + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + // Always use the handle returned from usbi_create_fd (wfd.handle) + wfd = usbi_create_fd(hid_handle, RW_READ, NULL, NULL); + if (wfd.fd < 0) { + return LIBUSB_ERROR_NOT_FOUND; + } + + switch(LIBUSB_REQ_TYPE(setup->request_type)) { + case LIBUSB_REQUEST_TYPE_STANDARD: + switch(setup->request) { + case LIBUSB_REQUEST_GET_DESCRIPTOR: + r = _hid_get_descriptor(priv->hid, wfd.handle, LIBUSB_REQ_RECIPIENT(setup->request_type), + (setup->value >> 8) & 0xFF, setup->value & 0xFF, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, &size); + break; + case LIBUSB_REQUEST_GET_CONFIGURATION: + r = windows_get_configuration(transfer->dev_handle, &config); + if (r == LIBUSB_SUCCESS) { + size = 1; + ((uint8_t*)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = (uint8_t)config; + r = LIBUSB_COMPLETED; + } + break; + case LIBUSB_REQUEST_SET_CONFIGURATION: + if (setup->value == priv->active_config) { + r = LIBUSB_COMPLETED; + } else { + usbi_warn(ctx, "cannot set configuration other than the default one"); + r = LIBUSB_ERROR_INVALID_PARAM; + } + break; + case LIBUSB_REQUEST_GET_INTERFACE: + size = 1; + ((uint8_t*)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = 0; + r = LIBUSB_COMPLETED; + break; + case LIBUSB_REQUEST_SET_INTERFACE: + r = hid_set_interface_altsetting(0, transfer->dev_handle, setup->index, setup->value); + if (r == LIBUSB_SUCCESS) { + r = LIBUSB_COMPLETED; + } + break; + default: + usbi_warn(ctx, "unsupported HID control request"); + r = LIBUSB_ERROR_INVALID_PARAM; + break; + } + break; + case LIBUSB_REQUEST_TYPE_CLASS: + r =_hid_class_request(priv->hid, wfd.handle, setup->request_type, setup->request, setup->value, + setup->index, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, transfer_priv, + &size, wfd.overlapped); + break; + default: + usbi_warn(ctx, "unsupported HID control request"); + r = LIBUSB_ERROR_INVALID_PARAM; + break; + } + + if (r == LIBUSB_COMPLETED) { + // Force request to be completed synchronously. Transferred size has been set by previous call + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + // http://msdn.microsoft.com/en-us/library/ms684342%28VS.85%29.aspx + // set InternalHigh to the number of bytes transferred + wfd.overlapped->InternalHigh = (DWORD)size; + r = LIBUSB_SUCCESS; + } + + if (r == LIBUSB_SUCCESS) { + // Use priv_transfer to store data needed for async polling + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + } else { + usbi_free_fd(&wfd); + } + + return r; +} + +static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + struct winfd wfd; + HANDLE hid_handle; + bool direction_in, ret; + int current_interface, length; + DWORD size; + int r = LIBUSB_SUCCESS; + + CHECK_HID_AVAILABLE; + + transfer_priv->pollable_fd = INVALID_WINFD; + transfer_priv->hid_dest = NULL; + safe_free(transfer_priv->hid_buffer); + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface); + + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN; + + wfd = usbi_create_fd(hid_handle, direction_in?RW_READ:RW_WRITE, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) { + return LIBUSB_ERROR_NO_MEM; + } + + // If report IDs are not in use, an extra prefix byte must be added + if ( ((direction_in) && (!priv->hid->uses_report_ids[0])) + || ((!direction_in) && (!priv->hid->uses_report_ids[1])) ) { + length = transfer->length+1; + } else { + length = transfer->length; + } + // Add a trailing byte to detect overflows on input + transfer_priv->hid_buffer = (uint8_t*)calloc(length+1, 1); + if (transfer_priv->hid_buffer == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + transfer_priv->hid_expected_size = length; + + if (direction_in) { + transfer_priv->hid_dest = transfer->buffer; + usbi_dbg("reading %d bytes (report ID: 0x00)", length); + ret = ReadFile(wfd.handle, transfer_priv->hid_buffer, length+1, &size, wfd.overlapped); + } else { + if (!priv->hid->uses_report_ids[1]) { + memcpy(transfer_priv->hid_buffer+1, transfer->buffer, transfer->length); + } else { + // We could actually do without the calloc and memcpy in this case + memcpy(transfer_priv->hid_buffer, transfer->buffer, transfer->length); + } + usbi_dbg("writing %d bytes (report ID: 0x%02X)", length, transfer_priv->hid_buffer[0]); + ret = WriteFile(wfd.handle, transfer_priv->hid_buffer, length, &size, wfd.overlapped); + } + if (!ret) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_err(ctx, "HID transfer failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + safe_free(transfer_priv->hid_buffer); + return LIBUSB_ERROR_IO; + } + } else { + // Only write operations that completed synchronously need to free up + // hid_buffer. For reads, copy_transfer_data() handles that process. + if (!direction_in) { + safe_free(transfer_priv->hid_buffer); + } + if (size == 0) { + usbi_err(ctx, "program assertion failed - no data was transferred"); + size = 1; + } + if (size > (size_t)length) { + usbi_err(ctx, "OVERFLOW!"); + r = LIBUSB_ERROR_OVERFLOW; + } + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = size; + } + + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + + return r; +} + +static int hid_abort_transfers(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + current_interface = transfer_priv->interface_number; + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + CancelIo(hid_handle); + + return LIBUSB_SUCCESS; +} + +static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + // Flushing the queues on all interfaces is the best we can achieve + for (current_interface = 0; current_interface < USB_MAXINTERFACES; current_interface++) { + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + if ((hid_handle != 0) && (hid_handle != INVALID_HANDLE_VALUE)) { + HidD_FlushQueue(hid_handle); + } + } + return LIBUSB_SUCCESS; +} + +static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface); + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + + // No endpoint selection with Microsoft's implementation, so we try to flush the + // whole interface. Should be OK for most case scenarios + if (!HidD_FlushQueue(hid_handle)) { + usbi_err(ctx, "Flushing of HID queue failed: %s", windows_error_str(0)); + // Device was probably disconnected + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +// This extra function is only needed for HID +static int hid_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + int r = LIBUSB_TRANSFER_COMPLETED; + uint32_t corrected_size = io_size; + + if (transfer_priv->hid_buffer != NULL) { + // If we have a valid hid_buffer, it means the transfer was async + if (transfer_priv->hid_dest != NULL) { // Data readout + if (corrected_size > 0) { + // First, check for overflow + if (corrected_size > transfer_priv->hid_expected_size) { + usbi_err(ctx, "OVERFLOW!"); + corrected_size = (uint32_t)transfer_priv->hid_expected_size; + r = LIBUSB_TRANSFER_OVERFLOW; + } + + if (transfer_priv->hid_buffer[0] == 0) { + // Discard the 1 byte report ID prefix + corrected_size--; + memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer+1, corrected_size); + } else { + memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer, corrected_size); + } + } + transfer_priv->hid_dest = NULL; + } + // For write, we just need to free the hid buffer + safe_free(transfer_priv->hid_buffer); + } + itransfer->transferred += corrected_size; + return r; +} + + +/* + * Composite API functions + */ +static int composite_init(int sub_api, struct libusb_context *ctx) +{ + return LIBUSB_SUCCESS; +} + +static int composite_exit(int sub_api) +{ + return LIBUSB_SUCCESS; +} + +static int composite_open(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int r = LIBUSB_ERROR_NOT_FOUND; + uint8_t i; + // SUB_API_MAX+1 as the SUB_API_MAX pos is used to indicate availability of HID + bool available[SUB_API_MAX+1] = {0}; + + for (i=0; iusb_interface[i].apib->id) { + case USB_API_WINUSBX: + if (priv->usb_interface[i].sub_api != SUB_API_NOTSET) + available[priv->usb_interface[i].sub_api] = true; + break; + case USB_API_HID: + available[SUB_API_MAX] = true; + break; + default: + break; + } + } + + for (i=0; idev); + uint8_t i; + bool available[SUB_API_MAX]; + + for (i = 0; iusb_interface[i].apib->id == USB_API_WINUSBX) + && (priv->usb_interface[i].sub_api != SUB_API_NOTSET) ) { + available[priv->usb_interface[i].sub_api] = true; + } + } + + for (i=0; idev); + return priv->usb_interface[iface].apib-> + claim_interface(priv->usb_interface[iface].sub_api, dev_handle, iface); +} + +static int composite_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + return priv->usb_interface[iface].apib-> + set_interface_altsetting(priv->usb_interface[iface].sub_api, dev_handle, iface, altsetting); +} + +static int composite_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + return priv->usb_interface[iface].apib-> + release_interface(priv->usb_interface[iface].sub_api, dev_handle, iface); +} + +static int composite_submit_control_transfer(int sub_api, 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 windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int i, pass; + + // Interface shouldn't matter for control, but it does in practice, with Windows' + // restrictions with regards to accessing HID keyboards and mice. Try a 2 pass approach + for (pass = 0; pass < 2; pass++) { + for (i=0; iusb_interface[i].path != NULL) { + if ((pass == 0) && (priv->usb_interface[i].restricted_functionality)) { + usbi_dbg("trying to skip restricted interface #%d (HID keyboard or mouse?)", i); + continue; + } + usbi_dbg("using interface %d", i); + return priv->usb_interface[i].apib->submit_control_transfer(priv->usb_interface[i].sub_api, itransfer); + } + } + } + + usbi_err(ctx, "no libusb supported interfaces to complete request"); + return LIBUSB_ERROR_NOT_FOUND; +} + +static int composite_submit_bulk_transfer(int sub_api, 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 windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int current_interface; + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib-> + submit_bulk_transfer(priv->usb_interface[current_interface].sub_api, itransfer);} + +static int composite_submit_iso_transfer(int sub_api, 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 windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int current_interface; + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib-> + submit_iso_transfer(priv->usb_interface[current_interface].sub_api, itransfer);} + +static int composite_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int current_interface; + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib-> + clear_halt(priv->usb_interface[current_interface].sub_api, dev_handle, endpoint);} + +static int composite_abort_control(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + + return priv->usb_interface[transfer_priv->interface_number].apib-> + abort_control(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer);} + +static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + + return priv->usb_interface[transfer_priv->interface_number].apib-> + abort_transfers(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer);} + +static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int r; + uint8_t i; + bool available[SUB_API_MAX]; + for (i = 0; iusb_interface[i].apib->id == USB_API_WINUSBX) + && (priv->usb_interface[i].sub_api != SUB_API_NOTSET) ) { + available[priv->usb_interface[i].sub_api] = true; + } + } + for (i=0; idev_handle->dev); + + return priv->usb_interface[transfer_priv->interface_number].apib-> + copy_transfer_data(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer, io_size); +} diff --git a/e502/libusb-1.0/libusb-1.0/os/windows_usb.h b/e502/libusb-1.0/libusb-1.0/os/windows_usb.h new file mode 100644 index 0000000..e9e50ef --- /dev/null +++ b/e502/libusb-1.0/libusb-1.0/os/windows_usb.h @@ -0,0 +1,958 @@ +/* + * 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 + * 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" + +#if defined(_MSC_VER) +// disable /W4 MSVC warnings that are benign +#pragma warning(disable:4127) // conditional expression is constant +#pragma warning(disable:4100) // unreferenced formal parameter +#pragma warning(disable:4214) // bit field types other than int +#pragma warning(disable:4201) // nameless struct/union +#endif + +// Missing from MSVC6 setupapi.h +#if !defined(SPDRP_ADDRESS) +#define SPDRP_ADDRESS 28 +#endif +#if !defined(SPDRP_INSTALL_STATE) +#define SPDRP_INSTALL_STATE 34 +#endif + +// Missing from MinGW +#if !defined(FACILITY_SETUPAPI) +#define FACILITY_SETUPAPI 15 +#endif + +#if defined(__CYGWIN__ ) +#define _stricmp stricmp +// cygwin produces a warning unless these prototypes are defined +extern int _snprintf(char *buffer, size_t count, const char *format, ...); +extern char *_strdup(const char *strSource); +// _beginthreadex is MSVCRT => unavailable for cygwin. Fallback to using CreateThread +#define _beginthreadex(a, b, c, d, e, f) CreateThread(a, b, (LPTHREAD_START_ROUTINE)c, d, e, f) +#endif + +#define MAX_CTRL_BUFFER_LENGTH 4096 +#define MAX_USB_DEVICES 256 +#define MAX_USB_STRING_LENGTH 128 +#define MAX_HID_REPORT_SIZE 1024 +#define MAX_HID_DESCRIPTOR_SIZE 256 +#define MAX_GUID_STRING_LENGTH 40 +#define MAX_PATH_LENGTH 128 +#define MAX_KEY_LENGTH 256 +#define LIST_SEPARATOR ';' +#define HTAB_SIZE (32*1021) + +// Handle code for HID interface that have been claimed ("dibs") +#define INTERFACE_CLAIMED ((HANDLE)(intptr_t)0xD1B5) +// Additional return code for HID operations that completed synchronously +#define LIBUSB_COMPLETED (LIBUSB_SUCCESS + 1) + +// http://msdn.microsoft.com/en-us/library/ff545978.aspx +// http://msdn.microsoft.com/en-us/library/ff545972.aspx +// http://msdn.microsoft.com/en-us/library/ff545982.aspx +#if !defined(GUID_DEVINTERFACE_USB_HOST_CONTROLLER) +const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = { 0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27} }; +#endif +#if !defined(GUID_DEVINTERFACE_USB_DEVICE) +const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED} }; +#endif +#if !defined(GUID_DEVINTERFACE_USB_HUB) +const GUID GUID_DEVINTERFACE_USB_HUB = { 0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8} }; +#endif +#if !defined(GUID_DEVINTERFACE_LIBUSB0_FILTER) +const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = { 0xF9F3FF14, 0xAE21, 0x48A0, {0x8A, 0x25, 0x80, 0x11, 0xA7, 0xA9, 0x31, 0xD9} }; +#endif + + +/* + * Multiple USB API backend support + */ +#define USB_API_UNSUPPORTED 0 +#define USB_API_HUB 1 +#define USB_API_COMPOSITE 2 +#define USB_API_WINUSBX 3 +#define USB_API_HID 4 +#define USB_API_MAX 5 +// The following is used to indicate if the HID or composite extra props have already been set. +#define USB_API_SET (1<os_priv; +} + +static inline void windows_device_priv_init(libusb_device* dev) { + struct windows_device_priv* p = _device_priv(dev); + int i; + p->depth = 0; + p->port = 0; + p->parent_dev = NULL; + p->path = NULL; + p->apib = &usb_api_backend[USB_API_UNSUPPORTED]; + p->sub_api = SUB_API_NOTSET; + p->hid = NULL; + p->active_config = 0; + p->config_descriptor = NULL; + memset(&(p->dev_descriptor), 0, sizeof(USB_DEVICE_DESCRIPTOR)); + for (i=0; iusb_interface[i].path = NULL; + p->usb_interface[i].apib = &usb_api_backend[USB_API_UNSUPPORTED]; + p->usb_interface[i].sub_api = SUB_API_NOTSET; + p->usb_interface[i].nb_endpoints = 0; + p->usb_interface[i].endpoint = NULL; + p->usb_interface[i].restricted_functionality = false; + } +} + +static inline void windows_device_priv_release(libusb_device* dev) { + struct windows_device_priv* p = _device_priv(dev); + int i; + safe_free(p->path); + if ((dev->num_configurations > 0) && (p->config_descriptor != NULL)) { + for (i=0; i < dev->num_configurations; i++) + safe_free(p->config_descriptor[i]); + } + safe_free(p->config_descriptor); + safe_free(p->hid); + for (i=0; iusb_interface[i].path); + safe_free(p->usb_interface[i].endpoint); + } +} + +struct interface_handle_t { + HANDLE dev_handle; // WinUSB needs an extra handle for the file + HANDLE api_handle; // used by the API to communicate with the device +}; + +struct windows_device_handle_priv { + int active_interface; + struct interface_handle_t interface_handle[USB_MAXINTERFACES]; + int autoclaim_count[USB_MAXINTERFACES]; // For auto-release +}; + +static inline struct windows_device_handle_priv *_device_handle_priv( + struct libusb_device_handle *handle) +{ + return (struct windows_device_handle_priv *) handle->os_priv; +} + +// used for async polling functions +struct windows_transfer_priv { + struct winfd pollable_fd; + uint8_t interface_number; + uint8_t *hid_buffer; // 1 byte extended data buffer, required for HID + uint8_t *hid_dest; // transfer buffer destination, required for HID + size_t hid_expected_size; +}; + +// used to match a device driver (including filter drivers) against a supported API +struct driver_lookup { + char list[MAX_KEY_LENGTH+1];// REG_MULTI_SZ list of services (driver) names + const DWORD reg_prop; // SPDRP registry key to use to retreive list + const char* designation; // internal designation (for debug output) +}; + +/* OLE32 dependency */ +DLL_DECLARE_PREFIXED(WINAPI, HRESULT, p, CLSIDFromString, (LPCOLESTR, LPCLSID)); + +/* This call is only available from XP SP2 */ +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, IsWow64Process, (HANDLE, PBOOL)); + +/* SetupAPI dependencies */ +DLL_DECLARE_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (const GUID*, PCSTR, HWND, DWORD)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInfo, (HDEVINFO, DWORD, PSP_DEVINFO_DATA)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInterfaces, (HDEVINFO, PSP_DEVINFO_DATA, + const GUID*, DWORD, PSP_DEVICE_INTERFACE_DATA)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceInterfaceDetailA, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, + PSP_DEVICE_INTERFACE_DETAIL_DATA_A, DWORD, PDWORD, PSP_DEVINFO_DATA)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiDestroyDeviceInfoList, (HDEVINFO)); +DLL_DECLARE_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDevRegKey, (HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceRegistryPropertyA, (HDEVINFO, + PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD)); +DLL_DECLARE_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDeviceInterfaceRegKey, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, DWORD, DWORD)); +DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegQueryValueExW, (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD)); +DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY)); + +/* + * Windows DDK API definitions. Most of it copied from MinGW's includes + */ +typedef DWORD DEVNODE, DEVINST; +typedef DEVNODE *PDEVNODE, *PDEVINST; +typedef DWORD RETURN_TYPE; +typedef RETURN_TYPE CONFIGRET; + +#define CR_SUCCESS 0x00000000 +#define CR_NO_SUCH_DEVNODE 0x0000000D + +#define USB_DEVICE_DESCRIPTOR_TYPE LIBUSB_DT_DEVICE +#define USB_CONFIGURATION_DESCRIPTOR_TYPE LIBUSB_DT_CONFIG +#define USB_STRING_DESCRIPTOR_TYPE LIBUSB_DT_STRING +#define USB_INTERFACE_DESCRIPTOR_TYPE LIBUSB_DT_INTERFACE +#define USB_ENDPOINT_DESCRIPTOR_TYPE LIBUSB_DT_ENDPOINT + +#define USB_REQUEST_GET_STATUS LIBUSB_REQUEST_GET_STATUS +#define USB_REQUEST_CLEAR_FEATURE LIBUSB_REQUEST_CLEAR_FEATURE +#define USB_REQUEST_SET_FEATURE LIBUSB_REQUEST_SET_FEATURE +#define USB_REQUEST_SET_ADDRESS LIBUSB_REQUEST_SET_ADDRESS +#define USB_REQUEST_GET_DESCRIPTOR LIBUSB_REQUEST_GET_DESCRIPTOR +#define USB_REQUEST_SET_DESCRIPTOR LIBUSB_REQUEST_SET_DESCRIPTOR +#define USB_REQUEST_GET_CONFIGURATION LIBUSB_REQUEST_GET_CONFIGURATION +#define USB_REQUEST_SET_CONFIGURATION LIBUSB_REQUEST_SET_CONFIGURATION +#define USB_REQUEST_GET_INTERFACE LIBUSB_REQUEST_GET_INTERFACE +#define USB_REQUEST_SET_INTERFACE LIBUSB_REQUEST_SET_INTERFACE +#define USB_REQUEST_SYNC_FRAME LIBUSB_REQUEST_SYNCH_FRAME + +#define USB_GET_NODE_INFORMATION 258 +#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260 +#define USB_GET_NODE_CONNECTION_NAME 261 +#define USB_GET_HUB_CAPABILITIES 271 +#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX) +#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274 +#endif +#if !defined(USB_GET_HUB_CAPABILITIES_EX) +#define USB_GET_HUB_CAPABILITIES_EX 276 +#endif +#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX_V2) +#define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 279 +#endif + +#ifndef METHOD_BUFFERED +#define METHOD_BUFFERED 0 +#endif +#ifndef FILE_ANY_ACCESS +#define FILE_ANY_ACCESS 0x00000000 +#endif +#ifndef FILE_DEVICE_UNKNOWN +#define FILE_DEVICE_UNKNOWN 0x00000022 +#endif +#ifndef FILE_DEVICE_USB +#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN +#endif + +#ifndef CTL_CODE +#define CTL_CODE(DeviceType, Function, Method, Access)( \ + ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) +#endif + +typedef enum USB_CONNECTION_STATUS { + NoDeviceConnected, + DeviceConnected, + DeviceFailedEnumeration, + DeviceGeneralFailure, + DeviceCausedOvercurrent, + DeviceNotEnoughPower, + DeviceNotEnoughBandwidth, + DeviceHubNestedTooDeeply, + DeviceInLegacyHub +} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS; + +typedef enum USB_HUB_NODE { + UsbHub, + UsbMIParent +} USB_HUB_NODE; + +/* Cfgmgr32.dll interface */ +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG)); + +#define IOCTL_USB_GET_HUB_CAPABILITIES_EX \ + CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_HUB_CAPABILITIES \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_ROOT_HUB_NAME \ + CTL_CODE(FILE_DEVICE_USB, HCD_GET_ROOT_HUB_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_INFORMATION \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_ATTRIBUTES, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_NAME \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) + +// Most of the structures below need to be packed +#pragma pack(push, 1) + +typedef struct USB_INTERFACE_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bInterfaceNumber; + UCHAR bAlternateSetting; + UCHAR bNumEndpoints; + UCHAR bInterfaceClass; + UCHAR bInterfaceSubClass; + UCHAR bInterfaceProtocol; + UCHAR iInterface; +} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR; + +typedef struct USB_CONFIGURATION_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + USHORT wTotalLength; + UCHAR bNumInterfaces; + UCHAR bConfigurationValue; + UCHAR iConfiguration; + UCHAR bmAttributes; + UCHAR MaxPower; +} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR; + +typedef struct USB_CONFIGURATION_DESCRIPTOR_SHORT { + struct { + ULONG ConnectionIndex; + struct { + UCHAR bmRequest; + UCHAR bRequest; + USHORT wValue; + USHORT wIndex; + USHORT wLength; + } SetupPacket; + } req; + USB_CONFIGURATION_DESCRIPTOR data; +} USB_CONFIGURATION_DESCRIPTOR_SHORT; + +typedef struct USB_ENDPOINT_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bEndpointAddress; + UCHAR bmAttributes; + USHORT wMaxPacketSize; + UCHAR bInterval; +} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR; + +typedef struct USB_DESCRIPTOR_REQUEST { + ULONG ConnectionIndex; + struct { + UCHAR bmRequest; + UCHAR bRequest; + USHORT wValue; + USHORT wIndex; + USHORT wLength; + } SetupPacket; +// UCHAR Data[0]; +} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST; + +typedef struct USB_HUB_DESCRIPTOR { + UCHAR bDescriptorLength; + UCHAR bDescriptorType; + UCHAR bNumberOfPorts; + USHORT wHubCharacteristics; + UCHAR bPowerOnToPowerGood; + UCHAR bHubControlCurrent; + UCHAR bRemoveAndPowerMask[64]; +} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR; + +typedef struct USB_ROOT_HUB_NAME { + ULONG ActualLength; + WCHAR RootHubName[1]; +} USB_ROOT_HUB_NAME, *PUSB_ROOT_HUB_NAME; + +typedef struct USB_ROOT_HUB_NAME_FIXED { + ULONG ActualLength; + WCHAR RootHubName[MAX_PATH_LENGTH]; +} USB_ROOT_HUB_NAME_FIXED; + +typedef struct USB_NODE_CONNECTION_NAME { + ULONG ConnectionIndex; + ULONG ActualLength; + WCHAR NodeName[1]; +} USB_NODE_CONNECTION_NAME, *PUSB_NODE_CONNECTION_NAME; + +typedef struct USB_NODE_CONNECTION_NAME_FIXED { + ULONG ConnectionIndex; + ULONG ActualLength; + WCHAR NodeName[MAX_PATH_LENGTH]; +} USB_NODE_CONNECTION_NAME_FIXED; + +typedef struct USB_HUB_NAME_FIXED { + union { + USB_ROOT_HUB_NAME_FIXED root; + USB_NODE_CONNECTION_NAME_FIXED node; + } u; +} USB_HUB_NAME_FIXED; + +typedef struct USB_HUB_INFORMATION { + USB_HUB_DESCRIPTOR HubDescriptor; + BOOLEAN HubIsBusPowered; +} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION; + +typedef struct USB_MI_PARENT_INFORMATION { + ULONG NumberOfInterfaces; +} USB_MI_PARENT_INFORMATION, *PUSB_MI_PARENT_INFORMATION; + +typedef struct USB_NODE_INFORMATION { + USB_HUB_NODE NodeType; + union { + USB_HUB_INFORMATION HubInformation; + USB_MI_PARENT_INFORMATION MiParentInformation; + } u; +} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION; + +typedef struct USB_PIPE_INFO { + USB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + ULONG ScheduleOffset; +} USB_PIPE_INFO, *PUSB_PIPE_INFO; + +typedef struct USB_NODE_CONNECTION_INFORMATION_EX { + ULONG ConnectionIndex; + USB_DEVICE_DESCRIPTOR DeviceDescriptor; + UCHAR CurrentConfigurationValue; + UCHAR Speed; + BOOLEAN DeviceIsHub; + USHORT DeviceAddress; + ULONG NumberOfOpenPipes; + USB_CONNECTION_STATUS ConnectionStatus; +// USB_PIPE_INFO PipeList[0]; +} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX; + +typedef union _USB_PROTOCOLS { + ULONG ul; + struct { + ULONG Usb110:1; + ULONG Usb200:1; + ULONG Usb300:1; + ULONG ReservedMBZ:29; + }; +} USB_PROTOCOLS, *PUSB_PROTOCOLS; + +typedef union _USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS { + ULONG ul; + struct { + ULONG DeviceIsOperatingAtSuperSpeedOrHigher:1; + ULONG DeviceIsSuperSpeedCapableOrHigher:1; + ULONG ReservedMBZ:30; + }; +} USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS; + +typedef struct _USB_NODE_CONNECTION_INFORMATION_EX_V2 { + ULONG ConnectionIndex; + ULONG Length; + USB_PROTOCOLS SupportedUsbProtocols; + USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS Flags; +} USB_NODE_CONNECTION_INFORMATION_EX_V2, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2; + +typedef struct USB_HUB_CAP_FLAGS { + ULONG HubIsHighSpeedCapable:1; + ULONG HubIsHighSpeed:1; + ULONG HubIsMultiTtCapable:1; + ULONG HubIsMultiTt:1; + ULONG HubIsRoot:1; + ULONG HubIsArmedWakeOnConnect:1; + ULONG ReservedMBZ:26; +} USB_HUB_CAP_FLAGS, *PUSB_HUB_CAP_FLAGS; + +typedef struct USB_HUB_CAPABILITIES { + ULONG HubIs2xCapable : 1; +} USB_HUB_CAPABILITIES, *PUSB_HUB_CAPABILITIES; + +typedef struct USB_HUB_CAPABILITIES_EX { + USB_HUB_CAP_FLAGS CapabilityFlags; +} USB_HUB_CAPABILITIES_EX, *PUSB_HUB_CAPABILITIES_EX; + +#pragma pack(pop) + +/* winusb.dll interface */ + +#define SHORT_PACKET_TERMINATE 0x01 +#define AUTO_CLEAR_STALL 0x02 +#define PIPE_TRANSFER_TIMEOUT 0x03 +#define IGNORE_SHORT_PACKETS 0x04 +#define ALLOW_PARTIAL_READS 0x05 +#define AUTO_FLUSH 0x06 +#define RAW_IO 0x07 +#define MAXIMUM_TRANSFER_SIZE 0x08 +#define AUTO_SUSPEND 0x81 +#define SUSPEND_DELAY 0x83 +#define DEVICE_SPEED 0x01 +#define LowSpeed 0x01 +#define FullSpeed 0x02 +#define HighSpeed 0x03 + +typedef enum USBD_PIPE_TYPE { + UsbdPipeTypeControl, + UsbdPipeTypeIsochronous, + UsbdPipeTypeBulk, + UsbdPipeTypeInterrupt +} USBD_PIPE_TYPE; + +typedef struct { + USBD_PIPE_TYPE PipeType; + UCHAR PipeId; + USHORT MaximumPacketSize; + UCHAR Interval; +} WINUSB_PIPE_INFORMATION, *PWINUSB_PIPE_INFORMATION; + +#pragma pack(1) +typedef struct { + UCHAR request_type; + UCHAR request; + USHORT value; + USHORT index; + USHORT length; +} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET; +#pragma pack() + +typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; + +typedef BOOL (WINAPI *WinUsb_AbortPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID +); +typedef BOOL (WINAPI *WinUsb_ControlTransfer_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + WINUSB_SETUP_PACKET SetupPacket, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred, + LPOVERLAPPED Overlapped +); +typedef BOOL (WINAPI *WinUsb_FlushPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID +); +typedef BOOL (WINAPI *WinUsb_Free_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle +); +typedef BOOL (WINAPI *WinUsb_GetAssociatedInterface_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AssociatedInterfaceIndex, + PWINUSB_INTERFACE_HANDLE AssociatedInterfaceHandle +); +typedef BOOL (WINAPI *WinUsb_GetCurrentAlternateSetting_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + PUCHAR AlternateSetting +); +typedef BOOL (WINAPI *WinUsb_GetDescriptor_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR DescriptorType, + UCHAR Index, + USHORT LanguageID, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred +); +typedef BOOL (WINAPI *WinUsb_GetOverlappedResult_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + LPOVERLAPPED lpOverlapped, + LPDWORD lpNumberOfBytesTransferred, + BOOL bWait +); +typedef BOOL (WINAPI *WinUsb_GetPipePolicy_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + ULONG PolicyType, + PULONG ValueLength, + PVOID Value +); +typedef BOOL (WINAPI *WinUsb_GetPowerPolicy_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + ULONG PolicyType, + PULONG ValueLength, + PVOID Value +); +typedef BOOL (WINAPI *WinUsb_Initialize_t)( + HANDLE DeviceHandle, + PWINUSB_INTERFACE_HANDLE InterfaceHandle +); +typedef BOOL (WINAPI *WinUsb_QueryDeviceInformation_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + ULONG InformationType, + PULONG BufferLength, + PVOID Buffer +); +typedef BOOL (WINAPI *WinUsb_QueryInterfaceSettings_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AlternateSettingNumber, + PUSB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor +); +typedef BOOL (WINAPI *WinUsb_QueryPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AlternateInterfaceNumber, + UCHAR PipeIndex, + PWINUSB_PIPE_INFORMATION PipeInformation +); +typedef BOOL (WINAPI *WinUsb_ReadPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred, + LPOVERLAPPED Overlapped +); +typedef BOOL (WINAPI *WinUsb_ResetPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID +); +typedef BOOL (WINAPI *WinUsb_SetCurrentAlternateSetting_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AlternateSetting +); +typedef BOOL (WINAPI *WinUsb_SetPipePolicy_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + ULONG PolicyType, + ULONG ValueLength, + PVOID Value +); +typedef BOOL (WINAPI *WinUsb_SetPowerPolicy_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + ULONG PolicyType, + ULONG ValueLength, + PVOID Value +); +typedef BOOL (WINAPI *WinUsb_WritePipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred, + LPOVERLAPPED Overlapped +); +typedef BOOL (WINAPI *WinUsb_ResetDevice_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle +); + +/* /!\ These must match the ones from the official libusbk.h */ +typedef enum _KUSB_FNID +{ + KUSB_FNID_Init, + KUSB_FNID_Free, + KUSB_FNID_ClaimInterface, + KUSB_FNID_ReleaseInterface, + KUSB_FNID_SetAltInterface, + KUSB_FNID_GetAltInterface, + KUSB_FNID_GetDescriptor, + KUSB_FNID_ControlTransfer, + KUSB_FNID_SetPowerPolicy, + KUSB_FNID_GetPowerPolicy, + KUSB_FNID_SetConfiguration, + KUSB_FNID_GetConfiguration, + KUSB_FNID_ResetDevice, + KUSB_FNID_Initialize, + KUSB_FNID_SelectInterface, + KUSB_FNID_GetAssociatedInterface, + KUSB_FNID_Clone, + KUSB_FNID_QueryInterfaceSettings, + KUSB_FNID_QueryDeviceInformation, + KUSB_FNID_SetCurrentAlternateSetting, + KUSB_FNID_GetCurrentAlternateSetting, + KUSB_FNID_QueryPipe, + KUSB_FNID_SetPipePolicy, + KUSB_FNID_GetPipePolicy, + KUSB_FNID_ReadPipe, + KUSB_FNID_WritePipe, + KUSB_FNID_ResetPipe, + KUSB_FNID_AbortPipe, + KUSB_FNID_FlushPipe, + KUSB_FNID_IsoReadPipe, + KUSB_FNID_IsoWritePipe, + KUSB_FNID_GetCurrentFrameNumber, + KUSB_FNID_GetOverlappedResult, + KUSB_FNID_GetProperty, + KUSB_FNID_COUNT, +} KUSB_FNID; + +typedef struct _KLIB_VERSION { + INT Major; + INT Minor; + INT Micro; + INT Nano; +} KLIB_VERSION; +typedef KLIB_VERSION* PKLIB_VERSION; + +typedef BOOL (WINAPI *LibK_GetProcAddress_t)( + PVOID* ProcAddress, + ULONG DriverID, + ULONG FunctionID +); + +typedef VOID (WINAPI *LibK_GetVersion_t)( + PKLIB_VERSION Version +); + +struct winusb_interface { + bool initialized; + WinUsb_AbortPipe_t AbortPipe; + WinUsb_ControlTransfer_t ControlTransfer; + WinUsb_FlushPipe_t FlushPipe; + WinUsb_Free_t Free; + WinUsb_GetAssociatedInterface_t GetAssociatedInterface; + WinUsb_GetCurrentAlternateSetting_t GetCurrentAlternateSetting; + WinUsb_GetDescriptor_t GetDescriptor; + WinUsb_GetOverlappedResult_t GetOverlappedResult; + WinUsb_GetPipePolicy_t GetPipePolicy; + WinUsb_GetPowerPolicy_t GetPowerPolicy; + WinUsb_Initialize_t Initialize; + WinUsb_QueryDeviceInformation_t QueryDeviceInformation; + WinUsb_QueryInterfaceSettings_t QueryInterfaceSettings; + WinUsb_QueryPipe_t QueryPipe; + WinUsb_ReadPipe_t ReadPipe; + WinUsb_ResetPipe_t ResetPipe; + WinUsb_SetCurrentAlternateSetting_t SetCurrentAlternateSetting; + WinUsb_SetPipePolicy_t SetPipePolicy; + WinUsb_SetPowerPolicy_t SetPowerPolicy; + WinUsb_WritePipe_t WritePipe; + WinUsb_ResetDevice_t ResetDevice; +}; + +/* hid.dll interface */ + +#define HIDP_STATUS_SUCCESS 0x110000 +typedef void* PHIDP_PREPARSED_DATA; + +#pragma pack(1) +typedef struct { + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; +#pragma pack() + +typedef USHORT USAGE; +typedef struct { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +} HIDP_CAPS, *PHIDP_CAPS; + +typedef enum _HIDP_REPORT_TYPE { + HidP_Input, + HidP_Output, + HidP_Feature +} HIDP_REPORT_TYPE; + +typedef struct _HIDP_VALUE_CAPS { + USAGE UsagePage; + UCHAR ReportID; + BOOLEAN IsAlias; + USHORT BitField; + USHORT LinkCollection; + USAGE LinkUsage; + USAGE LinkUsagePage; + BOOLEAN IsRange; + BOOLEAN IsStringRange; + BOOLEAN IsDesignatorRange; + BOOLEAN IsAbsolute; + BOOLEAN HasNull; + UCHAR Reserved; + USHORT BitSize; + USHORT ReportCount; + USHORT Reserved2[5]; + ULONG UnitsExp; + ULONG Units; + LONG LogicalMin, LogicalMax; + LONG PhysicalMin, PhysicalMax; + union { + struct { + USAGE UsageMin, UsageMax; + USHORT StringMin, StringMax; + USHORT DesignatorMin, DesignatorMax; + USHORT DataIndexMin, DataIndexMax; + } Range; + struct { + USAGE Usage, Reserved1; + USHORT StringIndex, Reserved2; + USHORT DesignatorIndex, Reserved3; + USHORT DataIndex, Reserved4; + } NotRange; + } u; +} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS; + +DLL_DECLARE(WINAPI, BOOL, HidD_GetAttributes, (HANDLE, PHIDD_ATTRIBUTES)); +DLL_DECLARE(WINAPI, VOID, HidD_GetHidGuid, (LPGUID)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetPreparsedData, (HANDLE, PHIDP_PREPARSED_DATA *)); +DLL_DECLARE(WINAPI, BOOL, HidD_FreePreparsedData, (PHIDP_PREPARSED_DATA)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetManufacturerString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetProductString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetSerialNumberString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, LONG, HidP_GetCaps, (PHIDP_PREPARSED_DATA, PHIDP_CAPS)); +DLL_DECLARE(WINAPI, BOOL, HidD_SetNumInputBuffers, (HANDLE, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_SetFeature, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetFeature, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetPhysicalDescriptor, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetInputReport, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_SetOutputReport, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_FlushQueue, (HANDLE)); +DLL_DECLARE(WINAPI, BOOL, HidP_GetValueCaps, (HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PULONG, PHIDP_PREPARSED_DATA)); diff --git a/e502/libusb-1.0/libusb-1.0/strerror.c b/e502/libusb-1.0/libusb-1.0/strerror.c new file mode 100644 index 0000000..7f04b4e --- /dev/null +++ b/e502/libusb-1.0/libusb-1.0/strerror.c @@ -0,0 +1,169 @@ +/* + * libusb strerror code + * Copyright © 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 "libusb.h" +#include "libusbi.h" + +#if defined(_MSC_VER) +#define strncasecmp _strnicmp +#endif + +static size_t usbi_locale = 0; + +/** \ingroup misc + * How to add a new \ref libusb_strerror() translation: + *
    + *
  1. Download the latest \c strerror.c from:
    + * https://raw.github.com/libusb/libusb/master/libusb/sterror.c
  2. + *
  3. Open the file in an UTF-8 capable editor
  4. + *
  5. Add the 2 letter ISO 639-1 + * code for your locale at the end of \c usbi_locale_supported[]
    + * Eg. for Chinese, you would add "zh" so that: + * \code... usbi_locale_supported[] = { "en", "nl", "fr" };\endcode + * becomes: + * \code... usbi_locale_supported[] = { "en", "nl", "fr", "zh" };\endcode
  6. + *
  7. Copy the { / * English (en) * / ... } section and add it at the end of \c usbi_localized_errors
    + * Eg. for Chinese, the last section of \c usbi_localized_errors could look like: + * \code + * }, { / * Chinese (zh) * / + * "Success", + * ... + * "Other error", + * } + * };\endcode
  8. + *
  9. Translate each of the English messages from the section you copied into your language
  10. + *
  11. Save the file (in UTF-8 format) and send it to \c libusb-devel\@lists.sourceforge.net
  12. + *
+ */ + +static const char* usbi_locale_supported[] = { "en", "ru" }; +static const char* usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUSB_ERROR_COUNT] = { + { /* English (en) */ + "Success", + "Input/Output Error", + "Invalid parameter", + "Access denied (insufficient permissions)", + "No such device (it may have been disconnected)", + "Entity not found", + "Resource busy", + "Operation timed out", + "Overflow", + "Pipe error", + "System call interrupted (perhaps due to signal)", + "Insufficient memory", + "Operation not supported or unimplemented on this platform", + "Other error", + }, { /* Russian (ru) */ + "Успех", + "Ошибка ввода/вывода", + "Неверный параметр", + "Доступ запрещён (не хватает прав)", + "Устройство отсутствует (возможно, оно было отсоединено)", + "Элемент не найден", + "Ресурс занят", + "Истекло время ожидания операции", + "Переполнение", + "Ошибка канала", + "Системный вызов прерван (возможно, сигналом)", + "Память исчерпана", + "Операция не поддерживается данной платформой", + "Неизвестная ошибка" + } +}; + +/** \ingroup misc + * Set the language, and only the language, not the encoding! used for + * translatable libusb messages. + * + * This takes a locale string in the default setlocale format: lang[-region] + * or lang[_country_region][.codeset]. Only the lang part of the string is + * used, and only 2 letter ISO 639-1 codes are accepted for it, such as "de". + * The optional region, country_region or codeset parts are ignored. This + * means that functions which return translatable strings will NOT honor the + * specified encoding. + * All strings returned are encoded as UTF-8 strings. + * + * If libusb_setlocale() is not called, all messages will be in English. + * + * The following functions return translatable strings: libusb_strerror(). + * Note that the libusb log messages controlled through libusb_set_debug() + * are not translated, they are always in English. + * + * For POSIX UTF-8 environments if you want libusb to follow the standard + * locale settings, call libusb_setlocale(setlocale(LC_MESSAGES, NULL)), + * after your app has done its locale setup. + * + * \param locale locale-string in the form of lang[_country_region][.codeset] + * or lang[-region], where lang is a 2 letter ISO 639-1 code + * \returns LIBUSB_SUCCESS on success + * \returns LIBUSB_ERROR_INVALID_PARAM if the locale doesn't meet the requirements + * \returns LIBUSB_ERROR_NOT_FOUND if the requested language is not supported + * \returns a LIBUSB_ERROR code on other errors + */ + +int API_EXPORTED libusb_setlocale(const char *locale) +{ + size_t i; + + if ( (locale == NULL) || (strlen(locale) < 2) + || ((strlen(locale) > 2) && (locale[2] != '-') && (locale[2] != '_') && (locale[2] != '.')) ) + return LIBUSB_ERROR_INVALID_PARAM; + + for (i=0; i= ARRAYSIZE(usbi_locale_supported)) { + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_locale = i; + + return LIBUSB_SUCCESS; +} + +/** \ingroup misc + * Returns a constant string with a short description of the given error code, + * this description is intended for displaying to the end user and will be in + * the language set by libusb_setlocale(). + * + * The returned string is encoded in UTF-8. + * + * The messages always start with a capital letter and end without any dot. + * The caller must not free() the returned string. + * + * \param errcode the error code whose description is desired + * \returns a short description of the error code in UTF-8 encoding + */ +DEFAULT_VISIBILITY const char* LIBUSB_CALL libusb_strerror(enum libusb_error errcode) +{ + int errcode_index = -errcode; + + if ((errcode_index < 0) || (errcode_index >= LIBUSB_ERROR_COUNT)) { + /* "Other Error", which should always be our last message, is returned */ + errcode_index = LIBUSB_ERROR_COUNT - 1; + } + + return usbi_localized_errors[usbi_locale][errcode_index]; +} diff --git a/e502/libusb-1.0/libusb-1.0/sync.c b/e502/libusb-1.0/libusb-1.0/sync.c new file mode 100644 index 0000000..d87032d --- /dev/null +++ b/e502/libusb-1.0/libusb-1.0/sync.c @@ -0,0 +1,307 @@ +/* + * Synchronous I/O functions for libusb + * Copyright © 2007-2008 Daniel Drake + * + * 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 "libusbi.h" + +/** + * @defgroup syncio Synchronous device I/O + * + * This page documents libusb's synchronous (blocking) API for USB device I/O. + * This interface is easy to use but has some limitations. More advanced users + * may wish to consider using the \ref asyncio "asynchronous I/O API" instead. + */ + +static void LIBUSB_CALL sync_transfer_cb(struct libusb_transfer *transfer) +{ + int *completed = transfer->user_data; + *completed = 1; + usbi_dbg("actual_length=%d", transfer->actual_length); + /* caller interprets result and frees transfer */ +} + +static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) +{ + int r, *completed = transfer->user_data; + struct libusb_context *ctx = HANDLE_CTX(transfer->dev_handle); + + while (!*completed) { + r = libusb_handle_events_completed(ctx, completed); + if (r < 0) { + if (r == LIBUSB_ERROR_INTERRUPTED) + continue; + usbi_err(ctx, "libusb_handle_events failed: %s, cancelling transfer and retrying", + libusb_error_name(r)); + libusb_cancel_transfer(transfer); + continue; + } + } +} + +/** \ingroup syncio + * Perform a USB control transfer. + * + * The direction of the transfer is inferred from the bmRequestType field of + * the setup packet. + * + * The wValue, wIndex and wLength fields values should be given in host-endian + * byte order. + * + * \param dev_handle a handle for the device to communicate with + * \param bmRequestType the request type field for the setup packet + * \param bRequest the request field for the setup packet + * \param wValue the value field for the setup packet + * \param wIndex the index field for the setup packet + * \param data a suitably-sized data buffer for either input or output + * (depending on direction bits within bmRequestType) + * \param wLength the length field for the setup packet. The data buffer should + * be at least this size. + * \param timeout timeout (in millseconds) that this function should wait + * before giving up due to no response being received. For an unlimited + * timeout, use value 0. + * \returns on success, the number of bytes actually transferred + * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out + * \returns LIBUSB_ERROR_PIPE if the control request was not supported by the + * device + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failures + */ +int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout) +{ + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + unsigned char *buffer; + int completed = 0; + int r; + + if (!transfer) + return LIBUSB_ERROR_NO_MEM; + + buffer = (unsigned char*) malloc(LIBUSB_CONTROL_SETUP_SIZE + wLength); + if (!buffer) { + libusb_free_transfer(transfer); + return LIBUSB_ERROR_NO_MEM; + } + + libusb_fill_control_setup(buffer, bmRequestType, bRequest, wValue, wIndex, + wLength); + if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) + memcpy(buffer + LIBUSB_CONTROL_SETUP_SIZE, data, wLength); + + libusb_fill_control_transfer(transfer, dev_handle, buffer, + sync_transfer_cb, &completed, timeout); + transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER; + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + return r; + } + + sync_transfer_wait_for_completion(transfer); + + if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) + memcpy(data, libusb_control_transfer_get_data(transfer), + transfer->actual_length); + + switch (transfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + r = transfer->actual_length; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + r = LIBUSB_ERROR_TIMEOUT; + break; + case LIBUSB_TRANSFER_STALL: + r = LIBUSB_ERROR_PIPE; + break; + case LIBUSB_TRANSFER_NO_DEVICE: + r = LIBUSB_ERROR_NO_DEVICE; + break; + case LIBUSB_TRANSFER_OVERFLOW: + r = LIBUSB_ERROR_OVERFLOW; + break; + case LIBUSB_TRANSFER_ERROR: + case LIBUSB_TRANSFER_CANCELLED: + r = LIBUSB_ERROR_IO; + break; + default: + usbi_warn(HANDLE_CTX(dev_handle), + "unrecognised status code %d", transfer->status); + r = LIBUSB_ERROR_OTHER; + } + + libusb_free_transfer(transfer); + return r; +} + +static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + int *transferred, unsigned int timeout, unsigned char type) +{ + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + int completed = 0; + int r; + + if (!transfer) + return LIBUSB_ERROR_NO_MEM; + + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length, + sync_transfer_cb, &completed, timeout); + transfer->type = type; + + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + return r; + } + + sync_transfer_wait_for_completion(transfer); + + *transferred = transfer->actual_length; + switch (transfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + r = 0; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + r = LIBUSB_ERROR_TIMEOUT; + break; + case LIBUSB_TRANSFER_STALL: + r = LIBUSB_ERROR_PIPE; + break; + case LIBUSB_TRANSFER_OVERFLOW: + r = LIBUSB_ERROR_OVERFLOW; + break; + case LIBUSB_TRANSFER_NO_DEVICE: + r = LIBUSB_ERROR_NO_DEVICE; + break; + case LIBUSB_TRANSFER_ERROR: + case LIBUSB_TRANSFER_CANCELLED: + r = LIBUSB_ERROR_IO; + break; + default: + usbi_warn(HANDLE_CTX(dev_handle), + "unrecognised status code %d", transfer->status); + r = LIBUSB_ERROR_OTHER; + } + + libusb_free_transfer(transfer); + return r; +} + +/** \ingroup syncio + * Perform a USB bulk transfer. The direction of the transfer is inferred from + * the direction bits of the endpoint address. + * + * For bulk reads, the length field indicates the maximum length of + * data you are expecting to receive. If less data arrives than expected, + * this function will return that data, so be sure to check the + * transferred output parameter. + * + * You should also check the transferred parameter for bulk writes. + * Not all of the data may have been written. + * + * Also check transferred when dealing with a timeout error code. + * libusb may have to split your transfer into a number of chunks to satisfy + * underlying O/S requirements, meaning that the timeout may expire after + * the first few chunks have completed. libusb is careful not to lose any data + * that may have been transferred; do not assume that timeout conditions + * indicate a complete lack of I/O. + * + * \param dev_handle a handle for the device to communicate with + * \param endpoint the address of a valid endpoint to communicate with + * \param data a suitably-sized data buffer for either input or output + * (depending on endpoint) + * \param length for bulk writes, the number of bytes from data to be sent. for + * bulk reads, the maximum number of bytes to receive into the data buffer. + * \param transferred output location for the number of bytes actually + * transferred. + * \param timeout timeout (in millseconds) that this function should wait + * before giving up due to no response being received. For an unlimited + * timeout, use value 0. + * + * \returns 0 on success (and populates transferred) + * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates + * transferred) + * \returns LIBUSB_ERROR_PIPE if the endpoint halted + * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see + * \ref packetoverflow + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failures + */ +int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, int *transferred, + unsigned int timeout) +{ + return do_sync_bulk_transfer(dev_handle, endpoint, data, length, + transferred, timeout, LIBUSB_TRANSFER_TYPE_BULK); +} + +/** \ingroup syncio + * Perform a USB interrupt transfer. The direction of the transfer is inferred + * from the direction bits of the endpoint address. + * + * For interrupt reads, the length field indicates the maximum length + * of data you are expecting to receive. If less data arrives than expected, + * this function will return that data, so be sure to check the + * transferred output parameter. + * + * You should also check the transferred parameter for interrupt + * writes. Not all of the data may have been written. + * + * Also check transferred when dealing with a timeout error code. + * libusb may have to split your transfer into a number of chunks to satisfy + * underlying O/S requirements, meaning that the timeout may expire after + * the first few chunks have completed. libusb is careful not to lose any data + * that may have been transferred; do not assume that timeout conditions + * indicate a complete lack of I/O. + * + * The default endpoint bInterval value is used as the polling interval. + * + * \param dev_handle a handle for the device to communicate with + * \param endpoint the address of a valid endpoint to communicate with + * \param data a suitably-sized data buffer for either input or output + * (depending on endpoint) + * \param length for bulk writes, the number of bytes from data to be sent. for + * bulk reads, the maximum number of bytes to receive into the data buffer. + * \param transferred output location for the number of bytes actually + * transferred. + * \param timeout timeout (in millseconds) that this function should wait + * before giving up due to no response being received. For an unlimited + * timeout, use value 0. + * + * \returns 0 on success (and populates transferred) + * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out + * \returns LIBUSB_ERROR_PIPE if the endpoint halted + * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see + * \ref packetoverflow + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other error + */ +int API_EXPORTED libusb_interrupt_transfer( + struct libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *data, int length, int *transferred, unsigned int timeout) +{ + return do_sync_bulk_transfer(dev_handle, endpoint, data, length, + transferred, timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT); +} diff --git a/e502/libusb-1.0/libusb-1.0/version.h b/e502/libusb-1.0/libusb-1.0/version.h new file mode 100644 index 0000000..3305765 --- /dev/null +++ b/e502/libusb-1.0/libusb-1.0/version.h @@ -0,0 +1,18 @@ +/* This file is parsed by m4 and windres and RC.EXE so please keep it simple. */ +#include "version_nano.h" +#ifndef LIBUSB_MAJOR +#define LIBUSB_MAJOR 1 +#endif +#ifndef LIBUSB_MINOR +#define LIBUSB_MINOR 0 +#endif +#ifndef LIBUSB_MICRO +#define LIBUSB_MICRO 19 +#endif +#ifndef LIBUSB_NANO +#define LIBUSB_NANO 0 +#endif +/* LIBUSB_RC is the release candidate suffix. Should normally be empty. */ +#ifndef LIBUSB_RC +#define LIBUSB_RC "" +#endif diff --git a/e502/libusb-1.0/libusb-1.0/version_nano.h b/e502/libusb-1.0/libusb-1.0/version_nano.h new file mode 100644 index 0000000..8c4063a --- /dev/null +++ b/e502/libusb-1.0/libusb-1.0/version_nano.h @@ -0,0 +1 @@ +#define LIBUSB_NANO 10903 diff --git a/e502/libusb-1.0/libusb.cmake b/e502/libusb-1.0/libusb.cmake new file mode 100644 index 0000000..6f76506 --- /dev/null +++ b/e502/libusb-1.0/libusb.cmake @@ -0,0 +1,95 @@ +# Файл для включения в проект на CMAKE. +# После включения будут установлены следующие перменные: +# LIBUSB_HEADERS - используемые заголовочные файлы +# LIBUSB_SOURCES - используемые файлы исходных кодов +# LIBUSB_INCLUDE_DIRS - директории включения заголовков +# LIBUSB_LIBS - используемые библиотеки + +cmake_policy(PUSH) + +cmake_minimum_required(VERSION 2.8.12) + + + + +set(LIBUSB_DIR ${CMAKE_CURRENT_LIST_DIR}) +set(LIBUSB_DIR_SRC ${LIBUSB_DIR}/libusb-1.0) + +set(LIBUSB_INCLUDE_DIRS ${LIBUSB_DIR}) + +set(LIBUSB_SOURCES + ${LIBUSB_DIR_SRC}/core.c + ${LIBUSB_DIR_SRC}/descriptor.c + ${LIBUSB_DIR_SRC}/hotplug.c + ${LIBUSB_DIR_SRC}/io.c + ${LIBUSB_DIR_SRC}/strerror.c + ${LIBUSB_DIR_SRC}/sync.c +) + +set(LIBUSB_HEADERS + ${LIBUSB_DIR_SRC}/libusb.h + ${LIBUSB_DIR_SRC}/libusbi.h + ${LIBUSB_DIR_SRC}/hotplug.h + ${LIBUSB_DIR_SRC}/version.h + ${LIBUSB_DIR_SRC}/version_nano.h +) + + +if(WIN32) + 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) + set(SOURCES ${SOURCES} + ${LIBUSB_DIR_SRC}/os/poll_windows.c + ${LIBUSB_DIR_SRC}/os/threads_windows.c + ${LIBUSB_DIR_SRC}/os/windows_usb.c + ) + set(HEADERS ${HEADERS} + ${LIBUSB_DIR_SRC}/os/poll_windows.h + ${LIBUSB_DIR_SRC}/os/threads_windows.h + ${LIBUSB_DIR_SRC}/os/windows_common.h + ) +else(WIN32) + message(FATAL_ERROR "unsupported os") +endif(WIN32) + + + +if(MSVC) + set(LIBUSB_INCLUDE_DIRS ${LIBUSB_INCLUDE_DIRS} ${LIBUSB_DIR_SRC}/msvc) + + #В зависимости от версии msvc файлы errno.h,stdint.h,inttypes.h могут присутсвовать + #или отсутствовать. При этом файлы из libusb могут конфликтовать с файлами из msvc. + #Поэтому проверяем каждый из этих файлов, и прописываем до него путь в поиске include, + #только в случае, если он не найден + include(CheckIncludeFile) + check_include_file(errno.h HAVE_ERRNO) + if (NOT ${HAVE_ERRNO}) + set(LIBUSB_INCLUDE_DIRS ${LIBUSB_INCLUDE_DIRS} ${LIBUSB_DIR_SRC}/errno) + endif (NOT ${HAVE_ERRNO}) + + check_include_file(stdint.h HAVE_STDINT) + if (NOT ${HAVE_STDINT}) + set(LIBUSB_INCLUDE_DIRS ${LIBUSB_INCLUDE_DIRS} ${LIBUSB_DIR_SRC}/stdint) + endif (NOT ${HAVE_STDINT}) + + check_include_file(inttypes.h HAVE_INTTYPES) + if (NOT ${HAVE_INTTYPES}) + set(LIBUSB_INCLUDE_DIRS ${LIBUSB_INCLUDE_DIRS} ${LIBUSB_DIR_SRC}/inttypes) + endif (NOT ${HAVE_INTTYPES}) + + set(LIBUSB_HEADERS ${LIBUSB_HEADERS} + ${LIBUSB_DIR_SRC}/msvc/config.h + ${LIBUSB_DIR_SRC}/msvc/missing.h + ${LIBUSB_DIR_SRC}/msvc/errno/errno.h + ${LIBUSB_DIR_SRC}/msvc/inttypes/inttypes.h + ${LIBUSB_DIR_SRC}/msvc/stdint/stdint.h + ) +else(MSVC) + message(FATAL_ERROR "unsupported compiler") +endif(MSVC) + + +cmake_policy(POP) diff --git a/e502/pas/e502api.pas b/e502/pas/e502api.pas new file mode 100644 index 0000000..cb4bdf2 --- /dev/null +++ b/e502/pas/e502api.pas @@ -0,0 +1,294 @@ +unit e502api; +interface +uses Windows, SysUtils, x502api; + + const + { } + E502_ETH_SVC_EVENT_NONE = 0; // + E502_ETH_SVC_EVENT_ADD = 1; // + E502_ETH_SVC_EVENT_REMOVE = 2; // + E502_ETH_SVC_EVENT_CHANGED = 3; // + + // . + type st_e502_eth_config_state = record + end; + type t_e502_eth_config_hnd = ^st_e502_eth_config_state; + + type t_e502_mac_addr = array[0..X502_MAC_ADDR_SIZE-1] of byte; + + // + type st_e502_eth_svc_browse_context = record + end; + type t_e502_eth_svc_browse_hnd = ^st_e502_eth_svc_browse_context; + // + type st_e502_eth_svc_record = record + end; + type t_e502_eth_svc_record_hnd = ^st_e502_eth_svc_record; + + // E502, USB + function E502_UsbGetSerialList(out serials: array of string; flags: LongWord; out devcnt: LongWord) : LongInt; overload; + function E502_UsbGetSerialList(out serials: array of string; flags: LongWord) : LongInt; overload; + // E502, USB, . + function E502_OpenUsb(hnd: t_x502_hnd; serial: string): LongInt; stdcall; + // E502 IP- + function E502_OpenByIpAddr(hnd : t_x502_hnd; ip_addr: LongWord; flags : LongWord; tout: LongWord) : LongInt; stdcall; + + // , E502 + function E502_UsbGetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord; out devcnt: LongWord) : LongInt; overload; + function E502_UsbGetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord) : LongInt; overload; + // IP- + function E502_MakeDevRecordByIpAddr(var devrec: t_x502_devrec; ip_addr: LongWord; flags : LongWord; tout: LongWord) : LongInt; stdcall; + // TCP- + function E502_EthDevRecordSetCmdPort(var devrec: t_x502_devrec; cmd_port: Word) : LongInt; stdcall; + // TCP- + function E502_EthDevRecordSetDataPort(var devrec: t_x502_devrec; data_port: Word) : LongInt; stdcall; + + // + function E502_MakeDevRecordByEthSvc(var devrec: t_x502_devrec; svc : t_e502_eth_svc_record_hnd; flags : LongWord; tout: LongWord) : LongInt; stdcall; + + + // IP- + function E502_GetIpAddr(hnd: t_x502_hnd; out ip_addr : LongWord) : LongInt; stdcall; + + + + // . + function E502_EthConfigCreate() : t_e502_eth_config_hnd; stdcall; + // . + function E502_EthConfigFree(cfg: t_e502_eth_config_hnd): LongInt; stdcall; + // + function E502_EthConfigRead(hnd: t_x502_hnd; cfg: t_e502_eth_config_hnd): LongInt; stdcall; + // + function E502_EthConfigWrite(hnd: t_x502_hnd; cfg: t_e502_eth_config_hnd; passwd : string): LongInt; stdcall; + // + function E502_EthConfigCopy(src_cfg: t_e502_eth_config_hnd; dst_cfg: t_e502_eth_config_hnd): LongInt; stdcall; + // , Ethernet + function E502_EthConfigGetEnabled(cfg: t_e502_eth_config_hnd; out en : LongBool): LongInt; stdcall; + // Ethernet + function E502_EthConfigSetEnabled(cfg: t_e502_eth_config_hnd; en : LongBool): LongInt; stdcall; + // , IP + function E502_EthConfigGetAutoIPEnabled(cfg: t_e502_eth_config_hnd; out en: LongBool): LongInt; stdcall; + // IP + function E502_EthConfigSetAutoIPEnabled(cfg: t_e502_eth_config_hnd; en: LongBool): LongInt; stdcall; + // , MAC- + function E502_EthConfigGetUserMACEnabled(cfg: t_e502_eth_config_hnd; out en: LongBool): LongInt; stdcall; + // , MAC- + function E502_EthConfigSetUserMACEnabled(cfg: t_e502_eth_config_hnd; en : LongBool): LongInt; stdcall; + // IP- + function E502_EthConfigGetIPv4Addr(cfg: t_e502_eth_config_hnd; out ip_addr : LongWord): LongInt; stdcall; + // IP- + function E502_EthConfigSetIPv4Addr(cfg: t_e502_eth_config_hnd; ip_addr: LongWord): LongInt; stdcall; + // + function E502_EthConfigGetIPv4Mask(cfg: t_e502_eth_config_hnd; out mask : LongWord): LongInt; stdcall; + // + function E502_EthConfigSetIPv4Mask(cfg: t_e502_eth_config_hnd; mask : LongWord): LongInt; stdcall; + // + function E502_EthConfigGetIPv4Gate(cfg: t_e502_eth_config_hnd; out gate: LongWord): LongInt; stdcall; + // + function E502_EthConfigSetIPv4Gate(cfg: t_e502_eth_config_hnd; gate: LongWord): LongInt; stdcall; + // MAC- + function E502_EthConfigGetUserMac(cfg: t_e502_eth_config_hnd; mac : t_e502_mac_addr): LongInt; stdcall; + // MAC- + function E502_EthConfigSetUserMac(cfg: t_e502_eth_config_hnd; mac: t_e502_mac_addr): LongInt; stdcall; + // MAC- + function E502_EthConfigGetFactoryMac(cfg: t_e502_eth_config_hnd; mac : t_e502_mac_addr): LongInt; stdcall; + // + function E502_EthConfigGetInstanceName(cfg: t_e502_eth_config_hnd; out name: string): LongInt; stdcall; + // + function E502_EthConfigSetInstanceName(cfg: t_e502_eth_config_hnd; const name: string): LongInt; stdcall; + // + function E502_EthConfigSetNewPassword(cfg: t_e502_eth_config_hnd; const new_passwd: string): LongInt; stdcall; + + // E502 + function E502_SwitchToBootloader(hnd: t_x502_hnd): LongInt; stdcall; + // + function E502_ReloadFPGA(hnd: t_x502_hnd): LongInt; stdcall; + // Cortex-M4. + function E502_CortexExecCmd(hnd: t_x502_hnd; cmd_code: LongWord; par: LongWord; + const snd_data : array of byte; snd_size : LongWord; + rcv_data : array of byte; rcv_size : LongWord; + tout: LongWord; out recvd_size: LongWord): LongInt; stdcall; + + // + function E502_EthSvcBrowseStart(out context : t_e502_eth_svc_browse_hnd; flags : LongWord): LongInt; stdcall; + // + function E502_EthSvcBrowseGetEvent(context : t_e502_eth_svc_browse_hnd; out svc: t_e502_eth_svc_record_hnd; out event: LongWord; out flags : LongWord; tout : LongWord): LongInt; stdcall; + // + function E502_EthSvcBrowseStop(context : t_e502_eth_svc_browse_hnd): LongInt; stdcall; + // + function E502_EthSvcRecordFree(svc : t_e502_eth_svc_record_hnd): LongInt; stdcall; + // + function E502_EthSvcRecordGetInstanceName(svc : t_e502_eth_svc_record_hnd; out name: string): LongInt; stdcall; + // + function E502_EthSvcRecordGetDevSerial(svc : t_e502_eth_svc_record_hnd; out serial : string): LongInt; stdcall; + // IP + function E502_EthSvcRecordResolveIPv4Addr(svc : t_e502_eth_svc_record_hnd; out addr :LongWord; tout : LongWord): LongInt; stdcall; + // , + function E502_EthSvcRecordIsSameInstance(svc1 : t_e502_eth_svc_record_hnd; svc2 : t_e502_eth_svc_record_hnd): LongInt; stdcall; + +implementation + + function _get_serials( ser_arr: p_x502_serial_array; size:LongWord; + flags:LongWord; out devcnt: LongWord) : LongInt; + stdcall; external 'e502api.dll' name 'E502_UsbGetSerialList'; + function _get_dev_records_list(out list; size:LongWord; + flags : LongWord; out devcnt: LongWord) : LongInt; + stdcall; external 'e502api.dll' name 'E502_UsbGetDevRecordsList'; + + function _open_usb(hnd: t_x502_hnd; serial: PAnsiChar) : LongInt; stdcall; external 'e502api.dll' name 'E502_OpenUsb'; + function E502_OpenByIpAddr(hnd : t_x502_hnd; ip_addr: LongWord; flags : LongWord; tout: LongWord) : LongInt; stdcall; external 'e502api.dll'; + function E502_MakeDevRecordByIpAddr(var devrec: t_x502_devrec; ip_addr: LongWord; flags : LongWord; tout: LongWord) : LongInt; stdcall; external 'e502api.dll'; + function E502_EthDevRecordSetCmdPort(var devrec: t_x502_devrec; cmd_port: Word) : LongInt; stdcall; external 'e502api.dll'; + function E502_EthDevRecordSetDataPort(var devrec: t_x502_devrec; data_port: Word) : LongInt; stdcall; external 'e502api.dll'; + function E502_MakeDevRecordByEthSvc(var devrec: t_x502_devrec; svc : t_e502_eth_svc_record_hnd; flags : LongWord; tout: LongWord) : LongInt; stdcall; external 'e502api.dll'; + function E502_GetIpAddr(hnd: t_x502_hnd; out ip_addr : LongWord) : LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigCreate() : t_e502_eth_config_hnd; stdcall; external 'e502api.dll'; + function E502_EthConfigFree(cfg: t_e502_eth_config_hnd): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigRead(hnd: t_x502_hnd; cfg: t_e502_eth_config_hnd): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigWrite(hnd: t_x502_hnd; cfg: t_e502_eth_config_hnd; passwd : string): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigCopy(src_cfg: t_e502_eth_config_hnd; dst_cfg: t_e502_eth_config_hnd): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetEnabled(cfg: t_e502_eth_config_hnd; out en : LongBool): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigSetEnabled(cfg: t_e502_eth_config_hnd; en : LongBool): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetAutoIPEnabled(cfg: t_e502_eth_config_hnd; out en: LongBool): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigSetAutoIPEnabled(cfg: t_e502_eth_config_hnd; en: LongBool): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetUserMACEnabled(cfg: t_e502_eth_config_hnd; out en: LongBool): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigSetUserMACEnabled(cfg: t_e502_eth_config_hnd; en : LongBool): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetIPv4Addr(cfg: t_e502_eth_config_hnd; out ip_addr : LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigSetIPv4Addr(cfg: t_e502_eth_config_hnd; ip_addr: LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetIPv4Mask(cfg: t_e502_eth_config_hnd; out mask : LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigSetIPv4Mask(cfg: t_e502_eth_config_hnd; mask : LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetIPv4Gate(cfg: t_e502_eth_config_hnd; out gate: LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigSetIPv4Gate(cfg: t_e502_eth_config_hnd; gate: LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetUserMac(cfg: t_e502_eth_config_hnd; mac : t_e502_mac_addr): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigSetUserMac(cfg: t_e502_eth_config_hnd; mac: t_e502_mac_addr): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetFactoryMac(cfg: t_e502_eth_config_hnd; mac : t_e502_mac_addr): LongInt; stdcall; external 'e502api.dll'; + + function _eth_config_get_instance_name(cfg: t_e502_eth_config_hnd; name: PAnsiChar): LongInt; stdcall; external 'e502api.dll' name 'E502_EthConfigGetInstanceName'; + function _eth_config_set_instance_name(cfg: t_e502_eth_config_hnd; name: PAnsiChar): LongInt; stdcall; external 'e502api.dll' name 'E502_EthConfigSetInstanceName'; + function _eth_config_set_new_password(cfg: t_e502_eth_config_hnd; new_passwd: PAnsiChar): LongInt; stdcall; external 'e502api.dll' name 'E502_EthConfigSetNewPassword'; + + function E502_SwitchToBootloader(hnd: t_x502_hnd): LongInt; stdcall; external 'e502api.dll'; + function E502_ReloadFPGA(hnd: t_x502_hnd): LongInt; stdcall; external 'e502api.dll'; + function _cortex_exec_cmd(hnd: t_x502_hnd; cmd_code: LongWord; par: LongWord; + const snd_data; snd_size : LongWord; + out rcv_data; rcv_size : LongWord; + tout: LongWord; out recvd_size: LongWord): LongInt; stdcall; external 'e502api.dll' name 'E502_CortexExecCmd'; + + function E502_EthSvcBrowseStart(out context : t_e502_eth_svc_browse_hnd; flags : LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthSvcBrowseGetEvent(context : t_e502_eth_svc_browse_hnd; out svc: t_e502_eth_svc_record_hnd; out event: LongWord; out flags : LongWord; tout : LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthSvcBrowseStop(context : t_e502_eth_svc_browse_hnd): LongInt; stdcall; external 'e502api.dll'; + function E502_EthSvcRecordFree(svc : t_e502_eth_svc_record_hnd): LongInt; stdcall; external 'e502api.dll'; + function _eth_svc_record_get_instance_name(svc : t_e502_eth_svc_record_hnd; name: PAnsiChar): LongInt; stdcall; external 'e502api.dll' name 'E502_EthSvcRecordGetInstanceName'; + function _eth_svc_record_get_dev_serial(svc : t_e502_eth_svc_record_hnd; serial : PAnsiChar): LongInt; stdcall; external 'e502api.dll' name 'E502_EthSvcRecordGetDevSerial'; + function E502_EthSvcRecordResolveIPv4Addr(svc : t_e502_eth_svc_record_hnd; out addr :LongWord; tout : LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthSvcRecordIsSameInstance(svc1 : t_e502_eth_svc_record_hnd; svc2 : t_e502_eth_svc_record_hnd): LongInt; stdcall; external 'e502api.dll'; + + + + function E502_UsbGetSerialList(out serials: array of string; flags: LongWord; out devcnt: LongWord) : LongInt; overload; + var + ser_arr : p_x502_serial_array; + res, i : LongInt; + begin + if (Length(serials) > 0) then + begin + ser_arr:=GetMemory(Length(serials)*X502_SERIAL_SIZE); + // + res := _get_serials(ser_arr, Length(serials), flags, devcnt); + if res >= 0 then + begin + // + for i:=0 to res-1 do + serials[i] := string(ser_arr[i]); + end; + // , + FreeMemory(ser_arr); + end + else + begin + res:= _get_serials(nil, 0, flags, devcnt); + end; + E502_UsbGetSerialList:=res; + end; + + function E502_UsbGetSerialList(out serials: array of string; flags: LongWord) : LongInt; overload; + begin + E502_UsbGetSerialList:= E502_UsbGetSerialList(serials, flags, PCardinal(nil)^); + end; + + function E502_UsbGetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord; out devcnt: LongWord) : LongInt; overload; + begin + E502_UsbGetDevRecordsList := _get_dev_records_list(list, Length(list), flags, devcnt); + end; + function E502_UsbGetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord) : LongInt; overload; + begin + E502_UsbGetDevRecordsList:= E502_UsbGetDevRecordsList(list, flags, PCardinal(nil)^); + end; + + function E502_OpenUsb(hnd: t_x502_hnd; serial: string) : LongInt; + begin + E502_OpenUsb:=_open_usb(hnd, PAnsiChar(AnsiString(serial))); + end; + + function E502_EthConfigGetInstanceName(cfg: t_e502_eth_config_hnd; out name: string): LongInt; stdcall; + var + strptr: PAnsiChar; + res: LongInt; + begin + strptr:=GetMemory(X502_INSTANCE_NAME_SIZE); + res:=_eth_config_get_instance_name(cfg, strptr); + if res = X502_ERR_OK then + name:=string(Utf8Decode(strptr)); + FreeMemory(strptr); + E502_EthConfigGetInstanceName:= res; + end; + + + function E502_EthConfigSetInstanceName(cfg: t_e502_eth_config_hnd; const name: string): LongInt; stdcall; + begin + E502_EthConfigSetInstanceName:=_eth_config_set_instance_name(cfg, PAnsiChar(Utf8Encode(AnsiString(name)))); + end; + + function E502_EthConfigSetNewPassword(cfg: t_e502_eth_config_hnd; const new_passwd: string): LongInt; stdcall; + begin + E502_EthConfigSetNewPassword:=_eth_config_set_new_password(cfg, PAnsiChar(AnsiString(new_passwd))); + end; + + function E502_CortexExecCmd(hnd: t_x502_hnd; cmd_code: LongWord; par: LongWord; + const snd_data : array of byte; snd_size : LongWord; + rcv_data : array of byte; rcv_size : LongWord; + tout: LongWord; out recvd_size: LongWord): LongInt; stdcall; + begin + if (LongWord(Length(snd_data)) < snd_size) or + (LongWord(Length(rcv_data)) < rcv_size) then + E502_CortexExecCmd := X502_ERR_INSUFFICIENT_ARRAY_SIZE + else + E502_CortexExecCmd:=_cortex_exec_cmd(hnd, cmd_code, par, snd_data, snd_size, rcv_data, rcv_size, tout, recvd_size); + end; + + function E502_EthSvcRecordGetInstanceName(svc : t_e502_eth_svc_record_hnd; out name: string): LongInt; stdcall; + var + strptr: PAnsiChar; + res: LongInt; + begin + strptr:=GetMemory(X502_INSTANCE_NAME_SIZE); + res:=_eth_svc_record_get_instance_name(svc, strptr); + if res = X502_ERR_OK then + name:=string(Utf8Decode(strptr)); + FreeMemory(strptr); + E502_EthSvcRecordGetInstanceName:= res; + end; + + function E502_EthSvcRecordGetDevSerial(svc : t_e502_eth_svc_record_hnd; out serial : string): LongInt; stdcall; + var + strptr: PAnsiChar; + res: LongInt; + begin + strptr:=GetMemory(X502_SERIAL_SIZE); + res:=_eth_svc_record_get_dev_serial(svc, strptr); + if res = X502_ERR_OK then + serial:=string(strptr); + FreeMemory(strptr); + E502_EthSvcRecordGetDevSerial:= res; + end; +end. diff --git a/l502/CMakeLists.txt b/l502/CMakeLists.txt new file mode 100644 index 0000000..56585fd --- /dev/null +++ b/l502/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 2.8.12) + +project(l502api C) +set(PROJECT_VARNAME_PREFIX L502API) + + + +include(${LTIMER_DIR}/ltimer.cmake) + +set(SOURCES + l502api.c + l502api_compat.c + l502api_eeprom.c + l502api_bf.c + ${LTIMER_SOURCES} + ) + +set(SETUP_HEADERS + l502api.h + l502api_compat.h) + +set(HEADERS + l502api_private.h + lpcie_ioctls.h + ${LTIMER_HEADERS}) + +set(LIBS + x502api + ${LTIMER_LIBS}) + +set(L502API_COMPILE_DEFINITIONS ${L502API_COMPILE_DEFINITIONS} ${LTIMER_DEFINITIONS}) + +if(WIN32) + #В Windows используем setupapi для нахождения устройства + set(LIBS ${LIBS} setupapi) + set(SOURCES ${SOURCES} win/l502_spec.c) +elseif(UNIX) + set(SOURCES ${SOURCES} linux/l502_spec.c) +endif(WIN32) + +include(${X502_LIBS_CMAKE_FILE}) + + + + + + + diff --git a/l502/l502_fpga_regs.h b/l502/l502_fpga_regs.h new file mode 100644 index 0000000..4a73c95 --- /dev/null +++ b/l502/l502_fpga_regs.h @@ -0,0 +1,212 @@ +#ifndef L5XX_REGS_H +#define L5XX_REGS_H + +#define L583_BF_ADDR_ENDPROG (0xFFFFFFFCUL) + + +#define L502_MAX_PAGES_CNT 252 + +#define L502_BF_SDRAM_SIZE (32UL*1024*1024) + +#define L502_BF_MEMADDR_CMD 0xFF800800 + + +#define L502_BF_CMD_READ 0x0001 +#define L502_BF_CMD_WRITE 0x0002 +#define L502_BF_CMD_HIRQ 0x0004 +#define L502_BF_CMD_HDMA_RST 0x0008 + + +/********************* Адреса регистров блока EEPROM *************************/ +#define L502_REGS_EEPROM_BLOCK 0x0100 + +#define L502_REGS_EEPROM_SET_RD_ADDR (L502_REGS_EEPROM_BLOCK + 0) +#define L502_REGS_EEPROM_RD_DWORD (L502_REGS_EEPROM_BLOCK + 1) +#define L502_REGS_EEPROM_RD_STATUS (L502_REGS_EEPROM_BLOCK + 2) +#define L502_REGS_EEPROM_WR_STATUS_EN (L502_REGS_EEPROM_BLOCK + 3) +#define L502_REGS_EEPROM_WR_EN (L502_REGS_EEPROM_BLOCK + 4) +#define L502_REGS_EEPROM_WR_DIS (L502_REGS_EEPROM_BLOCK + 5) +#define L502_REGS_EEPROM_WR_STATUS (L502_REGS_EEPROM_BLOCK + 6) +#define L502_REGS_EEPROM_ERASE_4K (L502_REGS_EEPROM_BLOCK + 7) +#define L502_REGS_EEPROM_ERASE_64K (L502_REGS_EEPROM_BLOCK + 8) +#define L502_REGS_EEPROM_WR_BYTE (L502_REGS_EEPROM_BLOCK + 9) +#define L502_REGS_EEPROM_HARD_WR_STATUS_EN (L502_REGS_EEPROM_BLOCK + 0xF) +#define L502_REGS_HARD_ID (L502_REGS_EEPROM_BLOCK + 0xA) +#define L502_REGS_JEDEC_RD_ID (L502_REGS_EEPROM_BLOCK + 0xB) + +/********************* Адреса регистров с отладочной информацией **************/ +#define L502_REGS_DBG_BLOCK 0x0140 +#define L502_REGS_DBG_EVENTS (L502_REGS_DBG_BLOCK + 0) +#define L502_REGS_DBG_LAST_ABORT_ADDR (L502_REGS_DBG_BLOCK + 8) +#define L502_REGS_DBG_LAST_NACK_ADDR (L502_REGS_DBG_BLOCK + 9) +#define L502_REGS_DBG_LINK_REPLAY_CNT (L502_REGS_DBG_BLOCK + 10) + +/********************* Адреса регистров блока IOHARD **************************/ +#define L502_REGS_IOHARD_BLOCK 0x0200 +//Адрес Control Table +#define L502_REGS_IOHARD_LTABLE (L502_REGS_IOHARD_BLOCK+0) +#define L502_REGS_IOHARD_LTABLE_MAX_SIZE 0x100 // Максимальный размер Control Table + +#define L502_REGS_IOHARD_LCH_CNT (L502_REGS_IOHARD_BLOCK+0x100) +#define L502_REGS_IOHARD_ADC_FREQ_DIV (L502_REGS_IOHARD_BLOCK+0x102) +#define L502_REGS_IOHARD_ADC_FRAME_DELAY (L502_REGS_IOHARD_BLOCK+0x104) +#define L502_REGS_IOHARD_DIGIN_FREQ_DIV (L502_REGS_IOHARD_BLOCK+0x106) +#define L502_REGS_IOHARD_IO_MODE (L502_REGS_IOHARD_BLOCK+0x108) +#define L502_REGS_IOHARD_GO_SYNC_IO (L502_REGS_IOHARD_BLOCK+0x10A) +#define L502_REGS_IOHARD_PRELOAD_ADC (L502_REGS_IOHARD_BLOCK+0x10C) +#define L502_REGS_IOHARD_ASYNC_OUT (L502_REGS_IOHARD_BLOCK+0x112) +#define L502_REGS_IOHARD_LED (L502_REGS_IOHARD_BLOCK+0x114) +#define L502_REGS_IOHARD_DIGIN_PULLUP (L502_REGS_IOHARD_BLOCK+0x116) +#define L502_REGS_IOHARD_OUTSWAP_BFCTL (L502_REGS_IOHARD_BLOCK+0x118) +#define L502_REGS_IOHARD_OUTSWAP_ERROR (L502_REGS_IOHARD_BLOCK+0x120) + + + +/********************* Адреса регистров блока IOARITH **************************/ +#define L502_REGS_IOARITH_BLOCK 0x0400 +#define L502_REGS_IOARITH_B10 L502_REGS_IOARITH_BLOCK +#define L502_REGS_IOARITH_B5 (L502_REGS_IOARITH_BLOCK+0x01) +#define L502_REGS_IOARITH_B2 (L502_REGS_IOARITH_BLOCK+0x02) +#define L502_REGS_IOARITH_B1 (L502_REGS_IOARITH_BLOCK+0x03) +#define L502_REGS_IOARITH_B05 (L502_REGS_IOARITH_BLOCK+0x04) +#define L502_REGS_IOARITH_B02 (L502_REGS_IOARITH_BLOCK+0x05) +#define L502_REGS_IOARITH_K10 (L502_REGS_IOARITH_BLOCK+0x08) +#define L502_REGS_IOARITH_K5 (L502_REGS_IOARITH_BLOCK+0x09) +#define L502_REGS_IOARITH_K2 (L502_REGS_IOARITH_BLOCK+0x0A) +#define L502_REGS_IOARITH_K1 (L502_REGS_IOARITH_BLOCK+0x0B) +#define L502_REGS_IOARITH_K05 (L502_REGS_IOARITH_BLOCK+0x0C) +#define L502_REGS_IOARITH_K02 (L502_REGS_IOARITH_BLOCK+0x0D) +#define L502_REGS_IOARITH_ADC_FREQ_DIV (L502_REGS_IOARITH_BLOCK+0x12) +#define L502_REGS_IOARITH_IN_STREAM_ENABLE (L502_REGS_IOARITH_BLOCK+0x19) +#define L502_REGS_IOARITH_DIN_ASYNC (L502_REGS_IOARITH_BLOCK+0x1A) + + +/********************* Адреса регистров блока управления BlackFin'ом **********/ +#define L502_REGS_BF_CTL_BLOCK 0 +#define L502_REGS_BF_CTL (L502_REGS_BF_CTL_BLOCK+0) +#define L502_REGS_BF_CMD (L502_REGS_BF_CTL_BLOCK+1) +#define L502_REGS_BF_STATUS (L502_REGS_BF_CTL_BLOCK+2) +#define L502_REGS_BF_IRQ (L502_REGS_BF_CTL_BLOCK+3) +#define L502_REGS_BF_IRQ_EN (L502_REGS_BF_CTL_BLOCK+4) +#define L502_REGS_BF_REQ_ADDR (L502_REGS_BF_CTL_BLOCK+5) +#define L502_REGS_BF_REQ_SIZE (L502_REGS_BF_CTL_BLOCK+6) +#define L502_REGS_BF_REQ_DATA (L502_REGS_BF_CTL_BLOCK+128) + +#define L502_BF_REQ_DATA_SIZE_MAX 128 +#define L502_BF_REQ_DATA_SIZE_MIN 8 + + +/********************* Адреса регистров блока DMA *****************************/ +#define L502_REGS_DMA_CTL_BLOCK 0x700 +#define L502_REGS_DMA_CAP (L502_REGS_DMA_CTL_BLOCK) +#define L502_REGS_DMA_EN (L502_REGS_DMA_CTL_BLOCK+1) +#define L502_REGS_DMA_DIS (L502_REGS_DMA_CTL_BLOCK+2) +#define L502_REGS_DMA_RST (L502_REGS_DMA_CTL_BLOCK+3) +#define L502_REGS_DMA_IRQ (L502_REGS_DMA_CTL_BLOCK+4) +#define L502_REGS_DMA_IRQ_EN (L502_REGS_DMA_CTL_BLOCK+5) +#define L502_REGS_DMA_IRQ_DIS (L502_REGS_DMA_CTL_BLOCK+6) + +#define L502_REGS_DMA_CH_PARAMS_SIZE (16 + L502_MAX_PAGES_CNT*4) +#define L502_DMA_CHNUM_IN 0 +#define L502_DMA_CHNUM_OUT 1 + +/* номер регистра, с которого начинаются параметры канала DMA */ +#define L502_REGS_DMA_CH_PARAMS(ch) (0x800 + L502_REGS_DMA_CH_PARAMS_SIZE*ch) +#define L502_REGS_DMA_CH_CTL(ch) (L502_REGS_DMA_CH_PARAMS(ch) + 0) +#define L502_REGS_DMA_CH_CMP_CNTR(ch) (L502_REGS_DMA_CH_PARAMS(ch) + 1) +#define L502_REGS_DMA_CH_CUR_CNTR(ch) (L502_REGS_DMA_CH_PARAMS(ch) + 2) +#define L502_REGS_DMA_CH_CUR_POS(ch) (L502_REGS_DMA_CH_PARAMS(ch) + 3) +#define L502_REGS_DMA_CH_PC_POS(ch) (L502_REGS_DMA_CH_PARAMS(ch) + 4) + + +/* адреса для регистров страниц DMA АЦП */ +#define L502_REGS_DMA_CH_PAGES(ch) (L502_REGS_DMA_CH_PARAMS(ch) + 16) +#define L502_REGS_DMA_CH_PAGE_ADDRL(ch,n) (L502_REGS_DMA_CH_PAGES(ch) + 4*(n)) +#define L502_REGS_DMA_CH_PAGE_ADDRH(ch,n) (L502_REGS_DMA_CH_PAGES(ch) + 4*(n)+1) +#define L502_REGS_DMA_CH_PAGE_LEN(ch,n) (L502_REGS_DMA_CH_PAGES(ch) + 4*(n)+2) + + + + + +#define L502_REGBIT_BF_STATUS_HWAIT_Pos 0 +#define L502_REGBIT_BF_STATUS_HWAIT_Msk (1UL << L502_REGBIT_BF_STATUS_HWAIT_Pos) + +#define L502_REGBIT_BF_STATUS_BUSY_Pos 1 +#define L502_REGBIT_BF_STATUS_BUSY_Msk (1UL << L502_REGBIT_BF_STATUS_BUSY_Pos) + + +/* описание отдельных битов регистров */ +#define L502_REGBIT_DMA_CTL_PACK_SIZE_Pos 0 +#define L502_REGBIT_DMA_CTL_PACK_SIZE_Msk (0xFFUL << L502_REGBIT_DMA_CTL_PACK_SIZE_Pos) + +#define L502_REGBIT_DMA_CTL_PAGE_CNT_Pos 16 +#define L502_REGBIT_DMA_CTL_PAGE_CNT_Msk (0xFFUL << L502_REGBIT_DMA_CTL_PAGE_CNT_Pos) + +#define L502_REGBIT_DMA_CTL_AUTOSTOP_Pos 31 +#define L502_REGBIT_DMA_CTL_AUTOSTOP_Msk (0x1UL << L502_REGBIT_DMA_CTL_AUTOSTOP_Pos) + +#define L502_REGBIT_DMA_CTL_PC_WAIT_Pos 30 +#define L502_REGBIT_DMA_CTL_PC_WAIT_Msk (0x1UL << L502_REGBIT_DMA_CTL_PC_WAIT_Pos) + +#define L502_REGBIT_DMA_CH_ADC_Pos 0 +#define L502_REGBIT_DMA_CH_ADC_Msk (0x1UL << L502_REGBIT_DMA_CH_ADC_Pos) + +#define L502_REGBIT_DMA_CH_DAC_Pos 1 +#define L502_REGBIT_DMA_CH_DAC_Msk (0x1UL << L502_REGBIT_DMA_CH_DAC_Pos) + + + +#define L502_REGBIT_BF_CTL_BF_RESET_Pos 1 +#define L502_REGBIT_BF_CTL_BF_RESET_Msk (0x1UL << L502_REGBIT_BF_CTL_BF_RESET_Pos) + + +#define L502_REGBIT_BF_CTL_HOST_WAIT_Pos 3 +#define L502_REGBIT_BF_CTL_HOST_WAIT_Msk (0x1UL << L502_REGBIT_BF_CTL_HOST_WAIT_Pos) + +#define L502_REGBIT_BF_CTL_DSP_MODE_Pos 4 +#define L502_REGBIT_BF_CTL_DSP_MODE_Msk (0x1UL << L502_REGBIT_BF_CTL_DSP_MODE_Pos) + +#define L502_REGBIT_BF_CTL_DBG_MODE_Pos 5 +#define L502_REGBIT_BF_CTL_DBG_MODE_Msk (0x1UL << L502_REGBIT_BF_CTL_DBG_MODE_Pos) + +#define L502_REGBIT_BF_CTL_CLK_DIV_Pos 8 +#define L502_REGBIT_BF_CTL_CLK_DIV_Msk (0xFUL << L502_REGBIT_BF_CTL_CLK_DIV_Pos) + +#define L502_REGBIT_DMA_CURPOS_PAGE_Pos 24 +#define L502_REGBIT_DMA_CURPOS_PAGE_Msk (0xFFUL << L502_REGBIT_DMA_CURPOS_PAGE_Pos) + +#define L502_REGBIT_DMA_CURPOS_OFFSET_Pos 0 +#define L502_REGBIT_DMA_CURPOS_OFFSET_Msk (0xFFFFFFUL << L502_REGBIT_DMA_CURPOS_OFFSET_Pos) + +#define L502_REGBIT_ADC_SLV_CLK_LOCK_Pos 31 +#define L502_REGBIT_ADC_SLV_CLK_LOCK_Msk (0x1UL << L502_REGBIT_ADC_SLV_CLK_LOCK_Pos) + + + +#define L502_REGBIT_IOHARD_OUT_SWAP_Pos 0 +#define L502_REGBIT_IOHARD_OUT_SWAP_Msk (0x1UL << L502_REGBIT_IOHARD_OUT_SWAP_Pos) + +#define L502_REGBIT_IOHARD_OUT_TFS_EN_Pos 1 +#define L502_REGBIT_IOHARD_OUT_TFS_EN_Msk (0x1UL << L502_REGBIT_IOHARD_OUT_TFS_EN_Pos) + +#define L502_REGBIT_IOHARD_OUT_RING_Pos 2 +#define L502_REGBIT_IOHARD_OUT_RING_Msk (0x1UL << L502_REGBIT_IOHARD_OUT_RING_Pos) + +#define L502_REGBIT_IOHARD_OUT_RFS_EN_Pos 3 +#define L502_REGBIT_IOHARD_OUT_RFS_EN_Msk (0x1UL << L502_REGBIT_IOHARD_OUT_RFS_EN_Pos) + + + + + + + + +#define L502_REGBIT_DMA_IRQ_STEP_Msk(ch) (1UL << ch) +#define L502_REGBIT_DMA_IRQ_PAGE_Msk(ch) (1UL << (ch+8)) +#define L502_REGBIT_DMA_IRQ_FLUSH_Msk(ch) (1UL << (ch+16)) + + + +#endif // L5XX_REGS_H diff --git a/l502/l502api.c b/l502/l502api.c new file mode 100644 index 0000000..e7086a2 --- /dev/null +++ b/l502/l502api.c @@ -0,0 +1,164 @@ +#include "l502api.h" +#include "l502api_private.h" +#include "l502_fpga_regs.h" +#include +#include +#include + +/* минимальный размер внутреннего буфера */ +#define L502_DMA_IN_BUF_SIZE_MIN 16*1024 + + +#ifdef _WIN32 +BOOL WINAPI DllMain(HINSTANCE hmod, DWORD reason, LPVOID resvd) { + switch (reason) { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +#endif + +static int32_t f_iface_free_devinfo_ptr(t_x502_devrec_inptr *devinfo_ptr); +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_cycle_load_start(t_x502_hnd hnd, uint32_t size); +static int32_t f_iface_cycle_setup(t_x502_hnd hnd, uint32_t flags); +static int32_t f_iface_cycle_stop(t_x502_hnd hnd, uint32_t flags); +static int32_t f_iface_cycle_check_setup(t_x502_hnd hnd, uint32_t *done); +static int32_t f_iface_check_feature(t_x502_hnd hnd, uint32_t feature); + + +static const t_x502_dev_iface f_pcie_iface = { + L502_REGS_HARD_ID, + L502_DMA_IN_BUF_SIZE_MIN, + 0, + L502_BF_REQ_DATA_SIZE_MAX, + 4, //flash rd size + 1, //flash wr size + f_iface_free_devinfo_ptr, + l502_port_open, + l502_port_close, + l502_port_fpga_reg_read, + l502_port_fpga_reg_write, + f_iface_stream_cfg, + l502_port_stream_start, + l502_port_stream_stop, + l502_port_stream_free, + NULL, + l502_port_stream_read, + l502_port_stream_write, + l502_port_stream_rdy_size, + l502_iface_bf_mem_block_rd, + l502_iface_bf_mem_block_wr, + l502_iface_bf_firm_load, + l502_iface_flash_rd, + l502_iface_flash_wr, + l502_iface_flash_erase, + l502_iface_flash_set_prot, + l502_port_renew_info, + f_iface_cycle_load_start, + f_iface_cycle_setup, + f_iface_cycle_stop, + f_iface_cycle_check_setup, + NULL, + NULL, + f_iface_check_feature +}; + + +X502_EXPORT(int32_t) L502_GetDriverVersion(t_x502_hnd hnd, uint32_t* ver) { + int32_t err = X502_CHECK_HND(hnd); + if (err == X502_ERR_OK) + err = l502_port_get_drv_ver(hnd, ver); + return err; +} + +int32_t l502_devlist_gen(t_x502_devrec *info, void *iface_data) { + 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; + } else { + devinfo_ptr->iface_data = iface_data; + devinfo_ptr->iface = &f_pcie_iface; + info->iface = X502_IFACE_PCI; + info->flags |= X502_DEVFLAGS_IFACE_SUPPORT_PCI | X502_DEVFLAGS_FPGA_LOADED; + info->internal = devinfo_ptr; + } + return err; +} + +X502_EXPORT(int32_t) L502_GetSerialList(char serials[][X502_SERIAL_SIZE], uint32_t size, + uint32_t flags, uint32_t *devcnt) { + return X502_GetSerialList(serials, size, flags, devcnt, L502_DEVICE_NAME, L502_GetDevRecordsList); +} + +X502_EXPORT(int32_t) L502_Open(t_x502_hnd hnd, const char *serial) { + return X502_Open(hnd, serial, L502_DEVICE_NAME, L502_GetDevRecordsList); +} + + +static int32_t f_iface_free_devinfo_ptr(t_x502_devrec_inptr *devinfo_ptr) { + l502_port_free_iface_data(devinfo_ptr->iface_data); + free(devinfo_ptr); + return X502_ERR_OK; +} + +static int32_t f_iface_stream_cfg(t_x502_hnd hnd, uint32_t ch, t_x502_stream_ch_params *params) { + t_lpcie_stream_ch_params lpcie_ch_params; + memset(&lpcie_ch_params, 0, sizeof(lpcie_ch_params)); + lpcie_ch_params.ch = ch; + lpcie_ch_params.irq_step = params->step; + lpcie_ch_params.buf_size = params->buf_size; + return l502_port_stream_set_params(hnd, &lpcie_ch_params); +} + +static int32_t f_iface_cycle_load_start(t_x502_hnd hnd, uint32_t size) { + uint32_t irq_step = STREAM_OUT_IRQ_STEP(hnd); + return l502_port_cycle_load_start(hnd, L502_DMA_CHNUM_OUT, size, irq_step); +} +static int32_t f_iface_cycle_setup(t_x502_hnd hnd, uint32_t flags) { + return l502_port_cycle_setup(hnd, L502_DMA_CHNUM_OUT, (flags & X502_OUT_CYCLE_FLAGS_FORCE) ? + LPCIE_CYCLE_SW_EVT_IMMIDIATLY : LPCIE_CYCLE_SW_EVT_END_OF_CYCLE); +} + +static int32_t f_iface_cycle_stop(t_x502_hnd hnd, uint32_t flags) { + return l502_port_cycle_stop(hnd, L502_DMA_CHNUM_OUT, (flags & X502_OUT_CYCLE_FLAGS_FORCE) ? + LPCIE_CYCLE_SW_EVT_IMMIDIATLY : LPCIE_CYCLE_SW_EVT_END_OF_CYCLE); +} + +static int32_t f_iface_cycle_check_setup(t_x502_hnd hnd, uint32_t *done) { + uint32_t ver; + int32_t err = L502_GetDriverVersion(hnd, &ver); + if ((err == X502_ERR_OK) && !LPCIE_IOCTL_SUPPORT_CYCLE_CHECK_SETUP(ver)) + err = X502_ERR_NOT_SUP_BY_DRIVER; + if (err == X502_ERR_OK) + err = l502_port_cycle_check_setup(hnd, L502_DMA_CHNUM_OUT, done); + + if (err == X502_ERR_OK) { + /* за счет буфера в плате на вывод может пройти несколько мс после + * передачи в модуль данных до того как реально эти данные появятся + * на выходе. т.к. это отследить явно нельзя, то приходится ставить задержку */ + SLEEP_MS(3); + } + + return err; +} + +static int32_t f_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: + if (hnd->info.fpga_ver >= 0x5) + err = X502_ERR_OK; + break; + default: + err = X502_ERR_UNKNOWN_FEATURE_CODE; + break; + } + return err; +} diff --git a/l502/l502api.def b/l502/l502api.def new file mode 100644 index 0000000..56e8121 --- /dev/null +++ b/l502/l502api.def @@ -0,0 +1,72 @@ +LIBRARY l502api.dll + +EXPORTS + L502_Create + L502_Free + L502_Open + L502_Close + L502_GetSerialList + L502_GetDevInfo + L502_Configure + L502_StreamsEnable + L502_StreamsDisable + L502_StreamsStart + L502_StreamsStop + L502_IsRunning + L502_Recv + L502_Send + L502_GetRecvReadyCount + L502_GetSendReadyCount + L502_SetDmaBufSize + L502_SetDmaIrqStep + L502_GetNextExpectedLchNum + L502_PreloadStart + L502_ProcessAdcData + L502_ProcessData + L502_ProcessDataWithUserExt + L502_PrepareData + L502_SetLChannel + L502_SetLChannelCount + L502_GetLChannelCount + L502_SetAdcFreqDivider + L502_SetAdcInterframeDelay + L502_SetDinFreqDivider + L502_SetAdcFreq + L502_SetDinFreq + L502_GetAdcFreq + L502_SetRefFreq + L502_SetSyncMode + L502_SetSyncStartMode + L502_SetMode + L502_GetMode + L502_SetAdcCoef + L502_GetAdcCoef + L502_SetDacCoef + L502_GetDacCoef + L502_AsyncOutDac + L502_AsyncOutDig + L502_AsyncInDig + L502_AsyncGetAdcFrame + L502_BfCheckFirmwareIsLoaded + L502_BfLoadFirmware + L502_BfMemRead + L502_BfMemWrite + L502_BfExecCmd + L502_FlashRead + L502_FlashWrite + L502_FlashErase + L502_FlashWriteEnable + L502_FlashWriteDisable + L502_FpgaRegWrite + L502_FpgaRegRead + L502_GetDllVersion + L502_GetDriverVersion + L502_GetErrorString + L502_LedBlink + L502_SetDigInPullup + L502_ReloadDevInfo + L502_OutCycleLoadStart + L502_OutCycleSetup + L502_OutCycleStop + L502_GetDevRecordsList + diff --git a/l502/l502api.h b/l502/l502api.h new file mode 100644 index 0000000..a7d2137 --- /dev/null +++ b/l502/l502api.h @@ -0,0 +1,162 @@ +/***************************************************************************//** + @file l502api.h + Файл содержит все необходимые описания типов, констант и функций для работы + с модулем L-502 из пользовательской программы. + @date 11.03.2012 + @author Borisov Alexey + ******************************************************************************/ + +#ifndef L502_API_H +#define L502_API_H + +#include "l502api_compat.h" +#include "x502api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + @addtogroup func_open + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Получение списка серийных номеров модулей L-502 + + Функция возвращает список номеров всех найденных модулей L-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, то в данную переменную сохраняется + общее число найденных модулей L502 + (может быть больше size). + @return Если <0 - код ошибки, иначе количество сохраненных + серийных номеров в массиве serials (всегда <= size) +*******************************************************************************/ +X502_EXPORT(int32_t) L502_GetSerialList(char serials[][X502_SERIAL_SIZE], uint32_t size, + uint32_t flags, uint32_t *devcnt); + +/***************************************************************************//** + @brief Открытие модуля L-502 по его серийному номеру + + Функция устанавливает связь с модулем L-502 по его серийному номеру. + После успешного выполнения этой функции, пользователь получает эксклюзивный + доступ к модулю через описатель модуля. До закрытия связи с помощью + X502_Close() никто другой установить связь с модулем не сможет + (будет возвращена ошибка #X502_ERR_DEVICE_ACCESS_DENIED). + + Если в качестве серийного номера передан NULL или пустая строка, то будет + установлена связь с первым найденным модулем, с которым получится успешно + ее установить. + Если в системе нет ни одного модуля, то будет возвращена ошибка + #X502_ERR_DEVICE_NOT_FOUND. Если в системе присутствуют модули L-502, но + соединение ни с одним из них установить не удалось, то будет возвращена + ошибка, полученная при попытке установить соединение с последним + найденным модулем. + + После завершения работы с устройством соединение должно быть закрыто с + помощью X502_Close(). + + @param[in] hnd Описатель устройства. + @param[in] serial Указатель на строку с серийным номером открываемого + модуля или NULL. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) L502_Open(t_x502_hnd hnd, const char *serial); + + +/** @} */ + + +/***************************************************************************//** + @addtogroup func_devrec + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Получить список записей, соответствующих подключенным модулям L502 + + Функция находит все подключенные модули L-502 и инициализирует + записи о каждом найденном устройстве и сохраняет их в переданный список + (если не нулевой). + Возвращенные в списке записи должны быть очищены после использования + с помощью X502_FreeDevRecordList() (также в случае повторного + вызов L502_GetDevRecordsList() с тем же массивом записей, записи, полученные + при предыдущем вызове, должны быть сперва очищены). + + @param[in] list Массив для сохранения записей о найденных устройствах. + Должен содержать место для сохранения не менее size записей. + Может быть NULL, если size=0, а devcnt!=NULL, в случае, + если нужно только получить количество модулей в системе. + @param[in] size Определяет, сколько максимально записей может + быть сохранено в массив list. Будут сохранены только + первые size записей, если устройств найденно больше. + @param[in] flags Флаги из #t_x502_getdevs_flags, определяющие поведение + функции. + @param[out] devcnt Если не нулевой указатель, то в данную переменную сохраняется + общее число найденных модулей L-502 (может быть больше size). + @return Если <0 --- код ошибки, иначе количество сохраненных + записей о найденных устройствах (всегда <= size). + Именно на этот размер нужно сделать в дальнейшем + X502_FreeDevRecordList() для освобождения памяти, + выделенной под информацию, на которую ссылается запись. + ******************************************************************************/ +X502_EXPORT(int32_t) L502_GetDevRecordsList(t_x502_devrec *list, uint32_t size, + uint32_t flags, uint32_t *devcnt) ; +/** @} */ + + + + +/***************************************************************************//** + @addtogroup func_misc + @{ +*******************************************************************************/ + +/**************************************************************************//** + @brief Получить версию драйвера модуля L-502 + + Функция возвращает версию драйвера, установленного для + указанного открытого устройства. + Версия возвращается в виде 32-битного числа. + Строковое представление возвращенной версии - четыре числа, + старшее соответствует старшему байту, младшее - младшему. + + Старший байт - мажорная версия, второй по старшинству байт - минорная, + третий - ревизия, четвертый - номер сборки (не используется - всегда 0). + + Это та версия, которая отображается в диспетчере устройств в Windows или + с помощью modinfo в Linux. + + Данная функция доступна только для устройств с интерфейсом PCI/PCI-Express (L502) + + @param[in] hnd Описатель модуля. + @param[out] ver 32-битное число, представляющее собой версию драйвера + @return Код ошибки. + *****************************************************************************/ +X502_EXPORT(int32_t) L502_GetDriverVersion(t_x502_hnd hnd, uint32_t* ver); + +/** @} */ + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/l502/l502api.rc.in b/l502/l502api.rc.in new file mode 100644 index 0000000..e39862f --- /dev/null +++ b/l502/l502api.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 L502" + VALUE "FileVersion", "@X502API_VERSION@" + VALUE "OriginalFilename", "l502api.dll" + VALUE "ProductName", "l502api" + VALUE "ProductVersion", "@X502API_VERSION@" + VALUE "LegalCopyright", "© 2015 L-Card Ltd." + END + + BLOCK "04190000" + BEGIN + VALUE "CompanyName", "Л Кард" + VALUE "FileDescription", "Библиотека для работы с платой L502" + VALUE "FileVersion", "@X502API_VERSION@" + VALUE "OriginalFilename", "l502api.dll" + VALUE "ProductName", "l502api" + 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/l502/l502api_bf.c b/l502/l502api_bf.c new file mode 100644 index 0000000..86b9b9d --- /dev/null +++ b/l502/l502api_bf.c @@ -0,0 +1,272 @@ +#include "l502api_private.h" +#include "ltimer.h" +#include "l502_fpga_regs.h" +#include +#include + +#define BF_LDR_HDR_SIZE (16) +#define BF_LDR_HDRSGN (0xAD) + +#define BF_LDR_HDRPOS_SGN (3) + +#define BF_LDR_FLAG_SAVE (0x0010) //не используется +#define BF_LDR_FLAG_AUX (0x0020) //не используется +#define BF_LDR_FLAG_FILL (0x0100) +#define BF_LDR_FLAG_QUICKBOOT (0x0200) //не используется +#define BF_LDR_FLAG_CALLBACK (0x0400) //не используется +#define BF_LDR_FLAG_INIT (0x0800) //не используется +#define BF_LDR_FLAG_IGNORE (0x1000) +#define BF_LDR_FLAG_INDIRECT (0x2000) //не используется +#define BF_LDR_FLAG_FIRST (0x4000) +#define BF_LDR_FLAG_FINAL (0x8000) + +#define L502_BF_WAIT_LOAD_RDY_TOUT 500 + +#define LDR_BUFF_SIZE 4096 + +#define BF_CHECK_ADDR(addr) (((addr) < 0xFFA0C000) && ((addr)>= 0xFFA0000)) || \ + (((addr) < 0xFF908000) && ((addr) >=0xFF900000)) || \ + (((addr) < 0xFF808000) && ((addr) >=0xFF800000)) || \ + (((addr) < 0x2000000)) ? 0 : X502_ERR_BF_INVALID_ADDR + +#define BF_CHECK_ADDR_SIZE(addr, size) BF_CHECK_ADDR(addr) ? X502_ERR_BF_INVALID_ADDR : \ + BF_CHECK_ADDR(addr+size*4-1) ? X502_ERR_BF_INVALID_ADDR : 0 + + +typedef struct st_bf_ldr_pkt { + uint8_t res; + uint8_t dma_mode; + uint16_t flags; + uint32_t addr; + uint32_t size; + uint32_t arg; +} t_bf_ldr_pkt; + +/* Разбираем заголовок блока LDR-формата из буфера размером BF_LDR_HDR_SIZE + и сохраняем параметры в структуре pkt */ +int32_t f_parse_ldr_hdr(const uint8_t *hdr, t_bf_ldr_pkt *pkt) { + int32_t err = X502_ERR_OK; + uint32_t* pdw_buff = (uint32_t*)hdr; + uint8_t xor_ch = 0; + int i; + for (i=0; i < BF_LDR_HDR_SIZE; i++) { + xor_ch ^= hdr[i]; + } + + if ((xor_ch!=0) || (hdr[BF_LDR_HDRPOS_SGN] != BF_LDR_HDRSGN)) { + err = X502_ERR_LDR_FILE_FORMAT; + } else { + pkt->res = 0; + pkt->dma_mode = pdw_buff[0]&0xF; + pkt->flags = pdw_buff[0]&0xFFF0; + pkt->addr = pdw_buff[1]; + pkt->size = pdw_buff[2]; + pkt->arg = pdw_buff[3]; + + if ((pkt->flags & BF_LDR_FLAG_INIT) && (pkt->flags & BF_LDR_FLAG_FILL)) { + err = X502_ERR_LDR_FILE_FORMAT; + } else if (pkt->flags & (BF_LDR_FLAG_CALLBACK | BF_LDR_FLAG_INDIRECT | BF_LDR_FLAG_INIT)) { + err = X502_ERR_LDR_FILE_UNSUP_FEATURE; + } else if ((pkt->flags & BF_LDR_FLAG_INIT) && (pkt->addr != 0xFFA00000)) { + err = X502_ERR_LDR_FILE_UNSUP_STARTUP_ADDR; + } + } + return err; +} + +static int32_t f_bf_wait_cmd_done(t_x502_hnd hnd) { + int32_t err = X502_ERR_OK; + t_ltimer tmr; + uint32_t status; + ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(X502_BF_REQ_TOUT)); + do { + err = l502_port_fpga_reg_read(hnd, L502_REGS_BF_STATUS, &status); + } while ((status & L502_REGBIT_BF_STATUS_BUSY_Msk) && + (err == X502_ERR_OK) && !ltimer_expired(&tmr)); + + if (!err && (status & L502_REGBIT_BF_STATUS_BUSY_Msk)) + err = X502_ERR_BF_REQ_TIMEOUT; + return err; +} + + + +int32_t l502_iface_bf_mem_block_rd(t_x502_hnd hnd, uint32_t addr, uint32_t *block, uint32_t size){ + uint32_t i; + int32_t err = f_bf_wait_cmd_done(hnd); + /* записываем переметры передачи - размер и адрес в памяти BlackFin */ + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_REQ_SIZE, size); + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_REQ_ADDR, addr); + /* даем команду на запис */ + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_CMD, L502_BF_CMD_READ); + + /* ждем, пока операция не будет завершена */ + if (err == X502_ERR_OK) + err = f_bf_wait_cmd_done(hnd); + + /* записываем блок данных в буфер ПЛИС */ + for (i=0; (i < size) && (err == X502_ERR_OK); i++) { + err = l502_port_fpga_reg_read(hnd, L502_REGS_BF_REQ_DATA+i, &block[i]); + } + return err; +} + +int32_t l502_iface_bf_mem_block_wr(t_x502_hnd hnd, uint32_t addr, const uint32_t *block, uint32_t size) { + uint32_t i; + int32_t err = f_bf_wait_cmd_done(hnd); + if (err == X502_ERR_OK) { + /* записываем блок данных в буфер ПЛИС */ + for (i=0; (i < size) && (err == X502_ERR_OK); i++) { + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_REQ_DATA+i, block[i]); + } + + /* записываем переметры передачи - размер и адрес в памяти BlackFin */ + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_REQ_SIZE, size); + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_REQ_ADDR, addr); + /* даем команду на запис */ + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_CMD, L502_BF_CMD_WRITE); + + /* ждем, пока операция не будет завершена */ + if (err == X502_ERR_OK) + err = f_bf_wait_cmd_done(hnd); + } + return err; +} + + +static int32_t f_bf_mem_wr(t_x502_hnd hnd, uint32_t addr, const uint32_t* regs, uint32_t size) { + int32_t err = X502_ERR_OK; + + /* данные записываем блоками по L502_BF_REQ_DATA_SIZE */ + while ((err == X502_ERR_OK) && size) { + int put_size = (size < hnd->iface_hnd->bf_mem_block_size) ? size : + hnd->iface_hnd->bf_mem_block_size; + err = hnd->iface_hnd->bf_mem_block_wr(hnd, addr, regs, put_size); + if (!err) { + size -= put_size; + regs += put_size; + addr += put_size*4; + } + } + return err; +} + +int32_t l502_iface_bf_firm_load(t_x502_hnd hnd, const char *filename) { + int32_t err = X502_ERR_OK; + FILE* ldr_file=fopen(filename, "rb"); + if (ldr_file==NULL) { + err = X502_ERR_LDR_FILE_OPEN; + } else { + int32_t next_err = X502_ERR_OK; + uint32_t *ldr_buff = NULL; + ldr_buff = malloc(LDR_BUFF_SIZE); + if (ldr_buff == NULL) + err = X502_ERR_MEMORY_ALLOC; + + if (err == X502_ERR_OK) { + int rd_size = 0; + int stop = 0; + uint32_t reg; + uint8_t hdr[BF_LDR_HDR_SIZE]; + t_ltimer tmr; + + //uint32_t* pdw = (uint32_t*)ldr_buff; + t_bf_ldr_pkt pkt, pkt_next; + uint32_t bf_val = 0; + memset(&pkt_next, 0, sizeof(pkt_next)); + + l502_port_fpga_reg_read(hnd, L502_REGS_BF_CTL, &bf_val); + l502_port_fpga_reg_write(hnd, L502_REGS_BF_CTL, L502_REGBIT_BF_CTL_DSP_MODE_Msk + | (bf_val & 0xF00)); //set rst + SLEEP_MS(1); + l502_port_fpga_reg_write(hnd, L502_REGS_BF_CTL, L502_REGBIT_BF_CTL_DSP_MODE_Msk | + L502_REGBIT_BF_CTL_BF_RESET_Msk | (bf_val & 0xF00)); //release rst + + ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(L502_BF_WAIT_LOAD_RDY_TOUT)); + do { + l502_port_fpga_reg_read(hnd, L502_REGS_BF_CTL, ®); + if ((reg & L502_REGBIT_BF_CTL_HOST_WAIT_Msk) && ltimer_expired(&tmr)) + err = X502_ERR_BF_LOAD_RDY_TOUT; + } while ((err == X502_ERR_OK) && (reg & L502_REGBIT_BF_CTL_HOST_WAIT_Msk)); + + if (err == X502_ERR_OK) { + err = fread(hdr, 1, BF_LDR_HDR_SIZE, ldr_file) == BF_LDR_HDR_SIZE ? + f_parse_ldr_hdr(hdr, &pkt) : X502_ERR_LDR_FILE_READ; + } + + while ((err == X502_ERR_OK) && !stop) { + if (next_err != X502_ERR_OK) { + err = next_err; + } else if (((pkt.flags & BF_LDR_FLAG_FILL) == 0) && (pkt.size != 0)) { + int r_size = (pkt.size > LDR_BUFF_SIZE) ? LDR_BUFF_SIZE : pkt.size; + + rd_size = (int)fread(ldr_buff, 1, r_size, ldr_file); + if (rd_size!=r_size) + err = X502_ERR_LDR_FILE_READ; + } + if (err == X502_ERR_OK) { + if (pkt.size > LDR_BUFF_SIZE) { + pkt_next = pkt; + pkt_next.addr += LDR_BUFF_SIZE; + pkt_next.size -= LDR_BUFF_SIZE; + pkt.size = LDR_BUFF_SIZE; + } else { + next_err = fread(hdr, 1, BF_LDR_HDR_SIZE, ldr_file) == BF_LDR_HDR_SIZE ? + f_parse_ldr_hdr(hdr, &pkt_next) : X502_ERR_LDR_FILE_READ; + if (next_err != X502_ERR_OK) { + pkt_next.size = 0; + } + } + + if (pkt.size!=0) { + uint32_t size = ((pkt.size+31)/(32))*8; + if (pkt.flags & BF_LDR_FLAG_FILL) { + uint32_t i; + for (i=0; i < size; i++) + ldr_buff[i] = pkt.arg; + } + + if ((pkt.flags & BF_LDR_FLAG_FINAL) + || ((pkt_next.flags & BF_LDR_FLAG_FINAL) && (pkt_next.size==0))) { + uint32_t buf_pos = 0; + err = BF_CHECK_ADDR_SIZE(pkt.addr, size); + + if ((err == X502_ERR_OK) && (size > 8)) { + err = f_bf_mem_wr(hnd, pkt.addr, ldr_buff, size-8); + pkt.addr+=4*(size-8); + buf_pos = size-8; + size = 8; + } + + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_CMD, L502_BF_CMD_HIRQ); + if (err == X502_ERR_OK) + err = f_bf_mem_wr(hnd, pkt.addr, &ldr_buff[buf_pos], size); + stop=1; + + if (err == X502_ERR_OK) { + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_CTL, L502_REGBIT_BF_CTL_DSP_MODE_Msk | + L502_REGBIT_BF_CTL_BF_RESET_Msk); + } + } else if (!(pkt.flags & BF_LDR_FLAG_IGNORE)) { + err = BF_CHECK_ADDR_SIZE(pkt.addr, size); + if (!err) + err = f_bf_mem_wr(hnd, pkt.addr, ldr_buff, size); + } + } + pkt = pkt_next; + } + } + } + free(ldr_buff); + + fclose(ldr_file); + } + return err; +} diff --git a/l502/l502api_compat.c b/l502/l502api_compat.c new file mode 100644 index 0000000..6b7946c --- /dev/null +++ b/l502/l502api_compat.c @@ -0,0 +1,285 @@ +#include "l502api_compat.h" +#include "x502api_private.h" + +LPCIE_EXPORT(t_l502_hnd) L502_Create(void) { + return X502_Create(); +} + +LPCIE_EXPORT(int32_t) L502_Free(t_l502_hnd hnd) { + return X502_Free(hnd); +} + +LPCIE_EXPORT(int32_t) L502_Close(t_l502_hnd hnd) { + return X502_Close(hnd); +} + +LPCIE_EXPORT(int32_t) L502_GetDevInfo(t_l502_hnd hnd, t_l502_info* info) { + return X502_GetDevInfo(hnd, (t_x502_info*)info); +} + +LPCIE_EXPORT(int32_t) L502_Configure(t_l502_hnd hnd, uint32_t flags) { + return X502_Configure(hnd, flags); +} + +LPCIE_EXPORT(int32_t) L502_SetLChannel(t_l502_hnd hnd, uint32_t lch, uint32_t phy_ch, + uint32_t mode, uint32_t range, uint32_t avg) { + return X502_SetLChannel(hnd, lch, phy_ch, mode, range, avg); +} + +LPCIE_EXPORT(int32_t) L502_SetLChannelCount(t_l502_hnd hnd, uint32_t lch_cnt) { + return X502_SetLChannelCount(hnd, lch_cnt); +} + +LPCIE_EXPORT(int32_t) L502_GetLChannelCount(t_l502_hnd hnd, uint32_t* lch_cnt) { + return X502_GetLChannelCount(hnd, lch_cnt); +} + +LPCIE_EXPORT(int32_t) L502_SetAdcFreqDivider(t_l502_hnd hnd, uint32_t adc_freq_div) { + return X502_SetAdcFreqDivider(hnd, adc_freq_div); +} + +LPCIE_EXPORT(int32_t) L502_SetAdcInterframeDelay(t_l502_hnd hnd, uint32_t delay) { + return X502_SetAdcInterframeDelay(hnd, delay); +} + +LPCIE_EXPORT(int32_t) L502_SetDinFreqDivider(t_l502_hnd hnd, uint32_t din_freq_div) { + return X502_SetDinFreqDivider(hnd, din_freq_div); +} + +LPCIE_EXPORT(int32_t) L502_SetAdcFreq(t_l502_hnd hnd, double *f_acq, double *f_frame) { + return X502_SetAdcFreq(hnd, f_acq, f_frame); +} + +LPCIE_EXPORT(int32_t) L502_SetDinFreq(t_l502_hnd hnd, double *f_din) { + return X502_SetDinFreq(hnd, f_din); +} + + +LPCIE_EXPORT(int32_t) L502_GetAdcFreq(t_l502_hnd hnd, double *f_acq, double *f_frame) { + return X502_GetAdcFreq(hnd, f_acq, f_frame); +} + +LPCIE_EXPORT(int32_t) L502_SetRefFreq(t_l502_hnd hnd, uint32_t freq) { + return X502_SetRefFreq(hnd, freq); +} + +LPCIE_EXPORT(int32_t) L502_SetSyncMode(t_l502_hnd hnd, uint32_t sync_mode) { + return X502_SetSyncMode(hnd, sync_mode); +} + +LPCIE_EXPORT(int32_t) L502_SetSyncStartMode(t_l502_hnd hnd, uint32_t sync_start_mode) { + return X502_SetSyncStartMode(hnd, sync_start_mode); +} + +LPCIE_EXPORT(int32_t) L502_SetMode(t_l502_hnd hnd, uint32_t mode) { + return X502_SetMode(hnd, mode); +} + +LPCIE_EXPORT(int32_t) L502_GetMode(t_l502_hnd hnd, uint32_t* mode) { + return X502_GetMode(hnd, mode); +} + +LPCIE_EXPORT(int32_t) L502_SetAdcCoef(t_l502_hnd hnd, uint32_t range, double k, double offs) { + return X502_SetAdcCoef(hnd, range, k, offs); +} + +LPCIE_EXPORT(int32_t) L502_GetAdcCoef(t_l502_hnd hnd, uint32_t range, double* k, double* offs) { + return X502_GetAdcCoef(hnd, range, k, offs); +} + +LPCIE_EXPORT(int32_t) L502_SetDacCoef(t_l502_hnd hnd, uint32_t ch, double k, double offs) { + return X502_SetDacCoef(hnd, ch, k, offs); +} + +LPCIE_EXPORT(int32_t) L502_GetDacCoef(t_l502_hnd hnd, uint32_t ch, double* k, double* offs) { + return X502_GetDacCoef(hnd, ch, k, offs); +} + +LPCIE_EXPORT(int32_t) L502_AsyncOutDac(t_l502_hnd hnd, uint32_t ch, double data, uint32_t flags) { + return X502_AsyncOutDac(hnd, ch, data, flags); +} + +LPCIE_EXPORT(int32_t) L502_AsyncOutDig(t_l502_hnd hnd, uint32_t val, uint32_t msk) { + return X502_AsyncOutDig(hnd, val, msk); +} + + +LPCIE_EXPORT(int32_t) L502_AsyncInDig(t_l502_hnd hnd, uint32_t* din) { + return X502_AsyncInDig(hnd, din); +} + + +LPCIE_EXPORT(int32_t) L502_AsyncGetAdcFrame(t_l502_hnd hnd, uint32_t flags, + uint32_t tout, double* data) { + return X502_AsyncGetAdcFrame(hnd, flags, tout, data); +} + +LPCIE_EXPORT(int32_t) L502_StreamsEnable(t_l502_hnd hnd, uint32_t streams) { + return X502_StreamsEnable(hnd, streams); +} + +LPCIE_EXPORT(int32_t) L502_StreamsDisable(t_l502_hnd hnd, uint32_t streams) { + return X502_StreamsDisable(hnd, streams); +} + +LPCIE_EXPORT(int32_t) L502_StreamsStart(t_l502_hnd hnd) { + return X502_StreamsStart(hnd); +} + +LPCIE_EXPORT(int32_t) L502_StreamsStop(t_l502_hnd hnd) { + return X502_StreamsStop(hnd); +} + +LPCIE_EXPORT(int32_t) L502_IsRunning(t_l502_hnd hnd) { + return X502_IsRunning(hnd); +} + +LPCIE_EXPORT(int32_t) L502_Recv(t_l502_hnd hnd, uint32_t* buf, uint32_t size, uint32_t tout) { + return X502_Recv(hnd, buf, size, tout); +} + +LPCIE_EXPORT(int32_t) L502_Send(t_l502_hnd hnd, const uint32_t* buf, uint32_t size, uint32_t tout) { + return X502_Send(hnd, buf, size, tout); +} + +LPCIE_EXPORT(int32_t) L502_ProcessAdcData(t_l502_hnd hnd, const uint32_t* src, double *dest, + uint32_t *size, uint32_t flags) { + return X502_ProcessAdcData(hnd, src, dest, size, flags); +} + +LPCIE_EXPORT(int32_t) L502_ProcessData(t_l502_hnd hnd, const uint32_t* src, uint32_t size, + uint32_t flags, double *adc_data, uint32_t *adc_data_size, + uint32_t *din_data, uint32_t *din_data_size) { + return X502_ProcessData(hnd, src, size, flags, adc_data, adc_data_size, + din_data, din_data_size); +} + + +LPCIE_EXPORT(int32_t) L502_ProcessDataWithUserExt(t_l502_hnd hnd, const uint32_t* src, uint32_t size, + uint32_t flags, double *adc_data, + uint32_t *adc_data_size, uint32_t *din_data, + uint32_t *din_data_size, + uint32_t *usr_data, uint32_t *usr_data_size) { + return X502_ProcessDataWithUserExt(hnd, src, size, flags, adc_data, adc_data_size, + din_data, din_data_size, usr_data, usr_data_size); +} + +LPCIE_EXPORT(int32_t) L502_PrepareData(t_l502_hnd hnd, const double* dac1, const double* dac2, + const uint32_t* digout, uint32_t size, int32_t flags, + uint32_t* out_buf) { + return X502_PrepareData(hnd, dac1, dac2, digout, size, flags, out_buf); +} + +LPCIE_EXPORT(int32_t) L502_GetRecvReadyCount(t_l502_hnd hnd, uint32_t *rdy_cnt) { + return X502_GetRecvReadyCount(hnd, rdy_cnt); +} + +LPCIE_EXPORT(int32_t) L502_GetSendReadyCount(t_l502_hnd hnd, uint32_t *rdy_cnt) { + return X502_GetSendReadyCount(hnd, rdy_cnt); +} + +LPCIE_EXPORT(int32_t) L502_GetNextExpectedLchNum(t_l502_hnd hnd, uint32_t *lch) { + return X502_GetNextExpectedLchNum(hnd, lch); +} + +LPCIE_EXPORT(int32_t) L502_PreloadStart(t_l502_hnd hnd) { + return X502_PreloadStart(hnd); +} + +LPCIE_EXPORT(int32_t) L502_OutCycleLoadStart(t_l502_hnd hnd, uint32_t size) { + return X502_OutCycleLoadStart(hnd, size); +} + + +LPCIE_EXPORT(int32_t) L502_OutCycleSetup(t_l502_hnd hnd, uint32_t flags) { + return X502_OutCycleSetup(hnd, flags); +} + +LPCIE_EXPORT(int32_t) L502_OutCycleStop(t_l502_hnd hnd, uint32_t flags) { + return X502_OutCycleStop(hnd, flags); +} + +LPCIE_EXPORT(int32_t) L502_SetDmaBufSize(t_l502_hnd hnd, uint32_t dma_ch, uint32_t size) { + return X502_SetStreamBufSize(hnd, dma_ch, size); +} + +LPCIE_EXPORT(int32_t) L502_SetDmaIrqStep(t_l502_hnd hnd, uint32_t dma_ch, uint32_t step) { + return X502_SetStreamStep(hnd, dma_ch, step); +} + +LPCIE_EXPORT(int32_t) L502_BfLoadFirmware(t_l502_hnd hnd, const char* filename) { + return X502_BfLoadFirmware(hnd, filename); +} + +LPCIE_EXPORT(int32_t) L502_BfCheckFirmwareIsLoaded(t_l502_hnd hnd, uint32_t *version) { + return X502_BfCheckFirmwareIsLoaded(hnd, version); +} + +LPCIE_EXPORT(int32_t) L502_BfMemRead(t_l502_hnd hnd, uint32_t addr, uint32_t* regs, + uint32_t size) { + return X502_BfMemRead(hnd, addr, regs, size); +} + +LPCIE_EXPORT(int32_t) L502_BfMemWrite(t_l502_hnd hnd, uint32_t addr, + const uint32_t* regs, uint32_t size) { + return X502_BfMemWrite(hnd, addr, regs, size); +} + +LPCIE_EXPORT(int32_t) L502_BfExecCmd(t_l502_hnd hnd, uint16_t cmd_code, uint32_t par, + const uint32_t* snd_data, uint32_t snd_size, + uint32_t* rcv_data, uint32_t rcv_size, uint32_t tout, uint32_t* recvd_size) { + return X502_BfExecCmd(hnd, cmd_code, par, snd_data, snd_size, rcv_data, rcv_size, + tout, recvd_size); +} + +LPCIE_EXPORT(int32_t) L502_FlashRead(t_l502_hnd hnd, uint32_t addr, uint8_t* data, + uint32_t size) { + return X502_FlashRead(hnd, addr, data, size); +} + +LPCIE_EXPORT(int32_t) L502_FlashWrite(t_l502_hnd hnd, uint32_t addr, + const uint8_t* data, uint32_t size) { + return X502_FlashWrite(hnd, addr, data, size); +} + +LPCIE_EXPORT(int32_t) L502_FlashErase(t_l502_hnd hnd, uint32_t addr, uint32_t size) { + return X502_FlashErase(hnd, addr, size); +} + +LPCIE_EXPORT(int32_t) L502_FlashWriteEnable(t_l502_hnd hnd) { + return X502_FlashWriteEnable(hnd); +} + +LPCIE_EXPORT(int32_t) L502_FlashWriteDisable(t_l502_hnd hnd) { + return X502_FlashWriteDisable(hnd); +} + +LPCIE_EXPORT(uint32_t) L502_GetDllVersion(void) { + return X502_GetLibraryVersion(); +} + + +LPCIE_EXPORT(const char*) L502_GetErrorString(int32_t err) { + return X502_GetErrorString(err); +} + +LPCIE_EXPORT(int32_t) L502_LedBlink(t_l502_hnd hnd) { + return X502_LedBlink(hnd); +} + +LPCIE_EXPORT(int32_t) L502_SetDigInPullup(t_l502_hnd hnd, uint32_t pullups) { + return X502_SetDigInPullup(hnd, pullups); +} + + +LPCIE_EXPORT(int32_t) L502_FpgaRegWrite(t_l502_hnd hnd, uint32_t reg, uint32_t val) { + return X502_FpgaRegWrite(hnd, reg, val); +} + +LPCIE_EXPORT(int32_t) L502_FpgaRegRead(t_l502_hnd hnd, uint32_t reg, uint32_t *val) { + return X502_FpgaRegRead(hnd, reg, val); +} + +LPCIE_EXPORT(int32_t) L502_ReloadDevInfo(t_l502_hnd hnd) { + return X502_ReloadDevInfo(hnd, 0); +} diff --git a/l502/l502api_compat.h b/l502/l502api_compat.h new file mode 100644 index 0000000..65cd765 --- /dev/null +++ b/l502/l502api_compat.h @@ -0,0 +1,1834 @@ +/***************************************************************************//** + @file l502api_compat.h + Файл содержит определения типов и функций для L502, которые оставлены + для совместимости с версией библиотеки 1.0.x (до включения поддержки E502). + Все эти определения имеют аналог в x502api.h, а функции вызывают напрямую + аналогичные функции X502_xxx из библиотеки x502api + + @note Не включены недокументированные функцие, такие как L502_OpenByListItem, + LPCIE_GetDevInfoList и LPCIE_FreeDevInfoList + @author Borisov Alexey + ******************************************************************************/ + +#ifndef L502API_COMPAT_H +#define L502API_COMPAT_H + + +#include "x502api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LPCIE_EXPORT(type) X502_EXPORT(type) + +/***************************************************************************//** + @addtogroup const_list Константы и перечисления. + @{ + *****************************************************************************/ + +/** Максимальное количество логических каналов в таблице*/ +#define L502_LTABLE_MAX_CH_CNT 256 +/** Количество диапазонов для измерения напряжений */ +#define L502_ADC_RANGE_CNT 6 + +/** Максимальное значение для аппаратного усреднения по логическому каналу */ +#define L502_LCH_AVG_SIZE_MAX 128 +/** Максимальное значения делителя частоты АЦП */ +#define L502_ADC_FREQ_DIV_MAX (1024*1024) +/** Максимальное значение делителя частоты синхронного цифрового ввода */ +#define L502_DIN_FREQ_DIV_MAX (1024*1024) + +/** Максимальное значение межкадровой задержки для АЦП */ +#define L502_ADC_INTERFRAME_DELAY_MAX (0x1FFFFF) + +/** Таймаут по умолчанию для выполнения команды к BlackFin*/ +#define L502_BF_CMD_DEFAULT_TOUT 500 + +/** Код АЦП, соответствующий максимальному значению шкалы */ +#define L502_ADC_SCALE_CODE_MAX 6000000 +/** Код ЦАП, соответствующий максимальному значению шкалы */ +#define L502_DAC_SCALE_CODE_MAX 30000 + +/** Максимальное количество символов в строке с названием устройства */ +#define L502_DEVNAME_SIZE 32 +/** Максимальное количество символов в строке с серийным номером */ +#define L502_SERIAL_SIZE 32 + +/** Максимально возможное значение внешней опорной частоты */ +#define L502_EXT_REF_FREQ_MAX 2000000 + + +/** Размер пользовательской области Flash-памяти */ +#define L502_FLASH_USER_SIZE 0x100000 + +/** Стандартный таймаут на выполнение запроса к BlackFin в мс */ +#define L502_BF_REQ_TOUT 500 + + +/** Диапазон ЦАП в вольтах */ +#define L502_DAC_RANGE 5. + +/** Количество каналов ЦАП */ +#define L502_DAC_CH_CNT 2 + + +/** слово в потоке, означающее, что произошло переполнение */ +#define L502_STREAM_IN_MSG_OVERFLOW 0x01010000 + + +/** Коды ошибок библиотеки */ +typedef enum { + /** Функция выполнена без ошибок */ + L502_ERR_OK = 0, + /** В функцию передан недействительный описатель модуля */ + L502_ERR_INVALID_HANDLE = -1, + /** Ошибка выделения памяти */ + L502_ERR_MEMORY_ALLOC = -2, + /** Попытка открыть уже открытое устройство */ + L502_ERR_ALREADY_OPENED = -3, + /** Устройство с заданными параметрами не найдено в системе */ + L502_ERR_DEVICE_NOT_FOUND = -4, + /** Доступ к устройству запрещен (Как правило из-за того, что устройство + уже открыто в другой программе) */ + L502_ERR_DEVICE_ACCESS_DENIED = -5, + /** Ошибка открытия устройства */ + L502_ERR_DEVICE_OPEN = -6, + /** В функцию передан недействительный указатель */ + L502_ERR_INVALID_POINTER = -7, + /** Функция не может быть выполнена при запущенном потоке сбора данных */ + L502_ERR_STREAM_IS_RUNNING = -8, + /** Ошибка чтения данных синхронного ввода */ + L502_ERR_RECV = -9, + /** Ошибка записи данных для синхронного вывода */ + L502_ERR_SEND = -10, + /** Произошло переполнение внутреннего буфера для потока синхронного ввода */ + L502_ERR_STREAM_OVERFLOW = -11, + /** Неизвестное сообщение в потоке синхронного ввода */ + L502_ERR_UNSUP_STREAM_MSG = -12, + /** Ошибка создания системного мьютекса */ + L502_ERR_MUTEX_CREATE = -13, + /** Неверный описатель мьютекса */ + L502_ERR_MUTEX_INVALID_HANDLE = -14, + /** Истекло время ожидания освобождения мьютекса */ + L502_ERR_MUTEX_LOCK_TOUT = -15, + /** Ошибка освобождения мьютекса */ + L502_ERR_MUTEX_RELEASE = -16, + /** Недостаточно системных ресурсов */ + L502_ERR_INSUFFICIENT_SYSTEM_RESOURCES= -17, + /** Данная возможность еще не реализована */ + L502_ERR_NOT_IMPLEMENTED = -18, + /** Недостаточный размер массива */ + L502_ERR_INSUFFICIENT_ARRAY_SIZE = -19, + /** Ошибка чтения регистра FPGA */ + L502_ERR_FPGA_REG_READ = -20, + /** Ошибка записи регистра FPGA */ + L502_ERR_FPGA_REG_WRITE = -21, + /** Сбор данных уже остановлен */ + L502_ERR_STREAM_IS_NOT_RUNNING = -22, + /** Задан неверный размер логической таблицы */ + L502_ERR_INVALID_LTABLE_SIZE = -102, + /** Задан неверный номер логического канала */ + L502_ERR_INVALID_LCH_NUMBER = -103, + /** Неверно задано значение диапазона АЦП */ + L502_ERR_INVALID_LCH_RANGE = -104, + /** Неверно задан режим измерения для логического канала */ + L502_ERR_INVALID_LCH_MODE = -105, + /** Неверно задан номер физического канала при настройке логического */ + L502_ERR_INVALID_LCH_PHY_NUMBER = -106, + /** Неверно задан размер усреднения для логического канала */ + L502_ERR_INVALID_LCH_AVG_SIZE = -107, + /** Неверно задан делитель частоты сбора данных АЦП */ + L502_ERR_INVALID_ADC_FREQ_DIV = -108, + /** Неверно задан делитель частоты синхронного ввода цифровых линий */ + L502_ERR_INVALID_DIN_FREQ_DIV = -108, + /** Неверно задан режим работы модуля L502 */ + L502_ERR_INVALID_MODE = -109, + /** Неверный номер канала ЦАП */ + L502_ERR_INVALID_DAC_CHANNEL = -110, + /** Неверный код выбора опорной частоты синхронизации */ + L502_ERR_INVALID_REF_FREQ = -111, + /** Неверно задано значение межкадровой задержки */ + L502_ERR_INVALID_INTERFRAME_DELAY = -112, + /** Неверно задан режим синхронизации */ + L502_ERR_INVALID_SYNC_MODE = -113, + /** Неверно задан номер канала DMA */ + L502_ERR_INVALID_DMA_CH = -114, + + /** Ошибка захвата опорной частоты синхронизации */ + L502_ERR_REF_FREQ_NOT_LOCKED = -131, + /** Управляющий запрос к драйверу завершен с ошибкой */ + L502_ERR_IOCTL_FAILD = -132, + /** Истек таймаут ожидания завершения выполнения управляющего запроса к драйверу */ + L502_ERR_IOCTL_TIMEOUT = -133, + /** Ошибка получения информации о устройстве от драйвера */ + L502_ERR_GET_INFO = -134, + /** За время ожидания не было считано новое слово с цифровых линий */ + L502_ERR_DIG_IN_NOT_RDY = -135, + /** Принято недостаточно слов от модуля */ + L502_ERR_RECV_INSUFFICIENT_WORDS = -136, + /** Попытка выполнить операцию, требующую наличие ЦАП, при его отсутствии */ + L502_ERR_DAC_NOT_PRESENT = -137, + /** Неверный номер канала в обрабатываемом потоке синхронного ввода */ + L502_ERR_PROC_INVALID_CH_NUM = -140, + /** Неверный код диапазона в обрабатываемом потоке синхронного ввода */ + L502_ERR_PROC_INVALID_CH_RANGE = -141, + /** Задан неверный адрес во Flash-памяти */ + L502_ERR_FLASH_INVALID_ADDR = -142, + /** Задан неверный размер блока данных при работе с Flash-памятью */ + L502_ERR_FLASH_INVALID_SIZE = -143, + /** Истек таймаут ожидания завершения записи во Flash-память */ + L502_ERR_FLASH_WRITE_TOUT = -144, + /** Истек таймаут ожидания завершения стирания блока Flash-памяти */ + L502_ERR_FLASH_ERASE_TOUT = -145, + /** Заданная область для стирания Flash-памяти нарушает границу блока в 4 Кбайт */ + L502_ERR_FLASH_SECTOR_BOUNDARY = -146, + /** Не удалось открыть файл прошивки BlackFin */ + L502_ERR_LDR_FILE_OPEN = -180, + /** Ошибка чтения из фала прошивки BlackFin */ + L502_ERR_LDR_FILE_READ = -181, + /** Неверный формат файла прошивки BlackFin */ + L502_ERR_LDR_FILE_FORMAT = -182, + /** Используются возможность LDR-файла, недоступные при записи прошивки + BlackFin по HDMA */ + L502_ERR_LDR_FILE_UNSUP_FEATURE = -183, + /** Неверный стартовый адрес программы в прошивке BlackFin */ + L502_ERR_LDR_FILE_UNSUP_STARTUP_ADDR = -184, + /** Истек таймаут выполнения запроса на чтения/запись памяти BlackFin */ + L502_ERR_BF_REQ_TIMEOUT = -185, + /** Команда для BlackFin все еще находится в процессе обработки */ + L502_ERR_BF_CMD_IN_PROGRESS = -186, + /** Истекло время выполнения управляющей команды процессором BlackFin */ + L502_ERR_BF_CMD_TIMEOUT = -187, + /** Возвращено недостаточно данных в ответ на команду к BlackFin */ + L502_ERR_BF_CMD_RETURN_INSUF_DATA = -188, + /** Истек таймаут ожидания готовности процессора BlackFin к записи прошивки */ + L502_ERR_BF_LOAD_RDY_TOUT = -189, + /** Попытка выполнить операцию для которой нужен сигнальный процессор при + отсутствии сигнального процессора в модуле */ + L502_ERR_BF_NOT_PRESENT = -190, + /** Неверный адрес памяти BlackFin при записи или чтении по HDMA */ + L502_ERR_BF_INVALID_ADDR = -191, + /** Неверный размер данных, передаваемых с управляющей командой в BlackFin */ + L502_ERR_BF_INVALID_CMD_DATA_SIZE = -192 +} t_lpcie_errs; + +/** Флаги, управляющие поиском присутствующих модулей */ +typedef enum { + /** Признак, что нужно вернуть серийные номера только тех устройств, + которые еще не открыты */ + L502_GETDEVS_FLAGS_ONLY_NOT_OPENED = X502_GETDEVS_FLAGS_ONLY_NOT_OPENED +} t_l502_getdevs_flags; + + + +/** @brief Флаги для управления цифровыми выходами. + + Флаги управления цифровыми выходами. Могут быть объединены через логическое + “ИЛИ” со значениями цифровых выходов при асинхронном выводе с помощью + L502_AsyncOutDig() или переданы в L502_PrepareData() при синхронном выводе.*/ +typedef enum { + L502_DIGOUT_WORD_DIS_H = X502_DIGOUT_WORD_DIS_H, /**< Запрещение (перевод в третье состояние) + старшей половины цифровых выходов */ + L502_DIGOUT_WORD_DIS_L = X502_DIGOUT_WORD_DIS_L /**< Запрещение младшей половины + цифровых выходов */ +} t_l502_digout_word_flags; + + +/** Константы для выбора опорной частоты */ +typedef enum { + L502_REF_FREQ_2000KHZ = X502_REF_FREQ_2000KHZ, /**< Частота 2МГц */ + L502_REF_FREQ_1500KHZ = X502_REF_FREQ_1500KHZ /**< Частота 1.5МГц */ +} t_l502_ref_freq; + + +/** Диапазоны измерения для канала АЦП */ +typedef enum { + L502_ADC_RANGE_10 = X502_ADC_RANGE_10, /**< Диапазон +/-10V */ + L502_ADC_RANGE_5 = X502_ADC_RANGE_5, /**< Диапазон +/-5V */ + L502_ADC_RANGE_2 = X502_ADC_RANGE_2, /**< Диапазон +/-2V */ + L502_ADC_RANGE_1 = X502_ADC_RANGE_1, /**< Диапазон +/-1V */ + L502_ADC_RANGE_05 = X502_ADC_RANGE_05, /**< Диапазон +/-0.5V */ + L502_ADC_RANGE_02 = X502_ADC_RANGE_02 /**< Диапазон +/-0.2V */ +} t_l502_adc_range; + +/** Режим измерения для логического канала */ +typedef enum { + L502_LCH_MODE_COMM = X502_LCH_MODE_COMM, /**< Измерение напряжения относительно общей земли */ + L502_LCH_MODE_DIFF = X502_LCH_MODE_DIFF, /**< Дифференциальное измерение напряжения */ + L502_LCH_MODE_ZERO = X502_LCH_MODE_ZERO /**< Измерение собственного нуля */ +} t_l502_lch_mode; + +/** @brief Режимы синхронизации. + + Режимы задания источника частоты синхронизации и признака начала + синхронного ввода-вывода */ +typedef enum { + L502_SYNC_INTERNAL = 0, /**< Внутренний сигнал */ + L502_SYNC_EXTERNAL_MASTER = 1, /**< От внешнего мастера по разъему синхронизации */ + L502_SYNC_DI_SYN1_RISE = 2, /**< По фронту сигнала DI_SYN1 */ + L502_SYNC_DI_SYN2_RISE = 3, /**< По фронту сигнала DI_SYN2 */ + L502_SYNC_DI_SYN1_FALL = 6, /**< По спаду сигнала DI_SYN1 */ + L502_SYNC_DI_SYN2_FALL = 7 /**< По спаду сигнала DI_SYN2 */ +} t_l502_sync_mode; + +/** Флаги, управляющие обработкой принятых данных */ +typedef enum { + /** Признак, что нужно преобразовать значения АЦП в вольты */ + L502_PROC_FLAGS_VOLT = X502_PROC_FLAGS_VOLT, + /** Признак, что не нужно проверять совпадение номеров каналов + в принятых данных с каналами из логической таблицы. + Может использоваться при нестандартной прошивке BlackFin + при передаче в ПК не всех данных. */ + L502_PROC_FLAGS_DONT_CHECK_CH = X502_PROC_FLAGS_DONT_CHECK_CH +} t_l502_proc_flags; + + +/** Флаги для обозначения синхронных потоков данных */ +typedef enum { + L502_STREAM_ADC = X502_STREAM_ADC, /**< Поток данных от АЦП */ + L502_STREAM_DIN = X502_STREAM_DIN, /**< Поток данных с цифровых входов */ + L502_STREAM_DAC1 = X502_STREAM_DAC1, /**< Поток данных первого канала ЦАП */ + L502_STREAM_DAC2 = X502_STREAM_DAC2, /**< Поток данных второго канала ЦАП */ + L502_STREAM_DOUT = X502_STREAM_DOUT, /**< Поток данных на цифровые выводы */ + /** Объединение всех флагов, обозначающих потоки данных на ввод */ + L502_STREAM_ALL_IN = X502_STREAM_ALL_IN, + /** Объединение всех флагов, обозначающих потоки данных на вывод */ + L502_STREAM_ALL_OUT = X502_STREAM_ALL_OUT +} t_l502_streams; + +/** Константы, определяющие тип передаваемого отсчета из ПК в модуль */ +typedef enum { + L502_STREAM_OUT_WORD_TYPE_DOUT = X502_STREAM_OUT_WORD_TYPE_DOUT, /**< Цифровой вывод */ + L502_STREAM_OUT_WORD_TYPE_DAC1 = X502_STREAM_OUT_WORD_TYPE_DAC1, /**< Код для 1-го канала ЦАП */ + L502_STREAM_OUT_WORD_TYPE_DAC2 = X502_STREAM_OUT_WORD_TYPE_DAC2 /**< Код для 2-го канала ЦАП */ +} t_l502_stream_out_wrd_type; + +/** Режим работы модуля L502 */ +typedef enum { + L502_MODE_FPGA = X502_MODE_FPGA, /**< Все потоки данных передаются через ПЛИС минуя + сигнальный процессор BlackFin */ + L502_MODE_DSP = X502_MODE_DSP, /**< Все потоки данных передаются через сигнальный + процессор, который должен быть загружен + прошивкой для обработки этих потоков */ + L502_MODE_DEBUG = X502_MODE_DEBUG /**< Отладочный режим */ +} t_l502_mode; + +/** @brief Номера каналов ЦАП. + + Номер каналов ЦАП для указания в L502_AsyncOutDac() */ +typedef enum { + L502_DAC_CH1 = X502_DAC_CH1, /**< Первый канал ЦАП */ + L502_DAC_CH2 = X502_DAC_CH2 /**< Второй канал ЦАП */ +} t_l502_dac_ch; + +/** @brief Флаги, используемые при выводе данных на ЦАП. + + Флаги, комбинацию которых можно передать в L502_AsyncOutDac() или + L502_PrepareData(), чтобы определить действия, которые должны выполнить + эти функции с переданным значением перед выводом их на ЦАП */ +typedef enum { + /** Указывает, что значение задано в Вольтах и при выводе его нужно + перевести его в коды ЦАП. Если флаг не указан, то считается, что значение + изначально в кодах */ + L502_DAC_FLAGS_VOLT = X502_DAC_FLAGS_VOLT, + /** Указывает, что нужно применить калибровочные коэффициенты перед + выводом значения на ЦАП. */ + L502_DAC_FLAGS_CALIBR = X502_DAC_FLAGS_CALIBR +} t_l502_dacout_flags; + + +/** Номера каналов DMA */ +typedef enum { + L502_DMA_CH_IN = X502_STREAM_CH_IN, /**< Общий канал DMA на ввод */ + L502_DMA_CH_OUT = X502_STREAM_CH_OUT /**< Общий канал DMA на вывод */ +} t_l502_dma_ch; + + +/** @brief Цифровые линии, на которых можно включить подтягивающие резисторы + + Флаги, определяющие на каких цифровых входах должны быть включены + подтягивающие резисторы */ +typedef enum { + L502_PULLUPS_DI_H = X502_PULLUPS_DI_H, /**< Старшая половина цифровых входов */ + L502_PULLUPS_DI_L = X502_PULLUPS_DI_L, /**< Младшая половина цифровых входов */ + L502_PULLUPS_DI_SYN1 = X502_PULLUPS_DI_SYN1, /**< Линия SYN1 */ + L502_PULLUPS_DI_SYN2 = X502_PULLUPS_DI_SYN2 /**< Линия SYN2 */ +} t_l502_pullups; + + +/** Флаги, определяющие наличие опций в модуле */ +typedef enum { + /** Признак наличия двухканального канального ЦАП */ + L502_DEVFLAGS_DAC_PRESENT = X502_DEVFLAGS_DAC_PRESENT, + /** Признак наличия гальваноразвязки */ + L502_DEVFLAGS_GAL_PRESENT = X502_DEVFLAGS_GAL_PRESENT, + /** Признак наличия сигнального процессора BlackFin */ + L502_DEVFLAGS_BF_PRESENT = X502_DEVFLAGS_BF_PRESENT, + /** Признак, что во Flash-памяти присутствует информация о модуле */ + L502_DEVFLAGS_FLASH_DATA_VALID = X502_DEVFLAGS_FLASH_DATA_VALID, + /** Признак, что во Flash-памяти присутствуют действительные калибровочные + коэффициенты АЦП */ + L502_DEVFLAGS_FLASH_ADC_CALIBR_VALID = X502_DEVFLAGS_FLASH_ADC_CALIBR_VALID, + /** Признак, что во Flash-памяти присутствуют действительные калибровочные + коэффициенты ЦАП */ + L502_DEVFLAGS_FLASH_DAC_CALIBR_VALID = X502_DEVFLAGS_FLASH_DAC_CALIBR_VALID +} t_l502_dev_flags; + + +/** @brief Флаги для режима циклического вывода + + Данные флаги могут быть переданы в L502_OutCycleSetup() и L502_OutCycleStop() */ +typedef enum { + /** Флаг указывает, что останов или смена сигнала могут произойти без ожидания + конца цикла предыдущего сигнала. Это позволяет выполнить переключение + быстрее (однако все равно может быть поставлено на передачу до 256 КСемплов, + которые должны будут быть переданы), но точка смены или останова + может быть в любом месте периода */ + L502_OUT_CYCLE_FLAGS_FORCE = X502_OUT_CYCLE_FLAGS_FORCE +} t_l502_out_cycle_flags; + +/** @} */ + +/***************************************************************************//** + @addtogroup type_list Типы данных. + @{ + *****************************************************************************/ + +/** @brief Описатель модуля. + + Непрозрачный указатель на структуру, + содержащую информацию о настройках модуля и текущем соединении с ним. + Пользовательской программе не доступны поля структуры напрямую, а только + через функции библиотеки. + Функции управления модулем принимают описатель модуля своим первым параметром. + Описатель модуля создается с помощью L502_Create() и в конце работы + освобождается с помощью L502_Free(). */ +typedef t_x502_hnd t_l502_hnd; + + + +/** @brief Списко серийный номеров + + Тип определяет массив серийных номеров для количестава модулей, определяемого + на этапе работы программы. */ +typedef t_x502_serial_list t_l502_serial_list; + + +/** @brief Калибровочные коэффициенты диапазона. + + Структура содержит калибровочные значения смещения нуля и коэффициента + шкалы для одного диапазона АЦП или ЦАП.Результирующее значение АЦП + вычисляется как (val-offs)*k, где val - некалиброванное значение */ +typedef struct { + double offs; /**< смещение нуля */ + double k; /**< коэффициент шкалы */ +} t_l502_cbr_coef; + + +/** @brief Калибровочные коэффициенты модуля. + + Структура, содержащая все калибровочные коэффициенты, которые + используются модулем L502 */ +typedef struct { + /** Калибровочные коэффициенты АЦП */ + t_l502_cbr_coef adc[L502_ADC_RANGE_CNT]; + uint32_t res1[64]; /**< Резерв */ + /** Калибровочные коэффициенты ЦАП */ + t_l502_cbr_coef dac[L502_DAC_CH_CNT]; + uint32_t res2[20]; /**< Резерв */ +} t_l502_cbr; + +/** @brief Информация о модуле L502. + + Структура, содержащая постоянную информация о модуле L502, которая как правило + не изменяется после открытия */ +typedef struct { + char name[L502_DEVNAME_SIZE]; /**< Название устройства ("L502") */ + char serial[L502_SERIAL_SIZE]; /**< Серийный номер */ + uint32_t devflags; /**< Флаги из #t_l502_dev_flags, описывающие наличие + в модуле определенных опций */ + uint16_t fpga_ver; /**< Версия ПЛИС (старший байт - мажорная, младший - минорная) */ + uint8_t plda_ver; /**< Версия ПЛИС, управляющего аналоговой частью */ + uint8_t board_rev; /**< Ревизия платы */ + uint8_t res[120]; /**< Резерв */ + t_l502_cbr cbr; /**< Заводские калибровочные коэффициенты (из Flash-памяти) */ +} t_l502_info; + +/** @} */ + + +/** @addtogroup func_list Функции. + @{ **/ + +/***************************************************************************//** + @addtogroup func_hnd Функции для создания и освобождения описателя модуля. + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Создание описателя модуля. + + Создание описателя модуля, для последующей работы с модулем L502. + В случае успешного выделения памяти инициализирует поля описателя + значениями по-умолчанию. + @return NULL в случае ошибки, иначе - описатель модуля +*******************************************************************************/ +LPCIE_EXPORT(t_l502_hnd) L502_Create(void); + +/***************************************************************************//** + @brief Освобождение описателя модуля. + + Освобождение памяти, выделенной под описатель модуля с помощью L502_Create(). + После этого описатель уже использовать нельзя, независимо от возвращенного + значения! + @param[in] hnd Описатель устройства + @return Код ошибки +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_Free(t_l502_hnd hnd); +/** @} */ + + +/***************************************************************************//** + @addtogroup func_open Функции для открытия и получения информации о модуле. + @{ +*******************************************************************************/ + + +/***************************************************************************//** + @brief Закрытие соединения с модулем. + + Функция разрывает соединение с модулем L502, если оно было ранее установлено + (в противном случае ничего не делает). + Описатель модуля не освобождается. + Память под описатель модуля должна быть освобождена вызовом L502_Free(). + @param[in] hnd Описатель модуля. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_Close(t_l502_hnd hnd); + + +/***************************************************************************//** + @brief Получение информации о модуле. + + Получение информации о модуле L502, с которым установлена связь. + @param[in] hnd Описатель модуля. + @param[out] info Информация о модуле (смотри описание типа #t_l502_info). + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetDevInfo(t_l502_hnd hnd, t_l502_info* info); + + + +/** @} */ + +/***************************************************************************//** + @addtogroup func_config Функции для изменения настроек модуля + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Передача установленных настроек в модуль. + + Функция выполняет запись текущих настроек (которые были установлены + с помощью функций L502_SetXXX) в модуль. + Должна вызываться перед запуском потока данных. + @param[in] hnd Описатель модуля. + @param[in] flags Флаги (резерв - должно быть равно 0). + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_Configure(t_l502_hnd hnd, uint32_t flags); + +/***************************************************************************//** + @brief Установка параметров логического канала. + + Функция устанавливает параметры заданного логического канала в логической + таблице АЦП. + @param[in] hnd Описатель модуля. + @param[in] lch Номер логического канала. + (от 0 до #L502_LTABLE_MAX_CH_CNT-1) + @param[in] phy_ch Номер физического канала АЦП, начиная с 0 + (0-15 для дифференциального режима, + 0-31 для режима с общей землей) + @param[in] mode Режим измерения канал АЦП (значение типа #t_l502_lch_mode) + @param[in] range Диапазон измерения канала (значение типа #t_l502_adc_range) + @param[in] avg Коэффициент усреднения по каналу. Нулевое значение + соответствует значению коэффициента, определенного + библиотекой. Для явного задания коэффициента усреднения + нужно перед значение от 1 (отсутствие усреднения) до + #L502_LCH_AVG_SIZE_MAX. + В случае если значение усреднения превышает делитель + частоты, то это значение будет скорректировано + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetLChannel(t_l502_hnd hnd, uint32_t lch, uint32_t phy_ch, + uint32_t mode, uint32_t range, uint32_t avg); + +/***************************************************************************//** + @brief Установка количества логических каналов. + + Функция устанавливает количество логических каналов в логической таблице АЦП. + @param[in] hnd Описатель модуля + @param[in] lch_cnt Количество логических каналов + (от 1 до #L502_LTABLE_MAX_CH_CNT) + @return Код ошибки +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetLChannelCount(t_l502_hnd hnd, uint32_t lch_cnt); + + +/***************************************************************************//** + @brief Получение количества логических каналов. + + Функция возвращает установленное ранее с помощью L502_SetLChannelCount() + количество логических каналов в управляющей таблице АЦП. + @param[in] hnd Описатель модуля + @param[out] lch_cnt Количество логических каналов + @return Код ошибки +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetLChannelCount(t_l502_hnd hnd, uint32_t* lch_cnt); + +/***************************************************************************//** + @brief Установка делителя частоты сбора для АЦП. + + Частота сбора АЦП получается как результат деления опорной частоты + синхронизации (как в случае внешней, так и внутренней) на делитель, + устанавливаемый этой функцией. + + Альтернативой этой функции служит L502_SetAdcFreq(), которая рассчитывает + этот делитель на основании переданной требуемой частоты сбора АЦП. + + @param[in] hnd Описатель модуля. + @param[in] adc_freq_div Делитель частоты АЦП (от 1 до #L502_ADC_FREQ_DIV_MAX). + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetAdcFreqDivider(t_l502_hnd hnd, uint32_t adc_freq_div); + +/***************************************************************************//** + @brief Установка значения межкадровой задержки для АЦП. + + Функция устанавливает межкадровую задержку для АЦП, то есть количество + периодов опорной частоты синхронизации, которое будет пропущено после + проведения измерения последнего канала логической таблицы до проведения + измерения, соответствующего первому логическому каналу следующего кадра. + + Альтернативой может являться функция L502_SetAdcFreq(), которая рассчитывает + значение межкадровой задержки по заданным параметрам частоты сбора и частоты + следования кадров (частоты сбора на логический канал). + + @param[in] hnd Описатель модуля. + @param[in] delay Значение межкадровой задержки (от 0 до + #L502_ADC_INTERFRAME_DELAY_MAX) + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetAdcInterframeDelay(t_l502_hnd hnd, uint32_t delay); + +/***************************************************************************//** + @brief Установка делителя частоты синхронного ввода с цифровых линий. + + Частота синхронного ввода данных с цифровых входов получается как результат + деления опорной частоты синхронизации на делитель, устанавливаемый этой + функцией. + + Альтернативой этой функции служит L502_SetDinFreq(), которая рассчитывает + этот делитель на основании переданной требуемой частоты синхронного ввода + с цифровых линий. + + @param[in] hnd Описатель модуля. + @param[in] din_freq_div Делитель частоты АЦП (от 1 до #L502_DIN_FREQ_DIV_MAX). + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetDinFreqDivider(t_l502_hnd hnd, uint32_t din_freq_div); + + +/***************************************************************************//** + @brief Установка частоты сбора АЦП. + + Функция подбирает делитель частоты АЦП так, чтобы полученная частота сбора + была наиболее близка к указанной в параметре f_acq. Функция возвращает + в этом же параметре реальную частоту, которая была установлена. + + Так же функция может подобрать значение межкадровой задержки так, чтобы + частота следования кадров (частота сбора на логический канал) была наиболее + близка к указанному значению. + Для этого следует передать требуемое значение в переменной f_frame (в ней + также по завершению будет возвращено значение установленной частоты). + Если в качестве f_frame передан нулевой указатель, то будет установлена + нулевая межкадровая задержка. + + Если необходимо изменить значение опорной частоты, то данная функция должна + быть вызвана после L502_SetRefFreq(), в противном случае полученные делители + будут давать неверное значение частоты. + + Если устанавливается частота кадров, то функция должна вызываться после + того, как было заданно нужное количество логических каналов в управляющей + таблице с помощью L502_SetLChannelCount(). + + При использовании внешней опорной частоты синхронизации эта функция будет + давать верный результат, только если эта внешняя частота соответствует + значению, установленному с помощью L502_SetRefFreq(). + + + @param[in] hnd Описатель модуля. + @param[in,out] f_acq На входе принимает требуемое значения частоты сбора + АЦП в Герцах. На выходе возвращает реально + установленное значение частоты. + @param[in,out] f_frame На входе принимает требуемое значение частоты сбора + кадров (частоты сбора на логический канал) АЦП + в Герцах. На выходе возвращает реально + установленное значение. Если передан нулевой + указатель, то устанавливает максимальную частоту + сбора кадров (нулевую межкадровую задержку). + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetAdcFreq(t_l502_hnd hnd, double *f_acq, double *f_frame); + + + + + + +/***************************************************************************//** + @brief Установка частоты синхронного ввода с цифровых входов. + + Функция подбирает делитель частоты ввода значений с цифровых входов так, чтобы + полученная частота ввода была наиболее близка к указанной. Функция возвращает + в этом же параметре реальную частоту, которая была установлена. + + Если необходимо изменить значение опорной частоты синхронизации, то данная + функция должна быть вызвана после L502_SetRefFreq(), в противном случае + полученный делитель будет давать неверное значение частоты. + + При использовании внешней опорной частоты синхронизации эта функция будет + давать верный результат, только если эта внешняя частота соответствует + значению, установленному с помощью L502_SetRefFreq(). + + @param[in] hnd Описатель модуля. + @param[in,out] f_din На входе принимает требуемое значения частоты ввода + с цифровых входов в Герцах. На выходе возвращает + реально установленное значение частоты. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetDinFreq(t_l502_hnd hnd, double *f_din); + + +/***************************************************************************//** + @brief Получить текущие значения частот сбора АЦП + + Функция возвращает ткущие установленные для модуля значения частоты сбора + и частоты кадров АЦП (частоты на логический канал) в Герцах, которые были + установлены до этого с помощью L502_SetAdcFreq() или с помощью функций + L502_SetAdcFreqDivider()/L502_SetAdcInterframeDelay(). + + @param[in] hnd Описатель модуля. + @param[out] f_acq Если не NULL, то на выходе возвращается текущее + значение частоты сбора АЦП. + @param[out] f_frame Если не NULL, то на выходе возвращается текущее + значение частоты кадров АЦП. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetAdcFreq(t_l502_hnd hnd, double *f_acq, double *f_frame); + + + +/***************************************************************************//** + @brief Установка значения опорной частоты синхронизации. + + Функция задает значение опорной частоты синхронизации, от которой получаются + все частоты синхронного ввода/вывода посредством деления на определенный + делитель. + + При использовании внутренней опорной частоты доступны два значения: + 2МГц и 1.5Мгц (2МГц является значением по-умолчанию), для задания которых + можно использовать константы из #t_l502_ref_freq. + + При использовании внешней опорной частоты для того, чтобы можно было + правильно установить нужную частоту сбора функциями + L502_SetAdcFreq()/L502_SetDinFreq() и правильно были установлены значения + по-умолчанию для размера буфера DMA и шага прерываний, нужно с помощью этой + функции задать реально подаваемое значение внешней опорной частоты в Герцах. + + @param[in] hnd Описатель модуля. + @param[in] freq Значение из #t_l502_ref_freq, которое задает + выбранную опорную частоту. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetRefFreq(t_l502_hnd hnd, uint32_t freq); + +/***************************************************************************//** + @brief Установка режима генерации частоты синхронизации. + + Функция устанавливает кто будет генератором опорной частоты синхронизации - + сам модуль или будет использоваться внешний сигнал. + + В режиме #L502_SYNC_INTERNAL модуль сам будет генерировать для себя + частоту синхронизации с частотой, заданной L502_SetRefFreq(). + При этом запуск генерации будет осуществлен по вызову L502_StreamsStart() + или по условию, заданому в L502_SetSyncStartMode(), а останов + по L502_StreamsStop(). + + В остальных режимах сбор будет осуществляться по внешнему сигналу + синхронизации. + + @param[in] hnd Описатель модуля. + @param[in] sync_mode Значение из #t_l502_sync_mode, определяющее кто + будет источником частоты синхронизации. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetSyncMode(t_l502_hnd hnd, uint32_t sync_mode); + +/***************************************************************************//** + @brief Установка режима запуска частоты синхронизации. + + Функция устанавливает условие запуска синхронного ввода/вывода данных. + + Если с помощью L502_SetSyncMode() установлен режим синхронизации + #L502_SYNC_INTERNAL, то по заданному данной функцией условию модуль начнет + генерировать частоту синхронизации, в противном случае по заданному условию + модуль начнет использовать внешне заданную частоту синхронизации + (т.е. до выполнения условия сигнал синхронизации на заданном входе будет + игнорироваться). + + Режимы задания условия запуска синхронизации имеют те же значения, + что и режимы задания самой частоты (см. тип #t_l502_sync_mode). + В случае #L502_SYNC_INTERNAL запуск осуществляется при выполнении функции + L502_StreamsStart(), в противном случае - после выполнения + L502_StreamsStart() модуль начинает ожидать заданного данной функцией условия. + Т.е. даже при задании внешних источников синхронизации, все равно необходимо + вызывать L502_StreamsStart(). + + @param[in] hnd Описатель модуля. + @param[in] sync_start_mode Значение из #t_l502_sync_mode, определяющее + условие запуска частоты синхронизации. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetSyncStartMode(t_l502_hnd hnd, uint32_t sync_start_mode); + + +/***************************************************************************//** + @brief Установить режим работы модуля. + + Функция устанавливает режим работы модуля, который определяет будет ли + потоки данных обрабатывать ПЛИС или сигнальный процессор BlackFin. + При включении питания модулем всегда управляет ПЛИС. + После загрузки прошивки с помощью L502_BfLoadFirmware() модуль переходит + в режим управления сигнальным процессором. + + Данная функция может использоваться для ручной установки режима, + например, для возврата в режим управления ПЛИС или для переключения в режим + управления сигнальным процессором, если прошивка уже была загружена + (например, через JTAG интерфейс при отладке). + + @param[in] hnd Описатель модуля. + @param[in] mode Режим работы модуля из #t_l502_mode. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetMode(t_l502_hnd hnd, uint32_t mode); +/***************************************************************************//** + @brief Получение текущего режима работы модуля. + + Функция возвращает текущий режим работы модуля. + @param[in] hnd Описатель модуля. + @param[out] mode В данном параметре возвращается текущий режим + работы модуля (из #t_l502_mode). + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetMode(t_l502_hnd hnd, uint32_t* mode); +/***************************************************************************//** + @brief Установить коэффициенты для калибровки значений АЦП. + + Функция записывает в ПЛИС коэффициенты для калибровки значений АЦП. + При открытии модуля, библиотека считывает калибровочные коэффициенты из + защищенной области Flash-памяти модуля и записывает их в ПЛИС для + выполнения калибровки на лету. + + Результирующее значение АЦП вычисляется по формуле (val+offs)*k, где val - + некалиброванное значение. + + Данная функция позволяет изменить используемые коэффициенты в то время, пока + не запущен синхронный сбор данных. При этом изменяются только текущие + коэффициенты, а заводские калибровочные коэффициенты из Flash-памяти + сохраняют свое значение и при следующем открытии будут восстановлены. + + @param[in] hnd Описатель модуля. + @param[in] range Диапазон АЦП (из #t_l502_adc_range). + @param[in] k Устанавливаемое значение коэффициента шкалы. + @param[in] offs Устанавливаемое значение смещения нуля. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetAdcCoef(t_l502_hnd hnd, uint32_t range, double k, double offs); + +/***************************************************************************//** + @brief Получение текущих калибровочных коэффициентов АЦП. + + Функция возвращает текущие калибровочные коэффициенты для заданного диапазона + измерения АЦП. Эти коэффициенты могут отличаться от заводских значений, + сохраненных во Flash-памяти модуля, например, если пользователь использовал + L502_SetAdcCoef() для установки своих коэффициентов. + + @param[in] hnd Описатель модуля. + @param[in] range Диапазон АЦП (из #t_l502_adc_range). + @param[in] k В данной переменной возвращается текущий коэффициент + шкалы. + @param[in] offs В данной переменной возвращается текущее смещение нуля. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetAdcCoef(t_l502_hnd hnd, uint32_t range, double* k, double* offs); + + + +/***************************************************************************//** + @brief Установить коэффициенты для калибровки значений ЦАП. + + Функция устанавливает калибровочные коэффициенты для заданного канала АЦП, + которые будут использоваться функциями l502api для калибровки выводимых + значений ЦАП, если указан фалаг #L502_DAC_FLAGS_CALIBR. + + Откалиброванное значение ЦАП в кодах получается как + (val+offs)*k, где val - некалиброванное значение (в кодах). + + При открытии модуля, библиотека считывает калибровочные коэффициенты из + защищенной области Flash-памяти модуля и использует их. + + Данная функция нужна только если пользователь хочет использовать свои + коэффициенты. При этом она не изменяет значения во Flash-памяти, т.е. + при следующем открытии модуля коэффициенты будут снова восстановлены из + Flash-памяти. + + @param[in] hnd Описатель модуля. + @param[in] ch Канал ЦАП (из #t_l502_dac_ch). + @param[in] k Устанавливаемое значение коэффициента шкалы. + @param[in] offs Устанавливаемое значение смещения нуля. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetDacCoef(t_l502_hnd hnd, uint32_t ch, double k, double offs); + + +/***************************************************************************//** + @brief Получение текущих калибровочных коэффициентов ЦАП. + + Функция возвращает текущие калибровочные коэффициенты для заданного канала ЦАП. + Эти коэффициенты могут отличаться от заводских значений, + сохраненных во Flash-памяти модуля, например, если пользователь использовал + L502_SetDacCoef() для установки своих коэффициентов. + + @param[in] hnd Описатель модуля. + @param[in] ch Канал ЦАП (из #t_l502_dac_ch). + @param[in] k В данной переменной возвращается текущий коэффициент + шкалы. + @param[in] offs В данной переменной возвращается текущее смещение нуля. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetDacCoef(t_l502_hnd hnd, uint32_t ch, double* k, double* offs); + + + +/** @} */ + + +/***************************************************************************//** + @addtogroup func_async Функции асинхронного ввода-вывода + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Асинхронный вывод данных на канал ЦАП. + + Функция выводит указанное значение на указанный канал ЦАП. Значение может + быть задано как в кодах, так и в Вольтах, и к нему могут быть применены + калибровочные коэффициенты (определяется флагами). + + Функция может вызываться либо когда синхронный сбор не запущен, либо при + запущенном сборе данных, если синхронный сбор по этому каналу ЦАП + не разрешен. + + @param[in] hnd Описатель модуля. + @param[in] ch Номер канала ЦАП (из #t_l502_dac_ch). + @param[in] data Выводимое значение на ЦАП (в кодах или вольтах) + @param[in] flags Флаги из #t_l502_dacout_flags. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_AsyncOutDac(t_l502_hnd hnd, uint32_t ch, double data, uint32_t flags); + +/***************************************************************************//** + @brief Асинхронный вывод данных на цифровые выходы. + + Функция выводит указанное значение на цифровые выходы модуля. + Формат значения аналогичен L502_PrepareData() - в младших 16 битах + указывается выводимое значение, а в старшие - флаги (с помощью которых можно + перевести одну из половин в третье состояние). + + Функция может вызываться либо когда синхронный сбор не запущен, либо при + запущенном сборе данных, если синхронный сбор по цифровым линиям не разрешен. + + Можно использовать маску, чтобы вывести только на часть выводов, оставив + остальные неизменными, однако следует учесть, что после открытия связи с + модулем необходимо сперва сделать вывод на все линии, после чего уже + можно использовать маску при последующих вызовах. + + @param[in] hnd Описатель модуля. + @param[in] val Младшая половина - выводимое значение, старшая - + флаги из #t_l502_digout_word_flags. + @param[in] msk Маска - указанные в маске биты не будут изменяться + с предыдущего выведенного состояния (распространяется + и на старшую половину val). + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_AsyncOutDig(t_l502_hnd hnd, uint32_t val, uint32_t msk); + + +/***************************************************************************//** + @brief Асинхронный ввод значений с цифровых входов. + + Функция считывает текущее значение цифровых входов. + При этом синхронный сбор цифровых входов не должен быть запущен (не разрешен + поток #L502_STREAM_DIN). + + Так как модуль L502 не поддерживает аппаратно асинхронный ввод, то если на + момент вызова этой функции не запущен синхронный ввод/вывод с помощью + L502_StreamsStart(), то данная функция на время выполнения запускает + синхронный сбор и останавливает его как только будут получено одно новое + значение цифровых входов. + + @param[in] hnd Описатель модуля. + @param[out] din При успешном выполнении в этой переменной + возвращается текущее состояние цифровых входов. + Действительны младшие 18 бит, старшие 14 - резерв. + Резервные биты могут быть использованы в последующих + версиях, не следует считать, что они всегда будут + равны нулю! + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_AsyncInDig(t_l502_hnd hnd, uint32_t* din); + + + + + + + + + +/***************************************************************************//** + @brief Асинхронный ввод одного кадра АЦП. + + Функия производит однократный ввод кадра в соответствии с заранее + установленной логической таблицей. Частота сбора АЦП соответствует частоте + установленной с помощью L502_SetAdcFreq(). Частота следования кадров + значения не имеет. Сам кадр вводится синхронно, но при последовательном + вызове L502_AsyncGetAdcFrame() для измерения нескольких кадров задержка + между этими кадрами не определена. + + Функция так же выполняет обработку принятых данных АЦП, аналогично + L502_ProcessAdcData(), и принимает набор флагов, аналогичный + L502_ProcessAdcData(). + + Для работы этой функции не должен быть разрешен синхронный ввод АЦП + и цифровых линий. + + Так как аппаратно асинхронный ввод в плате отсутствует, то эта функция + в случае не запущенного потока запускает его внутри себя, принимает один + кадр данных и после этого останавливает синхронный сбор. + + @param[in] hnd Описатель модуля. + @param[in] flags Флаги из t_l502_proc_flags + @param[in] tout Таймаут на выполнение функции в мс + @param[out] data Массив, в котором в случае успеха будут возвращены + отсчеты кадра АЦП. Должен быть размером, достаточным + для хранения отсчетов типа double в количестве, + равном количеству установленных логических каналов + в управляющей таблице АЦП. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_AsyncGetAdcFrame(t_l502_hnd hnd, uint32_t flags, + uint32_t tout, double* data); + +/** @} */ + + + + +/***************************************************************************//** + @addtogroup func_streams Функции для работы с синхронным потоковым вводом-выводом + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Разрешение синхронных потоков на ввод/вывод. + + Функция разрешает прием/передачу для указанных потоков + Не указанные потоки сохраняют свое разрешенное или запрещенное состояние. + Может вызываться как до L502_Configure(), так и после. + Разрешенные потоки устанавливаются как правило до вызова L502_StreamsStart(). + + При желании в некоторых ситуациях можно изменять состав разрешенных потоков + во время запущенного сбора данных, однако если эти потоки сильно различаются + в частоте, то рассчитанные библиотекой значения буфера и шага прерывания + могут не подходить для изменившихся значений (см. @ref sect_sync_mode_buf) + @param[in] hnd Описатель модуля. + @param[in] streams Набор флагов #t_l502_streams, указывающих, какие потоки + должны быть разрешены. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_StreamsEnable(t_l502_hnd hnd, uint32_t streams); +/****************************************************************************//** + @brief Запрещение синхронных потоков на ввод/вывод. + + Функция запрещает передачу синхронных данных для указанных потоков. + Не указанные потоки сохраняют свое разрешенное или запрещенное состояние. + Функция, противоположная по смыслу L502_StreamsEnable(). + @param[in] hnd Описатель модуля. + @param[in] streams Набор флагов #t_l502_streams, указывающих, какие потоки + должны быть запрещены. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_StreamsDisable(t_l502_hnd hnd, uint32_t streams); + +/***************************************************************************//** + @brief Запуск синхронных потоков ввода/вывода. + + Функция запуска синхронных потоков данных. Все синхронные потоки тактируются + от общей опорной частоты. Если был установлен внутренний старт синхронизации, + то синхронизация потоков начнется при выполнении данной функции, в противном + случае по данной функции модуль перейдет в состояние ожидания внешнего + признака начальной синхронизации. + + Также функция осуществляет инициализацию канала DMA на ввод данных из платы, + если был разрешен поток АЦП или синхронного ввода цифровых линий, + и инициалзицую канала DMA на вывод, если был разрешен хотя бы один поток на + вывод, но не была вызвана функция L502_PreloadStart() (однако в этом случае + начало вывода не совпадет с началом ввода). + + @param[in] hnd Описатель модуля. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_StreamsStart(t_l502_hnd hnd); + +/***************************************************************************//** + @brief Останов синхронных потоков ввода/вывода. + + Функция останова синхронных потоков ввода/вывода данных. После выполнению + этой функции модуль завершает генерацию опорной частоты синхронизации (или + использовать внешную частоту синхронизации) и останавливает синхронную + передачу данных. + @param[in] hnd Описатель модуля. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_StreamsStop(t_l502_hnd hnd); + + +/***************************************************************************//** + @brief Проверка, запущен ли синхронный ввод/вывод. + + Функция проверяет запущен ли синхронный ввод вывод с помощью L502_StreamsStart() + или какой-либо внутренней логикой в прошивки BlackFin. + Если сбор данных не запущен, то функция возвращает ошибку + #L502_ERR_STREAM_IS_NOT_RUNNING, если запущен, то нулевой код ошибки + + @param[in] hnd Описатель модуля. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_IsRunning(t_l502_hnd hnd); + +/***************************************************************************//** + @brief Чтение данных АЦП и цивровых входов из модуля. + + Функция считывает данные от модуля, которые были переданы по DMA в буфер + драйвера. Функция принимает отсчеты в специальном индексном формате, + в котором содержится информация, что это за данные (значения цифровых входов + или отсчеты АЦП) и дополнительная информация для АЦП (номер канала, режим). + Для разбора полученных отсчетов используется функция L502_ProcessData(). + + Если в буфере драйвера сейчас находится меньше отсчетов, чем было запрошено, + то функция будет ожидать пока придет заданноеколичество данных или + пока не истечет указанный таймаут. В последнем случае функция возвратит + столько отсчетов, сколько было в буфере при истечении таймаута. + + Количество готовых для чтения отсчетов в буфере драйвера можно при желании + узнать с помощью функции L502_GetRecvReadyCount(). + + До вызовов L502_Recv() синхронный поток сбора данных должен быть уже запущен + с помощью L502_StreamsStart(). + + @param[in] hnd Описатель модуля. + @param[out] buf Буфер, в которые будут сохранены отсчеты. + @param[in] size Количество считываемых отсчетов (32-битных слов). + @param[in] tout Таймаут на прием данных в мс. + @return Если < 0 - код ошибки. + Если >= 0 - количество считанных слов. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_Recv(t_l502_hnd hnd, uint32_t* buf, uint32_t size, uint32_t tout); + + +/***************************************************************************//** + @brief Передача потоковых данных ЦАП и цифровых выходов в модуль. + + Функция записывает данные на передачу во внутренний буфер драйвера, после + чего эти данные будут по DMA переданы в модуль. + Данные должны быть в специальном формате, который определяет, что это за + данные (цифровые выходы, канал ЦАП1 или канал ЦАП2). Подготовить данные + в нужном формате можно с помощью L502_PrepareData(). + + Если буфер драйвера на передачу заполнен, то функция будут ждать пока + он не освободится или пока не истечет указанный таймаут. + Количество свободного места в буфере можно при желании узнать с помощью + функции L502_GetSendReadyCount(). + + Возвращение означает что данные переданы в буфер драйвера, а не то что они + уже дошли до модуля и выведены. + + Перед вызовом этой функции должна быть запущена предзагрузка данных на + вывод с помощью L502_PreloadStart(). + + @param[in] hnd Описатель модуля. + @param[in] buf Буфер со словами, которые необходимо передать модулю + @param[in] size Количество передаваемых отсчетов (32-битных слов). + @param[in] tout Таймаут на передачу (в буфер драйвера) данных в мс. + @return Если < 0 - код ошибки. + Если >= 0 - количество записанных слов. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_Send(t_l502_hnd hnd, const uint32_t* buf, uint32_t size, uint32_t tout); + + + +/***************************************************************************//** + @brief Обработка принятых отсчетов АЦП от модуля. + + Функция выполняет обработку отсчетов АЦП, прочитанных с помощью L502_Recv(). + Функция проверяет служебную информацию из входного массива и переводит отсчеты + АЦП либо в коды, либо в вольты (если указан флаг #L502_PROC_FLAGS_VOLT). + + Функция используется, когда не запущен синхронный ввод с цифровых линий и + отсчеты АЦП являются единственными приходящими от модуля данными (если + в принятом потоке будут другие данные - то они будут отброшены). + + Если запущен синхронный ввод с цифровых линий, то следует использовать + L502_ProcessData(), которая выделяет данные с цифровых линий в отдельный + массив. + + @param[in] hnd Описатель модуля. + @param[in] src Входной массив отсчетов, принятых с помощью L502_Recv(). + @param[out] dest Массив, в который будут сохранены преобразованные данные + от АЦП. + @param[in,out] size На входе - количество слов в массиве src, на выходе - + количество сохраненных преобразованных значений в + массиве dest + @param[in] flags Набор флагов из #t_l502_proc_flags + @return Код ошибки. + ****************************************************************************/ +LPCIE_EXPORT(int32_t) L502_ProcessAdcData(t_l502_hnd hnd, const uint32_t* src, double *dest, + uint32_t *size, uint32_t flags); + + +/***************************************************************************//** + @brief Обработка принятых от модуля данных. + + Функция выполняет обработку данных, прочитанных с помощью L502_Recv(). + Функция проверяет служебную информацию из входного массива, разбивает данные + на два массива - данные от АЦП, которые переводятся в тип double, и данные + от синхронного цифрового ввода. + + Данные от АЦП так же могут быть переведены в вольты. При этом данные АЦП + приходят от модуля уже откалиброванными с помощью калибровочных коэффициентов, + так как калибровка выполняется аппаратно. Если данные АЦП не переводятся + в Вольты и при этом не были изменены заводские калибровочные коэффициенты, + то возвращенное значение равное #L502_ADC_SCALE_CODE_MAX соответствует + напряжению равному максимальному для используемого диапазона. + + Кроме того, функция разбирает сообщения, передаваемые в потоке данных + (например, сообщение о переполнении буфера). + @param[in] hnd Описатель модуля. + @param[in] src Входной массив отсчетов, принятый с помощью L502_Recv(). + @param[in] size Количество отсчетов (32-битных слов) в массиве src. + @param[in] flags Набор флагов из #t_l502_proc_flags, управляющих + поведением функции. Может быть указано несколько флагов + через логическое "ИЛИ". + @param[out] adc_data Массив, в который будут сохранены данные от АЦП, + преобразованные в соответствии с указанными флагами. + Может быть NULL, если не нужно сохранять данные от АЦП + (тогда adc_data_size должен быть тоже NULL, или в + переменной передан размер 0). + @param[in,out] adc_data_size На входе в данном параметре передается резмер + буфера adc_data. Если данных от АЦП во входном массиве + будет больше adc_data_size, то в adc_data будет + сохранено только первые adc_data_size отсчетов. + На выходе при успешном завершении функции в данную + переменную записывается количество сохранных отсчетов + АЦП. + Указатель может быть равен NULL, если adc_data = NULL + @param[out] din_data Массив, в который будут сохранены отчеты с синхронного + цифрового ввода. Младшие 18 битов соответствуют + состояниям линий, старшие 14 - резерв. + Может быть NULL, если не нужно сохранять данные от + синхронного ввода. + @param[in,out] din_data_size Аналогично параметру adc_data_size в этом + параметре передается размер буфера din_data в отсчетах, + а на выходе сохраняется количество реально сохраненных + отсчетов цифрового ввода. Может быть NULL, если + din_data = NULL. + @return Код ошибки. + ****************************************************************************/ +LPCIE_EXPORT(int32_t) L502_ProcessData(t_l502_hnd hnd, const uint32_t* src, uint32_t size, + uint32_t flags, double *adc_data, uint32_t *adc_data_size, + uint32_t *din_data, uint32_t *din_data_size); + +/***************************************************************************//** + @brief Обработка принятых от модуля данных с пользовательскими данными. + + Функция аналогична L502_ProcessData(), но позволяет также выделить + пользовательские данные из потока. Пользовательскими данными считаются все + отсчеты, которые не являются данными АЦП, данными цифрового ввода + или сообщениями. + Пользовательские данные складываются без изменений в массив usr_data + (если он не равен нулю). + Данная функция предназначена в первую очередь для программистов, которые + будут использовать модифицированную прошивку сигнального процессора BlackFin. + @param[in] hnd Описатель модуля. + @param[in] src Входной массив отсчетов, принятый с помощью + L502_Recv(). + @param[in] size Количество отсчетов (32-битных слов) в массиве src. + @param[in] flags Набор флагов из #t_l502_proc_flags. + @param[out] adc_data Массив, в который будут сохранены данные от АЦП + (см. L502_ProcessData()). + @param[in,out] adc_data_size см. L502_ProcessData() + @param[out] din_data Массив, в который будут сохранены отчеты с + синхронного цифрового ввода. См. L502_ProcessData(). + @param[in,out] din_data_size см. L502_ProcessData(). + @param[out] usr_data Массив, в который будут сохранены пользовательские + данные без изменения их формата. + @param[in,out] usr_data_size В этом параметре передается размер буфера usr_data + а на выходе сохраняется количество реально сохраненных + отсчетов пользовательских данных. + Может быть NULL только если usr_data = NULL. + @return Код ошибки. + ****************************************************************************/ +LPCIE_EXPORT(int32_t) L502_ProcessDataWithUserExt(t_l502_hnd hnd, const uint32_t* src, uint32_t size, + uint32_t flags, double *adc_data, + uint32_t *adc_data_size, uint32_t *din_data, + uint32_t *din_data_size, + uint32_t *usr_data, uint32_t *usr_data_size); + + + +/***************************************************************************//** + @brief Подготовка данных для вывода в модуль. + + Функция принимает данные из трех массивов - данные на цифровые выходы, + отсчеты первого и второго канала ЦАП. В качестве массива может быть передан + нулевой указатель, если данные из этого источника не требуются. + Все используемые массивы должны быть одинакового размера и функция их + равномерно перемешивает в общий поток, преобразуя в нужный для модуля формат. + + Выходной массив должен будет содержать n*size отсчетов, где n - количество + используемых входных массивов (от 1 до 3). + + Значения цифровых выходов представляют собой 32-битные слова, младшие 16-бит + которых определяют значения выводов, а старшие - флаги из + #t_l502_digout_word_flags, которые могут использоваться в частности для + перевода одной (или обеих) из половин выводов в третье состояние. + + В качестве значений ЦАП могут использоваться как коды, так и Вольты, + в зависимости от переданных флагов, и к выводимым значениям могут быть + применены калибровочные коэффициенты. + Если используются коды ЦАП с включенной калибровкой, то код + #L502_DAC_SCALE_CODE_MAX определяет код, соответствующий +5V. + + @param[in] hnd Описатель модуля. + @param[in] dac1 Входной массив отсчетов первого канала ЦАП или + NULL, если не используется. + @param[in] dac2 Входной массив отсчетов второго канала ЦАП или + NULL, если не используется. + @param[in] digout Входной массив со значениями цифровых выводов + или NULL, если не используется. + @param[in] size Размер каждого из используемых входных массивов. + @param[in] flags Флаги, управляющие работой функции, из + #t_l502_dacout_flags. + @param[out] out_buf Выходной массив, в который будут сохранены + сформированные отсчеты. Должен быть размера + n*size (n - количество используемых входных + массивов) + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_PrepareData(t_l502_hnd hnd, const double* dac1, const double* dac2, + const uint32_t* digout, uint32_t size, int32_t flags, + uint32_t* out_buf); + +/***************************************************************************//** + @brief Получить количество отсчетов в буфере потока на ввод. + + Функция возвращает количество отсчетов, которые были приняты из модуля + во внутренний буфер в драйвере и готовы для считывания с помощью + L502_Recv(). + То есть если в L502_Recv() передать значение, которое вернула данная функция, + то L502_Recv() вернет это количество данных без ожидания (так как они уже + в буфере). + @param[in] hnd Описатель модуля. + @param[out] rdy_cnt Количество готовых к приему отсчетов. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetRecvReadyCount(t_l502_hnd hnd, uint32_t *rdy_cnt); + + +/***************************************************************************//** + @brief Получить размер свободного места в буфере потока на вывод. + + Функция возвращает количество отсчетов, соответствующее свободному месту + в буфере драйвера на передачу в модуль. + Это количество отсчетов может быть передано с помощью L502_Send() без + ожидания. + @param[in] hnd Описатель модуля. + @param[out] rdy_cnt Количество свободных отсчетов в буфере + на передачу. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetSendReadyCount(t_l502_hnd hnd, uint32_t *rdy_cnt); + + +/***************************************************************************//** + @brief Получить номер следующего ожидаемого логического канала АЦП для + обработки. + + Функция возвращает номер логического канала АЦП, который должен быть + обработан первым при следующем вызове L502_ProcessData()/ + L502_ProcessAdcData() в случае, если поток данных непрерывен. + + По сути, это номер логического канала, слудущий за логическим каналом + последнего обработанного до этого отсчета АЦП. + Может быть использовано при обработке блоков данных не кратных целому + количеству кадров. + Если перед L502_ProcessData() вызывать данную функцию, то она вернет номер + логического канала, соответствющий первому отсчету АЦП, + обработаному последующим вызовом L502_ProcessData(). + + Например, если установлено 7 логических каналов, а в L502_ProcessData() + передано для обработки кратное 7 количество отсчетов, то последующий вызов + L502_GetNextExpectedLchNum() вернет номер канала равный 0 (так как обработано + целое число кадров и ожидается снова начало кадра). + Если в L502_ProcessData() передан массив с 7*n + 5 отсчетами АЦП, то следующим + ожидаемым каналом будет логический канал с номером 5 (обработаны каналы + 0,1,2,3,4 из неполного кадра). + + @param[in] hnd Описатель модуля. + @param[out] lch Номер логического канала (начиная с нуля). + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetNextExpectedLchNum(t_l502_hnd hnd, uint32_t *lch); + + +/***************************************************************************//** + @brief Начало подготовки вывода синхронных данных + + Функция должна вызываться перед началом предзагрузки потоковых синхронных + данных на вывод. Для начала выдачи синхронных данных одновременно с началом + синхронного ввода, к моменту начала сбора часть данных должна быть уже + загружена в модуль до вызова L502_StreamsStart(). + + Данная функция инициализирует DMA канал на передачу данных на вывод. + После вызова этой функции можно загрузить часть данных на вывод + с помощью L502_Send(). + + @param[in] hnd Описатель модуля. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_PreloadStart(t_l502_hnd hnd); + + + +/***************************************************************************//** + @brief Начало загрузки циклического сигнала на вывод + + По вызову этой функции в драйвере выделяется место под циклический буфер на + вывод. Должна вызываться перед загрузкой циклических данных с помощью + L502_Send(). + + Для успешного выполнения в драйвере должен быть свободный буфер (используется + двойная буферизация) - т.е. функция не может быть вызвана сразу после предыдущего + L502_OutCycleSetup(). Кроме того не должен был быть использован потоковый + вывод. + + @param[in] hnd Описатель модуля. + @param[in] size Количество отсчетов в выводимом циклическом сигнале + суммарно для всех используемых каналов вывода. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_OutCycleLoadStart(t_l502_hnd hnd, uint32_t size); + +/***************************************************************************//** + @brief Установка ранее загруженного циклического сигнала на вывод + + По вызову этой функции ранее загруженный циклический буфер становится активным. + Если синхронный ввод-вывод запущен (через L502_StreamsStart()), то по этой + функции сигнал будет выдаваться на выход, иначе выдача начнется при запуске + синхронного ввода-вывода. + + Если до этого уже выводился циклический сигнал, то смена на новый произойдет + в конце цикла предыдущего сигнала, если не указан флаг + #L502_OUT_CYCLE_FLAGS_FORCE. + + Данная функция должна быть вызвана только после вызова L502_OutCycleLoadStart() + и загрузки указанного в ней количества отсчетов в буфер! + + @param[in] hnd Описатель модуля. + @param[in] flags Флаги из #t_l502_out_cycle_flags. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_OutCycleSetup(t_l502_hnd hnd, uint32_t flags); + + +/***************************************************************************//** + @brief Останов вывода циклического сигнала + + По вызову этой функции прекращается выдача ранее установленного циклического + сигнала с помощью L502_OutCycleSetup(). Остановка осуществляется по + после выдачи последнего семпла в периоде (но функция возвращает управление + сразу, делая только запрос на останов), что позволяет знать какие значения + останутся на выходах. + + При вызове же L502_StreamsStop() (или при запрещении всех потоков + на вывод через L502_StreamsDisable()) останов всех потоков происходит сразу + и точную точка не извествна. + + @param[in] hnd Описатель модуля. + @param[in] flags Флаги из #t_l502_out_cycle_flags. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_OutCycleStop(t_l502_hnd hnd, uint32_t flags); + + + + + +/***************************************************************************//** + @brief Установка размера буфера в драйвере для синхронного ввода или вывода. + + Функция устанавливает размер буфера в драйвере, который используется + для временного хранения данных на прием или на передачу. + Предназначена для случаев, когда пользователя по каким-либо причинам не + удовлетворяет рассчитываемое библиотекой значение по-умолчанию + + @param[in] hnd Описатель модуля. + @param[in] dma_ch Определяет, устанавливается размер буфера на ввод + или на вывод (значение из #t_l502_dma_ch). + @param[in] size Размер буфера в 32-битных отсчетах + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetDmaBufSize(t_l502_hnd hnd, uint32_t dma_ch, uint32_t size); + +/***************************************************************************//** + @brief Установка шага прерывания при передаче потока по DMA. + + Функция устанавливает шаг для генерации прерываний при передаче синхронного + потока данных на ввод или на вывод. + После передачи указанного количества отсчетов по DMA между буфером + драйвера и модулем, модуль генерирует прерывание и драйвер обновляет счетчик + переданных данных. + Даннай функция предназначена для пользователей, которых не устроит + автоматически рассчитываемое библиотекой значение. + + @param[in] hnd Описатель модуля. + @param[in] dma_ch Определяет, шаг прерывания устанавливается на ввод + или на вывод (значение из #t_l502_dma_ch). + @param[in] step Шаг прерывания в 32-битных отсчетах + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetDmaIrqStep(t_l502_hnd hnd, uint32_t dma_ch, uint32_t step); + +/** @} */ + + + + +/***************************************************************************//** + @addtogroup func_dsp Функции для работы с сигнальным процессором + @{ +*******************************************************************************/ +/***************************************************************************//** + @brief Загрузка прошивки сигнального процессора BlackFin. + + Функция загружает прошивку сигнального процессора из указанного файла в + процессор и запускает ее, проверяет правильность загрузки путем получения + версии прошивки (через специальную команду). + Прошивка должна быть в бинарном формате LDR. + @param[in] hnd Описатель модуля. + @param[in] filename Имя файла с загружаемой прошивкой. + @return Код ошибки. + *****************************************************************************/ +LPCIE_EXPORT(int32_t) L502_BfLoadFirmware(t_l502_hnd hnd, const char* filename); + + + +/***************************************************************************//** + @brief Проверка, загружена ли прошивка BlackFIn. + + Функция передает команды процессору BlackFin для получения версии прошивки и + ее состояния. Успешное выполнение команд свидетельствует о том, что в + BlackFin загружена действительная прошивка. + Кроме того прошивке передается информация о модуле (наличие опций, версия + ПЛИС и т.д.) для внутреннего использования. + В случае успеха модуль переводится в режим DSP. + + Данная функция может служить для проверки была ли загружена прошивка раньше + (чтобы не загружать повторно) или для проверки была ли она загружена через + JTAG-интерфейс. + + @param[in] hnd Описатель модуля. + @param[out] version Если указатель не нулевой, то в данной переменной + возвращается версия прошивки BlackFin в случае + успешной проверки. + @return Код ошибки. + *****************************************************************************/ +LPCIE_EXPORT(int32_t) L502_BfCheckFirmwareIsLoaded(t_l502_hnd hnd, uint32_t *version); + +/***************************************************************************//** + @brief Чтение блока данных из памяти сигнального процессора. + + Функция считывает блок данных напрямую из памяти процессора. Может быть + прочитаны данные, как из внутренней памяти (L1), так и из внешней SDRAM. + Для выполнения этой функции в BlackFin должна быть загружена его прошивка. + + Функция предназначена в первую очередь для пользователей, пишущих свою + программу для сигнального процессора. + + @param[in] hnd Описатель модуля. + @param[in] addr Адрес памяти, начиная с которого будет считан блок + данных. + @param[out] regs Массив, в который будут сохранено прочитанное + содержимое памяти. + @param[in] size Количество считываемых 32-битных слов. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_BfMemRead(t_l502_hnd hnd, uint32_t addr, uint32_t* regs, + uint32_t size); + +/***************************************************************************//** + @brief Запись блока данных в память сигнального процессора. + + Функция записывает блок данных напрямую в памяти процессора BlackFin. Блок + данных должен быть всегда кратен 8 32-битным словам (32 байтам). + Запись может осуществляться как во внутреннюю память (L1), так и во внешнюю SDRAM. + Для выполнения этой функции в BlackFin должна быть загружена его прошивка. + + Функция предназначена в первую очередь для пользователей, пишущих свою + программу для сигнального процессора. + + @note Следует быть осторожным, т.к. запись в область данных, используемую + программой может привести к ее неработоспособности. + + @param[in] hnd Описатель модуля. + @param[in] addr Адрес памяти, начиная с которого будет записан блок + данных. + @param[out] regs Массив с данными для записи в сигнальный процессор. + @param[in] size Количество записываемых данных в 32-битных словах + (должно быть кратно 8). + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_BfMemWrite(t_l502_hnd hnd, uint32_t addr, + const uint32_t* regs, uint32_t size); + + +/***************************************************************************//** + @brief Передача управляющей команды сигнальному процессору. + + Функция предназначена для передачи пользовательских управляющих команд + процессору для пользователей, пишущих свою прошивку BlackFin. + + Управление работой сигнального процессора штатным образом осуществляется + через управляющие команды, которые записываются в специальную область + памяти сигнального процессора. Сигнальный процессор обрабатывает команду + и по завершению записывает в эту же область результат. + + Команды делаться на стандартные, которые используются библиотекой l502api и + пользовательские, которые может пользователь определять по своему усмотрению. + Пользовательские команды начинаются с кода L502_BF_CMD_CODE_USER (0x8000). + + @param[in] hnd Описатель модуля. + @param[in] cmd_code Код команды - определяет, что за команда выполняется. + @param[in] par Параметр, передаваемый с командой (значение зависит + от кода команды). + @param[in] snd_data Опциональные данные, передаваемые вместе с командой. + Если данные не передаются, то должен передаваться + нулевой указатель и snd_size = 0. + @param[in] snd_size Количество 32-битных слов, передаваемых в snd_data + @param[out] rcv_data Массив, в который будут переданы данные, возвращенные + процессором по завершению команды. Если данные не + должны возвращаться, то должен передаваться нулевой + указатель, а rcv_size = 0. + @param[in] rcv_size Количество 32-битных слов, которое ожидается, что + вернет процессор по выполнению команды. Массив + rcv_data должен быть рассчитан на данное количество + слов. + @param[in] tout Таймаут в течении которого будет ожидаться, когда + процессор завершит выполнение команды. Функция + возвратит управление либо по завершению команды, + либо по таймауту. + @param[out] recvd_size Если не является нулевым указателем, то в эту + переменную будет сохранено количество 32-битных слов, + которое реально вернул процессор после выполнения + команды (процессор имеет право вернуть меньше данных, + чем запрашивалось в rcv_size). + @return Код ошибки. Если процессор выполнил команду с ненулевым + кодом завершения, то этот код и будет возвращен + функцией. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_BfExecCmd(t_l502_hnd hnd, uint16_t cmd_code, uint32_t par, + const uint32_t* snd_data, uint32_t snd_size, + uint32_t* rcv_data, uint32_t rcv_size, uint32_t tout, uint32_t* recvd_size); +/** @} */ + + + +/***************************************************************************//** + @addtogroup func_flash Функции для работы с Flash-памятью модуля + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Чтение блока данных из Flash-памяти. + + Функция считывает массив данных из Flash-памяти модуля в массив, переданный + пользователем. Для считывания не нужно специальное разрешение - оно доступно + всегда. + @param[in] hnd Описатель модуля. + @param[in] addr Адрес начала блока. + @param[out] data Массив, куда будут сохранены считанные данные + (должен быть не меньше size байт). + @param[in] size Количество байт для чтения. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_FlashRead(t_l502_hnd hnd, uint32_t addr, uint8_t* data, + uint32_t size); +/***************************************************************************//** + @brief Запись блока данных во Flash-память модуля. + + Функция записывает переданный массив данных во Flash-память модуля. + Эта область должна быть предварительно стерта с помощью L502_FlashErase() и + до начала изменения должна быть вызвана функция L502_FlashWriteEnable(), + чтобы разрешить любое изменение содержимого Flash-памяти. + Пользователю для записи доступны только первые #L502_FLASH_USER_SIZE байт + Flash-памяти. + @param[in] hnd Описатель модуля. + @param[in] addr Адрес начала блока. + @param[in] data Массив c записываемыми данными (должен быть не меньше + size байт). + @param[in] size Количество байт для записи. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_FlashWrite(t_l502_hnd hnd, uint32_t addr, + const uint8_t* data, uint32_t size); +/***************************************************************************//** + @brief Стирание блока во Flash-памяти. + + Функция стирает блок во Flash-памяти модуля (все ячейки будут читаться как + 0xFF). Адрес и размер должны быть кратны 4096 байт! + Перед вызовом этой функции запись должна быть разрешена запись + в пользовательскую область с помощью L502_FlashWriteEnable(). + @param[in] hnd Описатель модуля. + @param[in] addr Адрес начала блока (должен быть кратен 4K). + @param[in] size Количество байт для стирания (кратно 4K). + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_FlashErase(t_l502_hnd hnd, uint32_t addr, uint32_t size); +/***************************************************************************//** + @brief Разрешение записи в пользовательскую область Flash-памяти. + + Функция разрешает запись в пользовательскую область Flash-памяти (первые + #L502_FLASH_USER_SIZE байт). Должна быть вызвана до того, как + можно будет использовать L502_FlashErase() и L502_FlashWrite() для изменения + содержимого пользовательской области памяти. После завершения изменения + следует вызвать L502_FlashWriteDisable(). + @param[in] hnd Описатель модуля. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_FlashWriteEnable(t_l502_hnd hnd); +/***************************************************************************//** + @brief Запрет записи в пользовательскую область Flash-памяти. + + Функция запрещает запись в пользовательскую область Flash-памяти модуля + (первые #L502_FLASH_USER_SIZE байт). Должна быть вызвана после того, как + нужные данные в пользовательской области были изменены с помощью + L502_FlashErase() и L502_FlashWrite(), чтобы защитить пользовательскую + область от случайной изменения в дальнейшем. + @param[in] hnd Описатель модуля. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_FlashWriteDisable(t_l502_hnd hnd); + +/** @} */ + +/***************************************************************************//** + @addtogroup func_misc Дополнительные вспомогательные функции. + @{ +*******************************************************************************/ +/**************************************************************************//** + @brief Получить версию библиотеки. + + Функция возвращает версию библиотеки l502api.dll. + Версия возвращается в виде 32-битного числа. + Строковое представление возвращенной версии - четыре числа, + старшее соответствует старшему байту, младшее - младшему. + + Старший байт - можорная версия, второй по старшинству байт - минорная, + третий - ревизия, четвертый - номер сборки (не используется - всегда 0) + + @return 32-битное число, представляющее собой версию библиотеки + *****************************************************************************/ +LPCIE_EXPORT(uint32_t) L502_GetDllVersion(void); + + + +/***************************************************************************//** + @brief Получение строки об ошибке. + + Функция возвращает строку, соответствующую переданному коду ошибки. + В настроящее время возвращается всегда русская версия строки (возможно в + будущем будет возможность сменить язык глобальной функцией). + + @note Следует учесть, что в ОС Windows строка возвращается в + стандартной для Windows кодировке CP1251, в то время как в Linux + используется кодировка UTF-8. + @param[in] err Код ошибки, для которого нужно вернуть строку. + @return Указатель на строку, соответствующую коду ошибки + ******************************************************************************/ +LPCIE_EXPORT(const char*) L502_GetErrorString(int32_t err); + + +/***************************************************************************//** + @brief Моргание светодиодом на передней панели. + + При вызове этой функции, если не запущен синхронный ввод/вывод, происходит + кратковременное затухание красного цвета светодиода на передней панели. + Может быть использована для визуальной идентификации модуля после его + открытия. + + При запущенном синхронном вводе/выводе светодиод на передней панели всегда + горит зеленым цветом и данная функция не влияет на его состояние. + + @param[in] hnd Описатель модуля. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_LedBlink(t_l502_hnd hnd); + +/***************************************************************************//** + @brief Установка подтягивающих резисторов на входных линиях. + + Функция может использоваться для включения подтягивающих резисторов на + цифровых входах. Отдельно можно задавать включены или отключены подтяжки + на младшей половине цифровых линий, старшей половине, на линии SYN1 и на + линии SYN2. На не указанных линиях подтягивающие резисторы будут отключены, + если они были включены до этого. + При включении питания все подтягивающие резисторы отключены. + + @param[in] hnd Описатель модуля. + @param[in] pullups Флаги (из #t_l502_pullups), определяющие, на каких + линиях включены подтягивающие резисторы. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetDigInPullup(t_l502_hnd hnd, uint32_t pullups); + +/** @} */ +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/l502/l502api_eeprom.c b/l502/l502api_eeprom.c new file mode 100644 index 0000000..bbc05fe --- /dev/null +++ b/l502/l502api_eeprom.c @@ -0,0 +1,171 @@ +#include "l502api_private.h" +#include "ltimer.h" +#include "l502_fpga_regs.h" + +#define L502_EEPROM_BIG_SECTOR_SIZE (64*1024) +#define L502_EEPROM_SMALL_SECTOR_SIZE (4*1024) + +#define L502_FLASH_WRITE_TOUT 500 +#define L502_FLASH_ERASE_TOUT 500 + +/** биты регистра статуса */ +typedef enum { + SST25_STATUS_BUSY = 0x01, + SST25_STATUS_WEL = 0x02, + SST25_STATUS_BP0 = 0x04, + SST25_STATUS_BP1 = 0x08, + SST25_STATUS_BP2 = 0x10, + SST25_STATUS_BP3 = 0x20, + SST25_STATUS_AAI = 0x40, + SST25_STATUS_BPL = 0x80 +} t_sst25_status_bits; + +static uint8_t f_prot_bits[] = { + SST25_STATUS_BP2 | SST25_STATUS_BP1 | SST25_STATUS_BP0, + SST25_STATUS_BP2 | SST25_STATUS_BP0, + SST25_STATUS_BP2, + SST25_STATUS_BP0, + 0 +}; + + + +static LINLINE int32_t f_eeprom_rd_status(t_x502_hnd hnd, uint8_t* stat) { + uint32_t val; + int32_t err = l502_port_fpga_reg_read(hnd, L502_REGS_EEPROM_RD_STATUS, &val); + if (err == X502_ERR_OK) + *stat = (val>>24)&0xFF; + return err; +} + +static LINLINE int32_t f_eeprom_wr_status(t_x502_hnd hnd, uint8_t stat) { + int32_t err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_STATUS_EN, 1); + if (err == X502_ERR_OK) { + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_STATUS, stat); + } + return err; +} + + +static int32_t f_eeprom_wr_byte(t_x502_hnd hnd, uint32_t addr, uint8_t val) { + int32_t err = X502_ERR_OK; + t_ltimer tmr; + uint8_t stat = SST25_STATUS_BUSY; + + /* разрешаем запись в EEPROM */ + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_EN, 1); + if (err == X502_ERR_OK) { + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_BYTE, ((addr & 0xFFFFFF) << 8) | val); + if (err != X502_ERR_OK) + l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_DIS, 1); + } + + if (err == X502_ERR_OK) + ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(L502_FLASH_WRITE_TOUT)); + + /* Ожидаем завершения записи */ + while ((err == X502_ERR_OK) && (stat & SST25_STATUS_BUSY) && !ltimer_expired(&tmr)) { + err = f_eeprom_rd_status(hnd, &stat); + } + + if ((err == X502_ERR_OK) && (stat & SST25_STATUS_BUSY)) { + err = X502_ERR_FLASH_WRITE_TOUT; + } + return err; +} + + + + + +int32_t l502_iface_flash_rd(t_x502_hnd hnd, uint32_t addr, uint8_t* data, uint32_t size) { + int32_t err = X502_ERR_OK; + uint32_t val; + + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_SET_RD_ADDR, (addr & 0xFFFFFF) << 8); + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_read(hnd, L502_REGS_EEPROM_RD_DWORD, &val); + if (err == X502_ERR_OK) { + unsigned int i; + for (i=0; (i < sizeof(val)) && size; i++, size--) { + *data++ = val & 0xFF; + val >>= 8; + } + } + return err; +} + +int32_t l502_iface_flash_wr(t_x502_hnd hnd, uint32_t addr, const uint8_t* data, uint32_t size) { + uint32_t i; + int32_t err = X502_ERR_OK; + for (i=0; (i < size) && (err == X502_ERR_OK); i++) { + err = f_eeprom_wr_byte(hnd, addr+i, data[i]); + } + return err; +} + +int32_t l502_iface_flash_erase(t_x502_hnd hnd, uint32_t addr, uint32_t size) { + int32_t err = X502_ERR_OK; + if (((addr & (L502_EEPROM_SMALL_SECTOR_SIZE-1)) || + (size & (L502_EEPROM_SMALL_SECTOR_SIZE-1)))) { + err = X502_ERR_FLASH_SECTOR_BOUNDARY; + } + + while((size != 0) && (err == X502_ERR_OK)) { + uint32_t er_size; + /* разрешаем запись в EEPROM */ + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_EN, 1); + if (err == X502_ERR_OK) { + uint8_t stat = SST25_STATUS_BUSY; + t_ltimer tmr; + /* проверяем - можем ли стереть целиком большой сектор или + придется писать в мелкий */ + if ((size >= L502_EEPROM_BIG_SECTOR_SIZE) && + !(size & (L502_EEPROM_BIG_SECTOR_SIZE-1))) { + er_size = L502_EEPROM_BIG_SECTOR_SIZE; + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_ERASE_64K, addr<<8); + } else { + er_size = L502_EEPROM_SMALL_SECTOR_SIZE; + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_ERASE_4K, addr<<8); + } + + if (err == X502_ERR_OK) + ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(L502_FLASH_ERASE_TOUT)); + + /* ожидаем завершения стирания */ + while ((err == X502_ERR_OK) && (stat & SST25_STATUS_BUSY) && !ltimer_expired(&tmr)) { + err = f_eeprom_rd_status(hnd, &stat); + } + + if ((err == X502_ERR_OK) && (stat & SST25_STATUS_BUSY)) { + err = X502_ERR_FLASH_ERASE_TOUT; + } + + /* запрещаем запись, если произошла ошибка. при успешном стирании + запись будут запрещена атоматически */ + if (err != X502_ERR_OK) + l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_EN, 0); + + if (err == X502_ERR_OK) { + addr += er_size; + size -= er_size; + } + } + } + return err; +} + +int32_t l502_iface_flash_set_prot(t_x502_hnd hnd, uint32_t prot, const uint8_t* prot_data, uint32_t size) { + int32_t err = X502_ERR_OK; + uint16_t prot_code = 0; + if (size == 2) { + prot_code = ((uint16_t)prot_data[1] << 8) | prot_data[0]; + } + + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_STATUS_EN, 1); + if ((err == X502_ERR_OK) && (prot_code != 0)) + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_HARD_WR_STATUS_EN, prot_code); + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_STATUS, f_prot_bits[prot]); + return err; +} diff --git a/l502/l502api_private.h b/l502/l502api_private.h new file mode 100644 index 0000000..2529423 --- /dev/null +++ b/l502/l502api_private.h @@ -0,0 +1,54 @@ +#ifndef L502API_PRIVATE_H +#define L502API_PRIVATE_H + +#include "x502api_private.h" +#include "lpcie_ioctls.h" + +#define L502_DEVICE_NAME "L502" + + +typedef struct { +#ifdef _WIN32 + HANDLE file; +#else + int file; +#endif +} t_pci_iface_data; + +#define L502_PCI_IFACE_FILE(hnd) (((t_pci_iface_data*)hnd->iface_data)->file) + +int32_t l502_port_fpga_reg_write(t_x502_hnd hnd, uint32_t reg, uint32_t val); +int32_t l502_port_fpga_reg_read(t_x502_hnd hnd, uint32_t reg, uint32_t *val); +int32_t l502_port_open(t_x502_hnd hnd, const t_x502_devrec *devrec); +int32_t l502_port_free_iface_data(void *intptr); +int32_t l502_port_close(t_x502_hnd hnd); +int32_t l502_port_stream_start(t_x502_hnd hnd, uint32_t ch, uint32_t single); +int32_t l502_port_stream_stop(t_x502_hnd hnd, uint32_t ch, uint32_t flags); +int32_t l502_port_stream_free(t_x502_hnd hnd, uint32_t ch, uint32_t flags); +int32_t l502_port_stream_read(t_x502_hnd hnd, uint32_t *buff, uint32_t size, uint32_t timeout); +int32_t l502_port_stream_write(t_x502_hnd hnd, const uint32_t *buff, uint32_t size, + uint32_t timeout); +int32_t l502_port_stream_set_params(t_x502_hnd hnd, t_lpcie_stream_ch_params *par); +int32_t l502_port_stream_rdy_size(t_x502_hnd hnd, uint32_t ch, uint32_t *rdy_size); +int32_t l502_port_renew_info(t_x502_hnd hnd); +int32_t l502_port_get_drv_ver(t_x502_hnd hnd, uint32_t *ver); +int32_t l502_port_cycle_load_start(t_x502_hnd hnd, uint32_t ch, uint32_t size, uint32_t min_irq_step); +int32_t l502_port_cycle_setup(t_x502_hnd hnd, uint32_t ch, uint32_t evt); +int32_t l502_port_cycle_stop(t_x502_hnd hnd, uint32_t ch, uint32_t evt); +int32_t l502_port_cycle_check_setup(t_x502_hnd hnd, uint32_t ch, uint32_t *done); + + + +int32_t l502_iface_flash_rd(t_x502_hnd hnd, uint32_t addr, uint8_t* data, uint32_t size); +int32_t l502_iface_flash_wr(t_x502_hnd hnd, uint32_t addr, const uint8_t* data, uint32_t size); +int32_t l502_iface_flash_erase(t_x502_hnd hnd, uint32_t addr, uint32_t size); +int32_t l502_iface_flash_set_prot(t_x502_hnd hnd, uint32_t prot, const uint8_t* prot_data, uint32_t size); + +int32_t l502_iface_bf_mem_block_rd(t_x502_hnd hnd, uint32_t addr, uint32_t *block, uint32_t size); +int32_t l502_iface_bf_mem_block_wr(t_x502_hnd hnd, uint32_t addr, const uint32_t *block, uint32_t size); +int32_t l502_iface_bf_firm_load(t_x502_hnd hnd, const char *filename); + +int32_t l502_devlist_gen(t_x502_devrec *info, void *iface_data); + +#endif // L502API_PRIVATE_H + diff --git a/l502/linux/l502_ioctls.h b/l502/linux/l502_ioctls.h new file mode 100644 index 0000000..51148b4 --- /dev/null +++ b/l502/linux/l502_ioctls.h @@ -0,0 +1,78 @@ +#ifndef LPCIE_IOCTLS_H +#define LPCIE_IOCTLS_H + +#include "linux/ioctl.h" +#include "linux/types.h" + + +#define L502_BF_IO_SIZE 8 + +#define LPCIE_DEVNAME_SIZE 32 +#define LPCIE_SERIAL_SIZE 32 +#define LPCIE_SOFTVER_SIZE 32 +#define LPCIE_REVISION_SIZE 16 +#define LPCIE_MODIFICATION_SIZE 16 +#define LPCIE_SPECINFO_SIZE 64 + +/** Информация о модуле */ +typedef struct +{ + char Name[LPCIE_DEVNAME_SIZE]; /**< название модуля ("L-502") */ + char Serial[LPCIE_SERIAL_SIZE]; /**< серийный номер изделия */ + char SoftVer[LPCIE_SOFTVER_SIZE]; /**< версия ПО контроллера */ + char Revision[LPCIE_REVISION_SIZE]; /**< ревизия платы */ + char Modification[LPCIE_MODIFICATION_SIZE]; /**< опции */ + char SpecInfo[LPCIE_SPECINFO_SIZE]; /**< резервная информация */ +} t_lpcie_devinfo; + +typedef struct +{ + __u32 addr; + __u32 val; +} t_lpcie_mem_rw; + +typedef struct +{ + __u32 page_size; /** размер каждой страницы */ + __u32 irq_step; /** количество отсчетов, после которого генерится прерывание */ + __u16 pages_cnt; /** количество страниц */ + __u16 packet_size; /** размер пакета по PCI-Express */ +} t_lpcie_dma_params; + +typedef struct +{ + uint32_t addr; + uint32_t reserv; + uint32_t buf[L502_BF_IO_SIZE]; +} t_l502_bf_rw; + +#define LPCIE_IO_MAGIC 'L' + +#define LPCIE_IOCTL_GET_DEVINFO _IOR(LPCIE_IO_MAGIC, 0x80, t_lpcie_devinfo) +#define LPCIE_IOCTL_TIMER_START _IOW(LPCIE_IO_MAGIC, 0x81, __u32) +#define LPCIE_IOCTL_TIMER_STOP _IO(LPCIE_IO_MAGIC, 0x82) +#define LPCIE_IOCTL_READ_CFG _IOWR(LPCIE_IO_MAGIC, 0x83, __u32) +#define LPCIE_IOCTL_MEM_FPGA_RD _IOWR(LPCIE_IO_MAGIC, 0x86, t_lpcie_mem_rw) +#define LPCIE_IOCTL_MEM_FPGA_WR _IOW(LPCIE_IO_MAGIC, 0x87, t_lpcie_mem_rw) +#define LPCIE_IOCTL_MEM_BLOCK_RD _IOWR(LPCIE_IO_MAGIC, 0x86, t_lpcie_mem_rw) + +#define LPCIE_IOCTL_ADC_START _IO(LPCIE_IO_MAGIC, 0x90) +#define LPCIE_IOCTL_ADC_STOP _IO(LPCIE_IO_MAGIC, 0x91) +#define LPCIE_IOCTL_DAC_START _IO(LPCIE_IO_MAGIC, 0x92) +#define LPCIE_IOCTL_DAC_STOP _IO(LPCIE_IO_MAGIC, 0x93) +#define LPCIE_IOCTL_SET_ADC_DMA_PAR _IOW(LPCIE_IO_MAGIC, 0x94, t_lpcie_dma_params) +#define LPCIE_IOCTL_SET_DAC_DMA_PAR _IOW(LPCIE_IO_MAGIC, 0x95, t_lpcie_dma_params) +#define LPCIE_IOCTL_DAC_RST_BUFS _IO(LPCIE_IO_MAGIC, 0x96) + +#define LPCIE_IOCTL_ADC_GET_RDY _IOR(LPCIE_IO_MAGIC, 0x97, __u32) +#define LPCIE_IOCTL_DAC_GET_FREE _IOR(LPCIE_IO_MAGIC, 0x98, __u32) + +#define LPCIE_IOCTL_DAC_SET_CYCLE_BUF _IOW(LPCIE_IO_MAGIC, 0x99, __u32) +#define LPCIE_IOCTL_DAC_SET_CYCLE_DATA _IOW(LPCIE_IO_MAGIC, 0x9A, __u32) + + +#define LPCIE_IOCTL_BF_RD _IOR(LPCIE_IO_MAGIC, 0xA0, t_l502_bf_rw) +#define LPCIE_IOCTL_BF_WR _IOW(LPCIE_IO_MAGIC, 0xA0, t_l502_bf_rw) +#define LPCIE_IOCTL_BF_HOST_IRQ _IO(LPCIE_IO_MAGIC, 0xA1) + +#endif // LPCIE_IOCTLS_H diff --git a/l502/linux/l502_spec.c b/l502/linux/l502_spec.c new file mode 100644 index 0000000..61ab6b4 --- /dev/null +++ b/l502/linux/l502_spec.c @@ -0,0 +1,369 @@ +#include "../l502api_private.h" +#include "../lpcie_ioctls.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#define LPCIE_CLASS_DIR "/sys/class/lpcie" + + +static int32_t f_ioctl(int fd, unsigned long int req, void* val) { + return ioctl(fd, req, val) ? X502_ERR_IOCTL_FAILD : X502_ERR_OK; +} + + + + +int32_t l502_port_fpga_reg_write(t_x502_hnd hnd, uint32_t reg, uint32_t val) { + t_lpcie_mem_rw mem_wr = {reg,val}; + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_MEMFPGA_WR, &mem_wr); +} + + +int32_t l502_port_fpga_reg_read(t_x502_hnd hnd, uint32_t reg, uint32_t *val) { + t_lpcie_mem_rw mem_wr = {reg,0}; + int32_t err = f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_MEMFPGA_RD, &mem_wr); + if ((err == X502_ERR_OK) && (val != NULL)) + *val = mem_wr.val; + return err; +} + +int32_t l502_port_stream_set_params(t_x502_hnd hnd, t_lpcie_stream_ch_params *par) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_SET_PARAMS, par); +} + +int32_t l502_port_stream_start(t_x502_hnd hnd, uint32_t ch, uint32_t single) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), single ? LPCIE_IOCTL_STREAM_START_SINGLE : + LPCIE_IOCTL_STREAM_START, &ch); +} + +int32_t l502_port_stream_stop(t_x502_hnd hnd, uint32_t ch, uint32_t flags) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_STOP, &ch); +} + +int32_t l502_port_stream_free(t_x502_hnd hnd, uint32_t ch, uint32_t flags) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_FREE, &ch); +} + + +int32_t l502_port_stream_rdy_size(t_x502_hnd hnd, uint32_t ch, uint32_t *rdy_size) { + t_lpcie_get_rdy_par rdy_par = {ch,0}; + int32_t err = f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_GET_RDY_SIZE, &rdy_par); + if ((err == X502_ERR_OK) && (rdy_size != NULL)) + *rdy_size = rdy_par.rdy_size; + return err; +} + +int32_t l502_port_cycle_load_start(t_x502_hnd hnd, uint32_t ch, uint32_t size, uint32_t min_irq_step) { + t_lpcie_cycle_set_par par; + memset(&par, 0, sizeof(par)); + par.ch = ch; + par.size = size; + par.irq_step = min_irq_step; + return f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_LOAD, &par); +} + +int32_t l502_port_cycle_setup(t_x502_hnd hnd, uint32_t ch, uint32_t evt) { + t_lpcie_cycle_evt_par par; + memset(&par, 0, sizeof(par)); + par.ch = ch; + par.evt = evt; + return f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_SWITCH, &par); +} + +int32_t l502_port_cycle_stop(t_x502_hnd hnd, uint32_t ch, uint32_t evt) { + t_lpcie_cycle_evt_par par; + memset(&par, 0, sizeof(par)); + par.ch = ch; + par.evt = evt; + return f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_STOP, &par); +} + +int32_t l502_port_cycle_check_setup(t_x502_hnd hnd, uint32_t ch, uint32_t *done) { + t_lpcie_cycle_check_setup_par par; + int32_t err; + + memset(&par, 0, sizeof(par)); + par.ch = ch; + err = f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_CHECK_SETUP, &par); + if (err == X502_ERR_OK) + *done = par.done; + return err; +} + +int32_t l502_port_renew_info(t_x502_hnd hnd) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_RELOAD_DEVINFO, NULL); +} + +int32_t l502_port_get_drv_ver(t_x502_hnd hnd, uint32_t *ver) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_GET_DRV_VERSION, ver); +} + + +int32_t l502_port_stream_read(t_x502_hnd hnd, uint32_t *buff, uint32_t size, uint32_t timeout) { + struct timeval tmval = {timeout/1000, (timeout%1000)*1000}; + uint32_t rcv_size = 0; + int out = 0; + int32_t err = X502_ERR_OK; + while (!out && (err == X502_ERR_OK) && (rcv_size < size)) { + fd_set rd_set; + int sel = 0; + + FD_ZERO(&rd_set); + FD_SET(L502_PCI_IFACE_FILE(hnd), &rd_set); + sel = select(L502_PCI_IFACE_FILE(hnd)+1, &rd_set, NULL, NULL, &tmval); + if ((sel > 0) && (FD_ISSET(L502_PCI_IFACE_FILE(hnd), &rd_set))) { + ssize_t rd = read(L502_PCI_IFACE_FILE(hnd), &buff[rcv_size], (size-rcv_size)*4); + if (rd>0) { + rcv_size += rd/4; + } else if (rd == 0) { + out = 1; + } else { + err = X502_ERR_RECV; + } + } else if (sel==0) { + out = 1; + } else { + err = X502_ERR_RECV; + } + } + return err != X502_ERR_OK ? err : (int32_t)rcv_size; +} + + +int32_t l502_port_stream_write(t_x502_hnd hnd, const uint32_t *buff, uint32_t size, + uint32_t timeout) { + struct timeval tmval = {timeout/1000, (timeout%1000)*1000}; + uint32_t snd_size = 0; + int32_t err = X502_ERR_OK; + int out = 0; + + while (!out && (err == X502_ERR_OK) && (snd_size < size)) { + fd_set wr_set; + int sel = 0; + + FD_ZERO(&wr_set); + FD_SET(L502_PCI_IFACE_FILE(hnd), &wr_set); + sel = select(L502_PCI_IFACE_FILE(hnd)+1, NULL, &wr_set, NULL, &tmval); + if ((sel > 0) && (FD_ISSET(L502_PCI_IFACE_FILE(hnd), &wr_set))) { + ssize_t wr = write(L502_PCI_IFACE_FILE(hnd), &buff[snd_size], (size-snd_size)*4); + if (wr > 0) { + snd_size += wr/4; + } else if (wr==0) { + out = 1; + } else { + err = X502_ERR_SEND; + } + } else if (sel==0) { + out = 1; + } else { + err = X502_ERR_SEND; + } + } + return err != X502_ERR_OK ? err : (int32_t)snd_size; +} + + +static int32_t f_get_file_par(const char *path, char *filebuf, + const char *file, char *res, uint32_t req_size, + uint32_t *read_size) { + int f; + int32_t err = X502_ERR_OK; + strcpy(filebuf, path); + strcat(filebuf, file); + + f = open (filebuf, O_RDONLY); + if (f == -1) { + err = X502_ERR_GET_INFO; + } else { + ssize_t rd = read(f, res, req_size); + if (rd < 0) { + err = X502_ERR_GET_INFO; + } else { + if (read_size!=NULL) + *read_size = rd; + } + close(f); + } + return err; +} + + +static void f_del_eol(char *str) { + for ( ; *str; str++) { + if ((*str=='\n') || (*str=='\r')) + *str = '\0'; + } +} + +static int32_t f_fill_devlist(const char *devname, t_x502_devrec *info) { + int32_t err = 0; + + int path_len = strlen(devname) + strlen(LPCIE_CLASS_DIR) + 2; + char *filename = malloc(path_len + 21); + char *path = malloc(path_len); + + if ((filename != NULL) && (path != NULL)) { + sprintf(path, LPCIE_CLASS_DIR "/%s", devname); + + err = f_get_file_par(path, filename, "/name", info->devname, + sizeof(info->devname), NULL); + if (err == X502_ERR_OK) + f_del_eol(info->devname); + + if (err == X502_ERR_OK) { + /* получаем серийный номер устройства */ + err = f_get_file_par(path, filename, "/sn", info->serial, + sizeof(info->serial), NULL); + if (err == X502_ERR_OK) + f_del_eol(info->serial); + } + + /* получаем информацию, открыто ли устройство */ + if (err == X502_ERR_OK) { + char val = '0'; + err = f_get_file_par(path, filename, "/opened", &val, 1, NULL); + if ((err == X502_ERR_OK) && (val!='0')) + info->flags |= X502_DEVFLAGS_DEVREC_OPENED; + } + + /* получаем информацию, присутствует ли BlackFin */ + if (err == X502_ERR_OK) { + char val = '0'; + if ((f_get_file_par(path, filename, "/bf", &val, 1, NULL) == X502_ERR_OK) + && (val != '0')) { + info->flags |= X502_DEVFLAGS_BF_PRESENT; + } + + if ((f_get_file_par(path, filename, "/dac", &val, 1, NULL) == X502_ERR_OK) + && (val != '0')) { + info->flags |= X502_DEVFLAGS_DAC_PRESENT; + } + + if ((f_get_file_par(path, filename, "/gal", &val, 1, NULL) == X502_ERR_OK) + && (val != '0')) { + info->flags |= X502_DEVFLAGS_GAL_PRESENT; + } + } + + if (err == X502_ERR_OK) { + char *devname_cpy = malloc(strlen(devname)+1); + if (devname_cpy!=NULL) { + strcpy(devname_cpy, devname); + err = l502_devlist_gen(info, devname_cpy); + if (err != X502_ERR_OK) { + free(devname_cpy); + } + } else { + err = X502_ERR_MEMORY_ALLOC; + } + } + } else { + err = X502_ERR_MEMORY_ALLOC; + } + + free(filename); + free(path); + return err; +} + + +int32_t l502_port_free_iface_data(void *intptr) { + free(intptr); + return X502_ERR_OK; +} + +int32_t l502_port_open(t_x502_hnd hnd, const t_x502_devrec *devinfo) { + const char *devname = (const char *)devinfo->internal->iface_data; + int32_t err = X502_ERR_OK; + int path_len = strlen(devname)+6; + char *path = malloc(path_len); + + if (path != NULL) { + int file; + snprintf(path, path_len, "/dev/%s", devname); + file = open(path, O_RDWR); + if (file != -1) { + hnd->iface_data = malloc(sizeof(t_pci_iface_data)); + if (hnd->iface_data == NULL) { + err = X502_ERR_MEMORY_ALLOC; + close(file); + } else { + L502_PCI_IFACE_FILE(hnd) = file; + } + } else { + /** @todo Разобрать коды ошибок */ + err = X502_ERR_DEVICE_OPEN; + } + free(path); + } else { + err = X502_ERR_MEMORY_ALLOC; + } + return err; +} + +int32_t l502_port_close(t_x502_hnd hnd) { + if (hnd->iface_data !=NULL) { + close(L502_PCI_IFACE_FILE(hnd)); + free(hnd->iface_data); + hnd->iface_data = NULL; + } + return X502_ERR_OK; +} + + + +X502_EXPORT(int32_t) L502_GetDevRecordsList(t_x502_devrec *list, uint32_t size, + uint32_t flags, uint32_t *devcnt) { + uint32_t curcnt = 0; + int32_t err = X502_ERR_OK; + /* все устройства, поддерживаемые драйвером LPCIE, создают папку + в директории класса устройств lpcie*/ + DIR *class_dir = opendir(LPCIE_CLASS_DIR); + if (class_dir!=NULL) { + struct dirent *dev_ent; + /* читаем все записи в директории класса */ + while ((dev_ent=readdir(class_dir))!=NULL) { + /* проверяем, что имя начинается с lpcie */ + if (!memcmp(dev_ent->d_name, "lpcie", sizeof("lpcie")-1)) { + t_x502_devrec info; + int info_used = 0; + X502_DevRecordInit(&info); + + /* получаем информацию о устройстве из служебных файлов, + * предоставляемых драйвером */ + if (f_fill_devlist(dev_ent->d_name, &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); + } + } + closedir(class_dir); + } + + if (devcnt != NULL) + *devcnt = curcnt; + + return err != X502_ERR_OK ? err : curcnt > size ? (int32_t)size : (int32_t)curcnt ; +} diff --git a/l502/lpcie_ioctls.h b/l502/lpcie_ioctls.h new file mode 100644 index 0000000..5faeaad --- /dev/null +++ b/l502/lpcie_ioctls.h @@ -0,0 +1,144 @@ +/***************************************************************************//** + * @file lpcie_ioctls.h + * Файл содержит определения управляющих запросов драйвера lpcie и типов данных, + * используемых для передачи параметров управляющих запросов + * @author Borisov Alexey + * @date 23.05.2012 + * ****************************************************************************/ +#ifndef LPCIE_IOCTLS_H +#define LPCIE_IOCTLS_H + +/** Проверка по версии дарайвера, поддерживается ли запрос LPCIE_IOCTL_CYCLE_CHECK_SETUP */ +#define LPCIE_IOCTL_SUPPORT_CYCLE_CHECK_SETUP(ver) (ver >= 0x01000900) + +/** Варианты событий, по которым должно произойти переключение + циклического сигнала */ +typedef enum { + LPCIE_CYCLE_SW_EVT_IMMIDIATLY = 1, /**< сразу по получению команды */ + LPCIE_CYCLE_SW_EVT_END_OF_CYCLE = 2 /**< по завершению текущего цикла */ +} t_lpcie_cycle_sw_evt; + + +/** параметры для записи значения регистра */ +typedef struct { + uint32_t addr; /** Адрес регистра */ + uint32_t val; /** Значение регистра */ +} t_lpcie_mem_rw; + +/** настройки канала DMA, передаваемые вместе с LPCIE_IOCTL_STREAM_SET_PARAMS */ +typedef struct { + uint32_t ch; /** канал DMA (ввод/вывод) */ + uint32_t res[2]; /** резерв */ + uint32_t buf_size; /** размер каждой страницы памяти в PC */ + uint32_t irq_step; /** через сколько переданных отсчетов будет + генерироваться прерывание */ + uint32_t res2[3]; +} t_lpcie_stream_ch_params; + +/** параметры для установки циклического сигнала */ +typedef struct { + uint32_t ch; /** канал DMA (доступно только на вывод) */ + uint32_t size; /** количество отсчетов в циклическом сигнале */ + uint32_t irq_step; /** шаг генерации прерываний */ + uint32_t res; /** резерв */ +} t_lpcie_cycle_set_par; + +/** параметры для остановки/смены циклического сигнала */ +typedef struct { + uint32_t ch; /** канал DMA (доступно только на вывод) */ + uint32_t evt; /** событие для смены сигнала из #t_lpcie_cycle_sw_evt */ + uint32_t res[2]; /** резерв */ +} t_lpcie_cycle_evt_par; + +/** параметры для запроса LPCIE_IOCTL_CYCLE_CHECK_SETUP */ +typedef struct { + uint32_t ch; /** канал DMA (доступно только на вывод) */ + uint32_t done; /** признак, завершена ли установка циклического сигнала */ +} t_lpcie_cycle_check_setup_par; + +/** параметры запроса для получения количества готовых для ввода или вывода + отсчетов */ +typedef struct { + uint32_t ch; /** канал DMA (ввод/вывод) */ + uint32_t rdy_size; /** Количество отсчетов доступных на ввод или вывод */ +} t_lpcie_get_rdy_par; + + +#ifdef _WIN32 +#define LPCIE_IOCTL_GET_DRV_VERSION \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED , FILE_ANY_ACCESS) + + +#define LPCIE_IOCTL_MEMFPGA_RD \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x810, METHOD_BUFFERED , FILE_ANY_ACCESS) + +#define LPCIE_IOCTL_MEMFPGA_WR \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x811, METHOD_BUFFERED , FILE_ANY_ACCESS) + +#define LPCIE_IOCTL_POWER_DONW \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x820, METHOD_BUFFERED , FILE_ANY_ACCESS) +#define LPCIE_IOCTL_RELOAD_DEVINFO \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x821, METHOD_BUFFERED , FILE_ANY_ACCESS) + + + +#define LPCIE_IOCTL_STREAM_SET_PARAMS \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x840, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_STREAM_GET_PARAMS \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x841, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_STREAM_GET_RDY_SIZE \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x842, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define LPCIE_IOCTL_STREAM_START \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x844, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_STREAM_STOP \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x845, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_STREAM_FREE \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x846, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_STREAM_START_SINGLE \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x847, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_CYCLE_LOAD \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x850, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_CYCLE_SWITCH \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x851, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_CYCLE_STOP \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x852, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_CYCLE_CHECK_SETUP \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x853, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#else + +#define LPCIE_IO_MAGIC 'L' + +#define LPCIE_IOCTL_GET_DRV_VERSION _IOR(LPCIE_IO_MAGIC, 0x800, uint32_t) + +#define LPCIE_IOCTL_MEMFPGA_RD _IOWR(LPCIE_IO_MAGIC, 0x810, t_lpcie_mem_rw) +#define LPCIE_IOCTL_MEMFPGA_WR _IOW(LPCIE_IO_MAGIC, 0x811, t_lpcie_mem_rw) + +#define LPCIE_IOCTL_POWER_DONW _IO(LPCIE_IO_MAGIC, 0x820) +#define LPCIE_IOCTL_RELOAD_DEVINFO _IO(LPCIE_IO_MAGIC, 0x821) + + +#define LPCIE_IOCTL_STREAM_SET_PARAMS _IOW(LPCIE_IO_MAGIC, 0x840, t_lpcie_stream_ch_params) +#define LPCIE_IOCTL_STREAM_GET_PARAMS _IOWR(LPCIE_IO_MAGIC, 0x841, t_lpcie_stream_ch_params) +#define LPCIE_IOCTL_STREAM_GET_RDY_SIZE _IOWR(LPCIE_IO_MAGIC, 0x842, t_lpcie_get_rdy_par) + + +#define LPCIE_IOCTL_STREAM_START _IOW(LPCIE_IO_MAGIC, 0x844, uint32_t) +#define LPCIE_IOCTL_STREAM_STOP _IOW(LPCIE_IO_MAGIC, 0x845, uint32_t) +#define LPCIE_IOCTL_STREAM_FREE _IOW(LPCIE_IO_MAGIC, 0x846, uint32_t) +#define LPCIE_IOCTL_STREAM_START_SINGLE _IOW(LPCIE_IO_MAGIC, 0x847, uint32_t) + +#define LPCIE_IOCTL_CYCLE_LOAD _IOW(LPCIE_IO_MAGIC, 0x850, t_lpcie_cycle_set_par) +#define LPCIE_IOCTL_CYCLE_SWITCH _IOW(LPCIE_IO_MAGIC, 0x851, t_lpcie_cycle_evt_par) +#define LPCIE_IOCTL_CYCLE_STOP _IOW(LPCIE_IO_MAGIC, 0x852, t_lpcie_cycle_evt_par) +#define LPCIE_IOCTL_CYCLE_CHECK_SETUP _IOWR(LPCIE_IO_MAGIC, 0x853, t_lpcie_cycle_check_setup_par) + +#endif + + + + + + +#endif // LPCIE_IOCTLS_H diff --git a/l502/pas/l502api.pas b/l502/pas/l502api.pas new file mode 100644 index 0000000..a94168b --- /dev/null +++ b/l502/pas/l502api.pas @@ -0,0 +1,805 @@ +unit l502api; +interface +uses Windows, SysUtils, x502api; + + + + // + function L502_GetSerialList(out serials: array of string; flags: LongWord; out devcnt: LongWord) : LongInt; overload; + function L502_GetSerialList(out serials: array of string; flags: LongWord) : LongInt; overload; + // + function L502_Open(hnd: t_x502_hnd; serial: string): LongInt; stdcall; + + function L502_GetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord; out devcnt: LongWord) : LongInt; overload; + function L502_GetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord) : LongInt; overload; + + + +{ ----------- , ----------} +const + // + L502_LTABLE_MAX_CH_CNT = 256; + // + L502_ADC_RANGE_CNT = 6; + // + L502_LCH_AVG_SIZE_MAX = 128; + // + L502_ADC_FREQ_DIV_MAX = (1024*1024); + // + L502_DIN_FREQ_DIV_MAX = (1024*1024); + // + L502_ADC_INTERFRAME_DELAY_MAX = $1FFFFF; + // BlackFin + L502_BF_CMD_DEFAULT_TOUT = 500; + + // , + L502_ADC_SCALE_CODE_MAX = 6000000; + // , + L502_DAC_SCALE_CODE_MAX = 30000; + + // + L502_DEVNAME_SIZE = X502_DEVNAME_SIZE; + // + L502_SERIAL_SIZE = X502_SERIAL_SIZE; + + // Flash- + L502_FLASH_USER_SIZE = $100000; + // BlackFin + L502_BF_REQ_TOUT = 500; + // + L502_DAC_RANGE = 5; + // + L502_DAC_CH_CNT = 2; + + // , , + L502_STREAM_IN_MSG_OVERFLOW = $01010000; + + // + L502_EXT_REF_FREQ_MAX = 2000000; + + + { ----------------- ---------------------------} + // + L502_ERR_OK = 0; + // + L502_ERR_INVALID_HANDLE = -1; + // + L502_ERR_MEMORY_ALLOC = -2; + // + L502_ERR_ALREADY_OPENED = -3; + // + L502_ERR_DEVICE_NOT_FOUND = -4; + // ( - , ) + L502_ERR_DEVICE_ACCESS_DENIED = -5; + // + L502_ERR_DEVICE_OPEN = -6; + // + L502_ERR_INVALID_POINTER = -7; + // + L502_ERR_STREAM_IS_RUNNING = -8; + // + L502_ERR_RECV = -9; + // + L502_ERR_SEND = -10; + // + L502_ERR_STREAM_OVERFLOW = -11; + // + L502_ERR_UNSUP_STREAM_MSG = -12; + // + L502_ERR_MUTEX_CREATE = -13; + // + L502_ERR_MUTEX_INVALID_HANDLE = -14; + // + L502_ERR_MUTEX_LOCK_TOUT = -15; + // + L502_ERR_MUTEX_RELEASE = -16; + // + L502_ERR_INSUFFICIENT_SYSTEM_RESOURCES= -17; + // + L502_ERR_NOT_IMPLEMENTED = -18; + // + L502_ERR_INSUFFICIENT_ARRAY_SIZE = -19; + // FPGA + L502_ERR_FPGA_REG_READ = -20; + // FPGA + L502_ERR_FPGA_REG_WRITE = -21; + // + L502_ERR_STREAM_IS_NOT_RUNNING = -22; + // + L502_ERR_INVALID_LTABLE_SIZE = -102; + // + L502_ERR_INVALID_LCH_NUMBER = -103; + // + L502_ERR_INVALID_LCH_RANGE = -104; + // + L502_ERR_INVALID_LCH_MODE = -105; + // + L502_ERR_INVALID_LCH_PHY_NUMBER = -106; + // + L502_ERR_INVALID_LCH_AVG_SIZE = -107; + // + L502_ERR_INVALID_ADC_FREQ_DIV = -108; + // + L502_ERR_INVALID_DIN_FREQ_DIV = -108; + // L502 + L502_ERR_INVALID_MODE = -109; + // + L502_ERR_INVALID_DAC_CHANNEL = -110; + // + L502_ERR_INVALID_REF_FREQ = -111; + // + L502_ERR_INVALID_INTERFRAME_DELAY = -112; + // + L502_ERR_INVALID_SYNC_MODE = -113; + // DMA + L502_ERR_INVALID_DMA_CH = -114; + // + L502_ERR_REF_FREQ_NOT_LOCKED = -131; + // + L502_ERR_IOCTL_FAILD = -132; + // + L502_ERR_IOCTL_TIMEOUT = -133; + // + L502_ERR_GET_INFO = -134; + // + L502_ERR_DIG_IN_NOT_RDY = -135; + // + L502_ERR_RECV_INSUFFICIENT_WORDS = -136; + // , , + L502_ERR_DAC_NOT_PRESENT = -137; + // + L502_ERR_PROC_INVALID_CH_NUM = -140; + // + L502_ERR_PROC_INVALID_CH_RANGE = -141; + // Flash- + L502_ERR_FLASH_INVALID_ADDR = -142; + // Flash- + L502_ERR_FLASH_INVALID_SIZE = -143; + // Flash- + L502_ERR_FLASH_WRITE_TOUT = -144; + // Flash- + L502_ERR_FLASH_ERASE_TOUT = -145; + // Flash- 4 + L502_ERR_FLASH_SECTOR_BOUNDARY = -146; + // BlackFin + L502_ERR_LDR_FILE_OPEN = -180; + // BlackFin + L502_ERR_LDR_FILE_READ = -181; + // BlackFin + L502_ERR_LDR_FILE_FORMAT = -182; + // LDR-, BlackFin HDMA + L502_ERR_LDR_FILE_UNSUP_FEATURE = -183; + // BlackFin + L502_ERR_LDR_FILE_UNSUP_STARTUP_ADDR = -184; + // / BlackFin + L502_ERR_BF_REQ_TIMEOUT = -185; + // BlackFin + L502_ERR_BF_CMD_IN_PROGRESS = -186; + // BlackFin + L502_ERR_BF_CMD_TIMEOUT = -187; + // BlackFin + L502_ERR_BF_CMD_RETURN_INSUF_DATA = -188; + // BlackFin + L502_ERR_BF_LOAD_RDY_TOUT = -189; + // + // + L502_ERR_BF_NOT_PRESENT = -190; + // BlackFin HDMA + L502_ERR_BF_INVALID_ADDR = -191; + // , BlackFin + L502_ERR_BF_INVALID_CMD_DATA_SIZE = -192; + + + {-------- , ------} + // , , + // + L502_GETDEVS_FLAGS_ONLY_NOT_OPENED = X502_GETDEVS_FLAGS_ONLY_NOT_OPENED; + + + + {-------- . ---------------} + // ( ) + L502_DIGOUT_WORD_DIS_H = $00020000; + // + L502_DIGOUT_WORD_DIS_L = $00010000; + + + {-------- -------------------} + L502_REF_FREQ_2000KHZ = 2000000; // 2 + L502_REF_FREQ_1500KHZ = 1500000; // 1.5 + + {-------- ----------------------} + L502_ADC_RANGE_10 = 0; // +/-10V + L502_ADC_RANGE_5 = 1; // +/-5V + L502_ADC_RANGE_2 = 2; // +/-2V + L502_ADC_RANGE_1 = 3; // +/-1V + L502_ADC_RANGE_05 = 4; // +/-0.5V + L502_ADC_RANGE_02 = 5; // +/-0.2V + + + {-------- ------------------} + L502_LCH_MODE_COMM = 0; // + L502_LCH_MODE_DIFF = 1; // + L502_LCH_MODE_ZERO = 2; // + + + {-------- ------------------------------------} + L502_SYNC_INTERNAL = 0; // + L502_SYNC_EXTERNAL_MASTER = 1; // + L502_SYNC_DI_SYN1_RISE = 2; // DI_SYN1 + L502_SYNC_DI_SYN2_RISE = 3; // DI_SYN2 + L502_SYNC_DI_SYN1_FALL = 6; // DI_SYN1 + L502_SYNC_DI_SYN2_FALL = 7; // DI_SYN2 + + {-------- , -------------------} + L502_PROC_FLAGS_VOLT = 1; // , + // + + {-------- -----------------} + L502_STREAM_ADC = $01; // + L502_STREAM_DIN = $02; // + L502_STREAM_DAC1 = $10; // + L502_STREAM_DAC2 = $20; // + L502_STREAM_DOUT = $40; // + // , + L502_STREAM_ALL_IN = L502_STREAM_ADC or L502_STREAM_DIN; + // , + L502_STREAM_ALL_OUT = L502_STREAM_DAC1 or L502_STREAM_DAC2 or L502_STREAM_DOUT; + + + {--- , -----} + L502_STREAM_OUT_WORD_TYPE_DOUT = $0; // + L502_STREAM_OUT_WORD_TYPE_DAC1 = $40000000; // 1- + L502_STREAM_OUT_WORD_TYPE_DAC2 = $80000000; // 2- + + {------------------ L502 ------------------------------} + L502_MODE_FPGA = 0; // + // BlackFin + L502_MODE_DSP = 1; // + // , + // + L502_MODE_DEBUG = 2; // + + {------------------- -----------------------------------} + L502_DAC_CH1 = 0; // + L502_DAC_CH2 = 1; // + + {----------- , -----------------} + // , + // . , , + // + L502_DAC_FLAGS_VOLT = $0001; + // , + // . + L502_DAC_FLAGS_CALIBR = $0002; + + {------------------ DMA ------------------------------------} + L502_DMA_CH_IN = 0; // DMA + L502_DMA_CH_OUT = 1; // DMA + + {--- , ----} + L502_PULLUPS_DI_H = $01; // + L502_PULLUPS_DI_L = $02; // + L502_PULLUPS_DI_SYN1 = $04; // SYN1 + L502_PULLUPS_DI_SYN2 = $08; // SYN2 + + + + {--------------- , ---------------} + // + L502_DEVFLAGS_DAC_PRESENT = X502_DEVFLAGS_DAC_PRESENT; + // BlackFin + L502_DEVFLAGS_BF_PRESENT = X502_DEVFLAGS_BF_PRESENT; + // + L502_DEVFLAGS_GAL_PRESENT = X502_DEVFLAGS_GAL_PRESENT; + // , Flash- + L502_DEVFLAGS_FLASH_DATA_VALID = $00010000; + // , Flash- + // + L502_DEVFLAGS_FLASH_ADC_CALIBR_VALID = $00020000; + // , Flash- + // + L502_DEVFLAGS_FLASH_DAC_CALIBR_VALID = $00040000; + + {---------------- ----------------------} + // , + // . + L502_OUT_CYCLE_FLAGS_FORCE = $01; + + +type + // - + t_l502_hnd = t_x502_hnd; + + + { } + t_l502_cbr_coef = record + offs: Double; // + k : Double; // + end; + + + { . } + t_l502_cbr = record + // + adc: array[0..L502_ADC_RANGE_CNT-1] of t_l502_cbr_coef; + res1: array[0..63] of LongWord; + // + dac: array[0..L502_DAC_CH_CNT-1] of t_l502_cbr_coef; + res2: array[0..19] of LongWord; + end; + + { L502.} + t_l502_info = record + name: array[0..L502_DEVNAME_SIZE-1] of AnsiChar; // ("L502") + serial: array[0..L502_SERIAL_SIZE-1] of AnsiChar; // + devflags: LongWord; // + fpga_ver : Word; // ( - , - ) + plda_ver : Byte; // , + res : array[0..120] of Byte; // + cbr : t_l502_cbr; // ( Flash-) + end; + + function L502_Create(): t_l502_hnd; stdcall; + function L502_Free(hnd: t_l502_hnd): LongInt; stdcall; + + // + function L502_Close(hnd: t_l502_hnd): LongInt; stdcall; + // + function L502_GetDevInfo(hnd: t_l502_hnd; out info: t_l502_info) : LongInt; stdcall; + + // + function L502_Configure(hnd: t_l502_hnd; flags: LongWord): LongInt; stdcall; + // + function L502_SetLChannel(hnd: t_l502_hnd; lch, phy_ch, mode, range, avg: LongWord): LongInt; stdcall; + // + function L502_SetLChannelCount(hnd: t_l502_hnd; lch_cnt : LongWord): LongInt; stdcall; + // + function L502_GetLChannelCount(hnd: t_l502_hnd; out lch_cnt: LongWord): LongInt; stdcall; + // + function L502_SetAdcFreqDivider(hnd: t_l502_hnd; adc_freq_div : LongWord): LongInt; stdcall; + // + function L502_SetAdcInterframeDelay(hnd: t_l502_hnd; delay : LongWord): LongInt; stdcall; + // + function L502_SetDinFreqDivider(hnd: t_l502_hnd; din_freq_div: LongWord): LongInt; stdcall; + // + function L502_SetAdcFreq(hnd: t_l502_hnd; var f_acq, f_frame: Double): LongInt; stdcall; + // + function L502_SetDinFreq(hnd: t_l502_hnd; var f_din: Double): LongInt; stdcall; + // + function L502_GetAdcFreq(hnd: t_l502_hnd; out f_acq, f_frame: Double): LongInt; stdcall; + // . + function L502_SetRefFreq(hnd: t_l502_hnd; freq: LongWord): LongInt; stdcall; + // . + function L502_SetSyncMode(hnd: t_l502_hnd; sync_mode: LongWord): LongInt; stdcall; + // . + function L502_SetSyncStartMode(hnd: t_l502_hnd; sync_start_mode: LongWord): LongInt; stdcall; + // + function L502_SetMode(hnd: t_l502_hnd; mode: LongWord): LongInt; stdcall; + // . + function L502_GetMode(hnd: t_l502_hnd; out mode: LongWord): LongInt; stdcall; + // . + function L502_SetAdcCoef(hnd: t_l502_hnd; range: LongWord; k, offs: Double): LongInt; stdcall; + // . + function L502_GetAdcCoef(hnd: t_l502_hnd; range: LongWord; out k, offs: Double): LongInt; stdcall; + + + {----------------------- - ------------------} + // . + function L502_AsyncOutDac(hnd: t_l502_hnd; ch: LongWord; data: Double; flags: LongWord): LongInt; stdcall; + // . + function L502_AsyncOutDig(hnd: t_l502_hnd; val, msk: LongWord): LongInt; stdcall; + // . + function L502_AsyncInDig(hnd: t_l502_hnd; out din: LongWord): LongInt; stdcall; + // . + function L502_AsyncGetAdcFrame(hnd: t_l502_hnd; flags: LongWord; tout: LongWord; out data: array of Double): LongInt; stdcall; + + + {-------------- - ----} + // /. + function L502_StreamsEnable(hnd: t_l502_hnd; streams: LongWord): LongInt; stdcall; + // /. + function L502_StreamsDisable(hnd: t_l502_hnd; streams: LongWord): LongInt; stdcall; + // /. + function L502_StreamsStart(hnd: t_l502_hnd): LongInt; stdcall; + // /. + function L502_StreamsStop(hnd: t_l502_hnd): LongInt; stdcall; + // , + function L502_IsRunning(hnd: t_l502_hnd): LongInt; stdcall; + + + // . + function L502_Recv(hnd: t_l502_hnd; out buf : array of LongWord; size: LongWord; tout : LongWord): LongInt; stdcall; + // . + function L502_Send(hnd: t_l502_hnd; const buf : array of LongWord; size: LongWord; tout: LongWord): LongInt; stdcall; + // . + function L502_ProcessAdcData(hnd: t_l502_hnd; const src: array of LongWord; + out dest: array of Double; var size : LongWord; + flags : LongWord): LongInt; stdcall; + // . + function L502_ProcessData(hnd: t_l502_hnd; const src: array of LongWord; size: LongWord; + flags : LongWord; out adc_data: array of Double; var adc_data_size : LongWord; + out din_data: array of LongWord; var din_data_size: LongWord):LongInt; stdcall; + // . + function L502_ProcessDataWithUserExt(hnd: t_l502_hnd; const src: array of LongWord; size: LongWord; + flags : LongWord; out adc_data: array of Double; var adc_data_size : LongWord; + out din_data: array of LongWord; var din_data_size: LongWord; + out usr_data: array of LongWord; var usr_data_size: LongWord):LongInt; stdcall; + // . + function L502_PrepareData(hnd: t_l502_hnd; const dac1, dac2: array of Double; + const digout: array of LongWord; size, flags : LongWord; + out out_buf: array of LongWord):LongInt; stdcall; + + // . + function L502_GetRecvReadyCount(hnd: t_l502_hnd; out rdy_cnt: LongWord):LongInt; stdcall; + // . + function L502_GetSendReadyCount(hnd: t_l502_hnd; out rdy_cnt: LongWord):LongInt; stdcall; + // . + function L502_GetNextExpectedLchNum(hnd: t_l502_hnd; out lch: LongWord):LongInt; stdcall; + // + function L502_PreloadStart(hnd: t_l502_hnd): LongInt; stdcall; + + // + function L502_OutCycleLoadStart(hnd: t_l502_hnd; size: LongWord):LongInt; stdcall; + // + function L502_OutCycleSetup(hnd: t_l502_hnd; flags: LongWord):LongInt; stdcall; + // + function L502_OutCycleStop(hnd: t_l502_hnd; flags: LongWord):LongInt; stdcall; + + // . + function L502_SetDmaBufSize(hnd: t_l502_hnd; dma_ch, size: LongWord): LongInt; stdcall; + // DMA. + function L502_SetDmaIrqStep(hnd: t_l502_hnd; dma_ch, step: LongWord): LongInt; stdcall; + + {------------ -------------------} + function L502_BfCheckFirmwareIsLoaded(hnd: t_l502_hnd; out version: LongWord): LongInt; stdcall; + /// BlackFin. + function L502_BfLoadFirmware(hnd: t_l502_hnd; filename: string): LongInt; stdcall; + /// . + function L502_BfMemRead(hnd: t_l502_hnd; addr : LongWord; out regs: array of LongWord; + size: LongWord): LongInt; stdcall; + /// . + function L502_BfMemWrite(hnd: t_l502_hnd; addr : LongWord; + const regs: array of LongWord; size: LongWord): LongInt; stdcall; + /// . + function L502_BfExecCmd(hnd: t_l502_hnd; cmd_code : Word; par : LongWord; + const snd_data : array of LongWord; snd_size : LongWord; + out rcv_data : array of LongWord; rcv_size : LongWord; + tout: LongWord; out recvd_size: LongWord): LongInt; stdcall; + + {------------------- Flash- -------------} + /// Flash- . + function L502_FlashRead(hnd: t_l502_hnd; addr: LongWord; + out data: array of Byte; size: LongWord): LongInt; stdcall; + /// Flash- . + function L502_FlashWrite(hnd: t_l502_hnd; addr: LongWord; + const data: array of Byte; size: LongWord): LongInt; stdcall; + /// Flash-. + function L502_FlashErase(hnd: t_l502_hnd; addr: LongWord; size: LongWord): LongInt; stdcall; + /// Flash-. + function L502_FlashWriteEnable(hnd: LongWord): LongInt; stdcall; + /// Flash-. + function L502_FlashWriteDisable(hnd: t_l502_hnd): LongInt; stdcall; + + {----------------- ------------------} + // . + function L502_GetDllVersion() : LongWord; stdcall; + // + function L502_GetDriverVersion(hnd: t_l502_hnd; out ver: LongWord): LongInt; stdcall; + // + function L502_GetErrorString(err: LongInt) : string; stdcall; + // . + function L502_LedBlink(hnd: t_l502_hnd): LongInt; stdcall; + // . + function L502_SetDigInPullup(hnd: t_l502_hnd; pullups : LongWord): LongInt; stdcall; + + + + + + + + + +implementation + function L502_Create() : t_l502_hnd; stdcall; external 'l502api.dll'; + function L502_Free(hnd: t_l502_hnd) : LongInt; stdcall; external 'l502api.dll'; + function _get_serials( ser_arr: p_x502_serial_array; size:LongWord; + flags:LongWord; out devcnt: LongWord) : LongInt; + stdcall; external 'l502api.dll' name 'L502_GetSerialList'; + function _get_dev_records_list(out list; size:LongWord; + flags : LongWord; out devcnt: LongWord) : LongInt; + stdcall; external 'l502api.dll' name 'L502_GetDevRecordsList'; + + function _open(hnd: t_l502_hnd; serial: PAnsiChar) : LongInt; stdcall; external 'l502api.dll' name 'L502_Open'; + function L502_Close(hnd : t_l502_hnd) : LongInt; stdcall; external 'l502api.dll'; + function L502_GetDevInfo(hnd : t_l502_hnd; out info : t_l502_info) : LongInt; stdcall; external 'l502api.dll'; + + function L502_Configure(hnd: t_l502_hnd; flags: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetLChannel(hnd: t_l502_hnd; lch, phy_ch, mode, range, avg: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetLChannelCount(hnd: t_l502_hnd; lch_cnt : LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_GetLChannelCount(hnd: t_l502_hnd; out lch_cnt: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetAdcFreqDivider(hnd: t_l502_hnd; adc_freq_div : LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetAdcInterframeDelay(hnd: t_l502_hnd; delay : LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetDinFreqDivider(hnd: t_l502_hnd; din_freq_div: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetAdcFreq(hnd: t_l502_hnd; var f_acq, f_frame: Double): LongInt; stdcall; external 'l502api.dll'; + function L502_SetDinFreq(hnd: t_l502_hnd; var f_din: Double): LongInt; stdcall; external 'l502api.dll'; + function L502_GetAdcFreq(hnd: t_l502_hnd; out f_acq, f_frame: Double): LongInt; stdcall; external 'l502api.dll'; + function L502_SetRefFreq(hnd: t_l502_hnd; freq: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetSyncMode(hnd: t_l502_hnd; sync_mode: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetSyncStartMode(hnd: t_l502_hnd; sync_start_mode: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetMode(hnd: t_l502_hnd; mode: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_GetMode(hnd: t_l502_hnd; out mode: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetAdcCoef(hnd: t_l502_hnd; range: LongWord; k, offs: Double): LongInt; stdcall; external 'l502api.dll'; + function L502_GetAdcCoef(hnd: t_l502_hnd; range: LongWord; out k, offs: Double): LongInt; stdcall; external 'l502api.dll'; + + function L502_AsyncOutDac(hnd: t_l502_hnd; ch: LongWord; data: Double; flags: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_AsyncOutDig(hnd: t_l502_hnd; val, msk: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_AsyncInDig(hnd: t_l502_hnd; out din: LongWord): LongInt; stdcall; external 'l502api.dll'; + function _get_adc_frame(hnd: t_l502_hnd; flags: LongWord; tout: LongWord; out data): LongInt; stdcall; external 'l502api.dll' name 'L502_AsyncGetAdcFrame'; + + function L502_StreamsEnable(hnd: t_l502_hnd; streams: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_StreamsDisable(hnd: t_l502_hnd; streams: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_StreamsStart(hnd: t_l502_hnd): LongInt; stdcall; external 'l502api.dll'; + function L502_IsRunning(hnd: t_l502_hnd): LongInt; stdcall; external 'l502api.dll'; + function L502_StreamsStop(hnd: t_l502_hnd): LongInt; stdcall; external 'l502api.dll'; + function _recv(hnd: t_l502_hnd; out buf; size: LongWord; tout : LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_Recv'; + function _send(hnd: t_l502_hnd; const buf; size: LongWord; tout : LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_Send'; + function _process_adc_data(hnd: t_l502_hnd; const src; out dest; var size : LongWord; + flags : LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_ProcessAdcData'; + function _process_data(hnd: t_l502_hnd; const src; size: LongWord; + flags : LongWord; out adc_data; var adc_data_size : LongWord; + out din_data; var din_data_size: LongWord):LongInt; stdcall; external 'l502api.dll' name 'L502_ProcessData'; + + function _process_data_usr(hnd: t_l502_hnd; const src; size: LongWord; + flags : LongWord; out adc_data; var adc_data_size : LongWord; + out din_data; var din_data_size: LongWord; + out usr_data; var usr_data_size: LongWord):LongInt; stdcall; external 'l502api.dll' name 'L502_ProcessDataWithUserExt'; + function _prepare_data(hnd: t_l502_hnd; const dac1, dac2; const digout; size, flags : LongWord; + out out_buf):LongInt; stdcall; external 'l502api.dll' name 'L502_PrepareData'; + function L502_GetRecvReadyCount(hnd: t_l502_hnd; out rdy_cnt: LongWord):LongInt; stdcall; external 'l502api.dll'; + function L502_GetSendReadyCount(hnd: t_l502_hnd; out rdy_cnt: LongWord):LongInt; stdcall; external 'l502api.dll'; + function L502_GetNextExpectedLchNum(hnd: t_l502_hnd; out lch: LongWord):LongInt; stdcall; external 'l502api.dll'; + function L502_PreloadStart(hnd: t_l502_hnd): LongInt; stdcall; external 'l502api.dll'; + function L502_OutCycleLoadStart(hnd: t_l502_hnd; size: LongWord):LongInt; stdcall; external 'l502api.dll'; + function L502_OutCycleSetup(hnd: t_l502_hnd; flags: LongWord):LongInt; stdcall; external 'l502api.dll'; + function L502_OutCycleStop(hnd: t_l502_hnd; flags: LongWord):LongInt; stdcall; external 'l502api.dll'; + function L502_SetDmaBufSize(hnd: t_l502_hnd; dma_ch, size: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetDmaIrqStep(hnd: t_l502_hnd; dma_ch, step: LongWord): LongInt; stdcall; external 'l502api.dll'; + + function L502_BfCheckFirmwareIsLoaded(hnd: t_l502_hnd; out version: LongWord): LongInt; stdcall; external 'l502api.dll'; + function _bf_load_firm(hnd: t_l502_hnd; filename: PAnsiChar): LongInt; stdcall; external 'l502api.dll' name 'L502_BfLoadFirmware'; + function _bf_mem_read(hnd: t_l502_hnd; addr : LongWord; out regs; size: LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_BfMemRead'; + function _bf_mem_write(hnd: t_l502_hnd; addr : LongWord; const regs; size: LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_BfMemWrite'; + function _bf_exec_cmd(hnd: t_l502_hnd; cmd_code : Word; par : LongWord; + const snd_data; snd_size : LongWord; out rcv_data; rcv_size : LongWord; + tout: LongWord; out recved_size: LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_BfExecCmd'; + + function _flash_read(hnd: t_l502_hnd; addr: LongWord; out data; size: LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_FlashRead'; + function _flash_write(hnd: t_l502_hnd; addr: LongWord; const data; size: LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_FlashWrite'; + function L502_FlashErase(hnd: t_l502_hnd; addr: LongWord; size: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_FlashWriteEnable(hnd: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_FlashWriteDisable(hnd: t_l502_hnd): LongInt; stdcall; external 'l502api.dll'; + + function L502_GetDllVersion() : LongWord; stdcall; external 'l502api.dll'; + function L502_GetDriverVersion(hnd: t_l502_hnd; out ver: LongWord): LongInt; stdcall; external 'l502api.dll'; + function _get_err_str(err : LongInt) : PAnsiChar; stdcall; external 'l502api.dll' name 'L502_GetErrorString'; + function L502_LedBlink(hnd: t_l502_hnd): LongInt; stdcall; external 'l502api.dll'; + function L502_SetDigInPullup(hnd: t_l502_hnd; pullups : LongWord): LongInt; stdcall; external 'l502api.dll'; + + + { + function L502_GetSerialList(out serials: t_l502_serial_list; flags: LongWord) : LongInt; overload; + var + ser_arr : p_l502_serial_array; + devcnt: LongWord; + res, i : LongInt; + begin + // + res := _get_serials(nil, 0, flags, devcnt); + if (res >= 0) and (devcnt>0) then + begin + // devcnt + ser_arr:=GetMemory(devcnt*L502_SERIAL_SIZE); + // + res:= _get_serials(ser_arr, devcnt, flags, PLongWord(nil)^); + if (res > 0) then + begin + // + SetLength(serials, res); + // + for i:=0 to res-1 do + serials[i] := string(ser_arr[i]); + end; + // , + FreeMemory(ser_arr); + end; + L502_GetSerialList:= res; + end; + } + + function L502_GetSerialList(out serials: array of string; flags: LongWord; out devcnt: LongWord) : LongInt; overload; + var + ser_arr : p_x502_serial_array; + res, i : LongInt; + begin + if (Length(serials) > 0) then + begin + ser_arr:=GetMemory(Length(serials)*X502_SERIAL_SIZE); + // + res := _get_serials(ser_arr, Length(serials), flags, devcnt); + if res >= 0 then + begin + // + for i:=0 to res-1 do + serials[i] := string(ser_arr[i]); + end; + // , + FreeMemory(ser_arr); + end + else + begin + res:= _get_serials(nil, 0, flags, devcnt); + end; + L502_GetSerialList:=res; + end; + + function L502_GetSerialList(out serials: array of string; flags: LongWord) : LongInt; overload; + begin + L502_GetSerialList:= L502_GetSerialList(serials, flags, PCardinal(nil)^); + end; + + function L502_GetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord; out devcnt: LongWord) : LongInt; overload; + begin + if (Length(list) > 0) then + begin + L502_GetDevRecordsList := _get_dev_records_list(list, Length(list), flags, devcnt); + end + else + begin + L502_GetDevRecordsList:= _get_dev_records_list(PCardinal(nil)^, 0, flags, devcnt); + end; + end; + function L502_GetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord) : LongInt; overload; + begin + L502_GetDevRecordsList:= L502_GetDevRecordsList(list, flags, PCardinal(nil)^); + end; + + function L502_Open(hnd: t_l502_hnd; serial: string) : LongInt; + begin + L502_Open:=_open(hnd, PAnsiChar(AnsiString(serial))); + end; + + function L502_GetErrorString(err: LongInt) : string; + begin + L502_GetErrorString:= string(_get_err_str(err)); + end; + + function L502_AsyncGetAdcFrame(hnd: t_l502_hnd; flags: LongWord; tout: LongWord; out data: array of Double): LongInt; stdcall; + var err: LongInt; + lch_cnt: LongWord; + begin + err:= L502_GetLChannelCount(hnd, lch_cnt); + if err=L502_ERR_OK then + begin + if LongWord(Length(data)) < lch_cnt then + err:=L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + err:=_get_adc_frame(hnd,flags,tout,data); + end; + L502_AsyncGetAdcFrame:=err; + end; + + function L502_Recv(hnd: t_l502_hnd; out buf : array of LongWord; size: LongWord; tout : LongWord): LongInt; stdcall; + begin + if LongWord(Length(buf)) < size then + L502_Recv:=L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_Recv:=_recv(hnd, buf, size, tout); + end; + + function L502_Send(hnd: t_l502_hnd; const buf : array of LongWord; size: LongWord; tout: LongWord): LongInt; stdcall; + begin + if LongWord(Length(buf)) < size then + L502_Send:=L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_Send:=_send(hnd,buf,size,tout); + end; + + + function L502_ProcessAdcData(hnd: t_l502_hnd; const src: array of LongWord; + out dest: array of Double; var size : LongWord; + flags : LongWord): LongInt; stdcall; + begin + if (LongWord(Length(src)) < size) or (LongWord(Length(dest)) < size) then + L502_ProcessAdcData:=L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_ProcessAdcData:=_process_adc_data(hnd, src, dest, size, flags); + end; + + function L502_ProcessData(hnd: t_l502_hnd; const src: array of LongWord; size: LongWord; + flags : LongWord; out adc_data: array of Double; var adc_data_size : LongWord; + out din_data: array of LongWord; var din_data_size: LongWord):LongInt; stdcall; + begin + if (LongWord(Length(adc_data)) < adc_data_size) or (LongWord(Length(din_data)) < din_data_size) + or (LongWord(Length(src)) < size) then + L502_ProcessData:=L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_ProcessData:=_process_data(hnd, src, size, flags, adc_data, adc_data_size, din_data, din_data_size); + end; + + function L502_ProcessDataWithUserExt(hnd: t_l502_hnd; const src: array of LongWord; size: LongWord; + flags : LongWord; out adc_data: array of Double; var adc_data_size : LongWord; + out din_data: array of LongWord; var din_data_size: LongWord; + out usr_data: array of LongWord; var usr_data_size: LongWord):LongInt; stdcall; + begin + if (LongWord(Length(adc_data)) < adc_data_size) or (LongWord(Length(din_data)) < din_data_size) + or (LongWord(Length(src)) < size) or (LongWord(Length(usr_data)) < usr_data_size) then + L502_ProcessDataWithUserExt:=L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_ProcessDataWithUserExt:=_process_data_usr(hnd, src,size,flags,adc_data, + adc_data_size, din_data, din_data_size, usr_data, usr_data_size); + end; + + function L502_PrepareData(hnd: t_l502_hnd; const dac1, dac2: array of Double; + const digout: array of LongWord; size, flags : LongWord; + out out_buf: array of LongWord):LongInt; stdcall; + begin + L502_PrepareData:=_prepare_data(hnd, dac1, dac2, digout, size, flags, out_buf); + end; + + function L502_BfLoadFirmware(hnd: t_l502_hnd; filename: string): LongInt; stdcall; + begin + L502_BfLoadFirmware:=_bf_load_firm(hnd, PAnsiChar(AnsiString(filename))); + end; + + function L502_BfMemRead(hnd: t_l502_hnd; addr : LongWord; out regs: array of LongWord; + size: LongWord): LongInt; stdcall; + begin + if (LongWord(Length(regs)) < size) then + L502_BfMemRead := L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_BfMemRead:=_bf_mem_read(hnd, addr, regs, size); + end; + + function L502_BfMemWrite(hnd: t_l502_hnd; addr : LongWord; + const regs: array of LongWord; size: LongWord): LongInt; stdcall; + begin + if (LongWord(Length(regs)) < size) then + L502_BfMemWrite := L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_BfMemWrite:=_bf_mem_write(hnd, addr, regs, size); + end; + + function L502_BfExecCmd(hnd: t_l502_hnd; cmd_code : Word; par : LongWord; + const snd_data : array of LongWord; snd_size : LongWord; + out rcv_data : array of LongWord; rcv_size : LongWord; + tout: LongWord; out recvd_size: LongWord): LongInt; stdcall; + begin + if (LongWord(Length(snd_data)) < snd_size) or + (LongWord(Length(rcv_data)) < rcv_size) then + L502_BfExecCmd := L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_BfExecCmd:=_bf_exec_cmd(hnd, cmd_code, par, snd_data, snd_size, + rcv_data, rcv_size, tout, recvd_size); + end; + + function L502_FlashRead(hnd: t_l502_hnd; addr: LongWord; + out data: array of Byte; size: LongWord): LongInt; stdcall; + begin + if (LongWord(Length(data)) < size) then + L502_FlashRead := L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_FlashRead:=_bf_mem_write(hnd, addr, data, size); + end; + /// Flash- . + function L502_FlashWrite(hnd: t_l502_hnd; addr: LongWord; + const data: array of Byte; size: LongWord): LongInt; stdcall; + begin + if (LongWord(Length(data)) < size) then + L502_FlashWrite := L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_FlashWrite:=_bf_mem_write(hnd, addr, data, size); + end; +end. + diff --git a/l502/win/l502_spec.c b/l502/win/l502_spec.c new file mode 100644 index 0000000..2341ffd --- /dev/null +++ b/l502/win/l502_spec.c @@ -0,0 +1,507 @@ +#include +#include +#include +#include +#include + +#include "../l502api_private.h" + +#include + + + +/* GUID интерфейса устройств lpcie для связи приложения с драйвером */ +DEFINE_GUID (GUID_LPCIE_INTERFACE, + 0x53869b9a, 0x7875, 0x4fd3, 0x9e, 0x04, 0xbe, 0xc8, 0x1a, 0x92, 0xf9, 0xa9); + +#define L502_IOCTL_TIMEOUT 500 + + +typedef struct { + int32_t size; + SP_DEVICE_INTERFACE_DETAIL_DATA *intf_detail; +} t_lpcie_devlst_intptr; + + +static int32_t f_ioctl(HANDLE hDevice, + uint32_t dwIoControlCode, // control code of operation to perform + void* lpInBuffer, // pointer to buffer to supply input data + uint32_t nInBufferSize, // size of input buffer in bytes + void* lpOutBuffer, // pointer to buffer to receive output data + uint32_t nOutBufferSize, // size of output buffer in bytes + uint32_t* rx_size, + uint32_t TimeOut) { // таймаут в мс + uint32_t RealBytesTransferred; + uint32_t BytesReturned; + OVERLAPPED Ov; + int32_t err = X502_ERR_OK; + uint32_t syserr = 0; + + + // инициализируем OVERLAPPED структуру + memset(&Ov, 0x0, sizeof(OVERLAPPED)); + // создаём событие для асинхронного запроса + Ov.hEvent = CreateEvent(NULL, FALSE , FALSE, NULL); + if(!Ov.hEvent) { + err = X502_ERR_MEMORY_ALLOC; + } else { + // посылаем требуемый запрос + if(!DeviceIoControl( hDevice, dwIoControlCode, + lpInBuffer, nInBufferSize, + lpOutBuffer, nOutBufferSize, + &BytesReturned, &Ov)) { + syserr = GetLastError(); + if(syserr != ERROR_IO_PENDING) { + CloseHandle(Ov.hEvent); + err = X502_ERR_IOCTL_FAILD; + GetOverlappedResult(hDevice, &Ov, &RealBytesTransferred, TRUE); + } + } + } + + if (err == X502_ERR_OK) { + // ждём окончания выполнения запроса + if (WaitForSingleObject(Ov.hEvent, TimeOut) == WAIT_TIMEOUT) { + CancelIo(hDevice); + CloseHandle(Ov.hEvent); + err = X502_ERR_IOCTL_TIMEOUT; + GetOverlappedResult(hDevice, &Ov, &RealBytesTransferred, TRUE); + } + } + // попробуем получить кол-во реально переданных байт данных + if (err == X502_ERR_OK) { + if(!GetOverlappedResult(hDevice, &Ov, &RealBytesTransferred, TRUE)) { + syserr = GetLastError(); + CancelIo(hDevice); + CloseHandle(Ov.hEvent); + err = X502_ERR_IOCTL_FAILD; + } else if(nOutBufferSize != RealBytesTransferred) { + CancelIo(hDevice); + CloseHandle(Ov.hEvent); + } else { + CloseHandle(Ov.hEvent); + } + } + + if ((err == X502_ERR_OK) && (rx_size != NULL)) { + *rx_size = RealBytesTransferred; + } + + if (err==X502_ERR_IOCTL_FAILD) { + if (syserr == ERROR_NO_SYSTEM_RESOURCES) + err = X502_ERR_INSUFFICIENT_SYSTEM_RESOURCES; + } + return err; +} + + +int32_t l502_port_fpga_reg_write(t_x502_hnd hnd, uint32_t reg, uint32_t val) { + t_lpcie_mem_rw mem_wr = {reg,val}; + return f_ioctl (L502_PCI_IFACE_FILE(hnd), + LPCIE_IOCTL_MEMFPGA_WR, &mem_wr, sizeof(mem_wr), + NULL, 0, NULL, L502_IOCTL_TIMEOUT) ? + X502_ERR_FPGA_REG_READ : 0; +} + + +int32_t l502_port_fpga_reg_read(t_x502_hnd hnd, uint32_t reg, uint32_t *val) { + uint32_t rd_val; + int32_t err = f_ioctl (L502_PCI_IFACE_FILE(hnd), + LPCIE_IOCTL_MEMFPGA_RD, ®, sizeof(reg), + &rd_val, sizeof(rd_val), NULL, L502_IOCTL_TIMEOUT); + if (err != X502_ERR_OK) { + err = X502_ERR_FPGA_REG_READ; + } else if (val != NULL) { + *val = rd_val; + } + return err; +} + +int32_t l502_port_stream_set_params(t_x502_hnd hnd, t_lpcie_stream_ch_params *par) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_SET_PARAMS, par, + sizeof(t_lpcie_stream_ch_params), NULL, 0, NULL, + L502_IOCTL_TIMEOUT); +} + +int32_t l502_port_stream_start(t_x502_hnd hnd, uint32_t ch, uint32_t single) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), single ? LPCIE_IOCTL_STREAM_START_SINGLE : + LPCIE_IOCTL_STREAM_START, + &ch, sizeof(ch), NULL, 0, NULL, + L502_IOCTL_TIMEOUT); +} + +int32_t l502_port_stream_stop(t_x502_hnd hnd, uint32_t ch, uint32_t flags) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_STOP, &ch, sizeof(ch), NULL, 0, NULL, + L502_IOCTL_TIMEOUT); +} + +int32_t l502_port_stream_free(t_x502_hnd hnd, uint32_t ch, uint32_t flags) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_FREE, &ch, sizeof(ch), NULL, 0, NULL, + L502_IOCTL_TIMEOUT); +} + + +int32_t l502_port_stream_rdy_size(t_x502_hnd hnd, uint32_t ch, uint32_t *rdy_size) { + uint32_t rd_val; + int32_t err = f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_GET_RDY_SIZE, &ch, sizeof(ch), + &rd_val, sizeof(rd_val), NULL, L502_IOCTL_TIMEOUT); + if ((err == X502_ERR_OK) && (rdy_size != NULL)) + *rdy_size = rd_val; + return err; +} + + + +int32_t l502_port_cycle_load_start(t_x502_hnd hnd, uint32_t ch, uint32_t size, uint32_t min_irq_step) { + t_lpcie_cycle_set_par par; + memset(&par, 0, sizeof(par)); + par.ch = ch; + par.size = size; + par.irq_step = min_irq_step; + return f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_LOAD, &par, sizeof(par), NULL, 0, + NULL, L502_IOCTL_TIMEOUT); +} + +int32_t l502_port_cycle_setup(t_x502_hnd hnd, uint32_t ch, uint32_t evt) { + t_lpcie_cycle_evt_par par; + memset(&par, 0, sizeof(par)); + par.ch = ch; + par.evt = evt; + return f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_SWITCH, &par, sizeof(par), NULL, 0, + NULL, L502_IOCTL_TIMEOUT); +} + +int32_t l502_port_cycle_stop(t_x502_hnd hnd, uint32_t ch, uint32_t evt) { + t_lpcie_cycle_evt_par par; + memset(&par, 0, sizeof(par)); + par.ch = ch; + par.evt = evt; + return f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_STOP, &par, sizeof(par), NULL, 0, + NULL, L502_IOCTL_TIMEOUT); +} + +int32_t l502_port_cycle_check_setup(t_x502_hnd hnd, uint32_t ch, uint32_t *done) { + uint32_t rd_val; + int32_t err = f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_CHECK_SETUP, &ch, sizeof(ch), + &rd_val, sizeof(rd_val), NULL, L502_IOCTL_TIMEOUT); + if ((err == X502_ERR_OK) && (done != NULL)) + *done = rd_val; + return err; +} + +int32_t l502_port_renew_info(t_x502_hnd hnd) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_RELOAD_DEVINFO, NULL, 0, NULL, 0, NULL, + L502_IOCTL_TIMEOUT); +} + +int32_t l502_port_get_drv_ver(t_x502_hnd hnd, uint32_t *ver) { + uint32_t rd_val; + int32_t err = f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_GET_DRV_VERSION, NULL, 0, + &rd_val, sizeof(rd_val), NULL, L502_IOCTL_TIMEOUT); + if ((err == X502_ERR_OK) && (ver != NULL)) + *ver = rd_val; + return err; +} + + +int32_t l502_port_stream_read(t_x502_hnd hnd, uint32_t* buff, uint32_t size, uint32_t timeout) { + int send_size = 4*size; + uint32_t NumberOfBytesRead = 0; + int32_t err = X502_ERR_OK; + OVERLAPPED Ov; + + // инициализируем OVERLAPPED структуру + memset(&Ov, 0, sizeof(OVERLAPPED)); + Ov.hEvent = CreateEvent(NULL, FALSE , FALSE, NULL); + if(!Ov.hEvent) { + err = X502_ERR_MEMORY_ALLOC; + } + + if (err == X502_ERR_OK) { + // посылаем асинхронный запрос на сбор необходимого кол-ва данных + if(!ReadFile(L502_PCI_IFACE_FILE(hnd), buff, send_size, NULL, &Ov)) { + if(GetLastError() != ERROR_IO_PENDING) { + CancelIo(L502_PCI_IFACE_FILE(hnd)); + CloseHandle(Ov.hEvent); + err = X502_ERR_RECV; + GetOverlappedResult(L502_PCI_IFACE_FILE(hnd), &Ov, &NumberOfBytesRead, TRUE); + } + } + } + // ждём окончания выполнения запроса + if (err == X502_ERR_OK) { + if (WaitForSingleObject(Ov.hEvent, timeout) == WAIT_TIMEOUT) { + CancelIo(L502_PCI_IFACE_FILE(hnd)); + CloseHandle(Ov.hEvent); + GetOverlappedResult(L502_PCI_IFACE_FILE(hnd), &Ov, &NumberOfBytesRead, TRUE); + } else if(!GetOverlappedResult(L502_PCI_IFACE_FILE(hnd), &Ov, &NumberOfBytesRead, TRUE)) { + CancelIo(L502_PCI_IFACE_FILE(hnd)); + CloseHandle(Ov.hEvent); + err = X502_ERR_RECV; + } else { + CloseHandle(Ov.hEvent); + } + } + return err != X502_ERR_OK ? err : NumberOfBytesRead/4; +} + + + +int32_t l502_port_stream_write(t_x502_hnd hnd, const uint32_t* buff, + uint32_t size, uint32_t timeout) { + int send_size = 4*size; + uint32_t NumberOfBytesRead = 0; + int32_t err = X502_ERR_OK; + OVERLAPPED Ov; + + // инициализируем OVERLAPPED структуру + memset(&Ov, 0, sizeof(OVERLAPPED)); + Ov.hEvent = CreateEvent(NULL, FALSE , FALSE, NULL); + if(!Ov.hEvent) { + err = X502_ERR_MEMORY_ALLOC; + } + if (err == X502_ERR_OK) { + // посылаем асинхронный запрос на сбор необходимого кол-ва данных + if(!WriteFile(L502_PCI_IFACE_FILE(hnd), buff, send_size, NULL, &Ov)) { + if(GetLastError() != ERROR_IO_PENDING) { + CancelIo(L502_PCI_IFACE_FILE(hnd)); + CloseHandle(Ov.hEvent); + err = X502_ERR_SEND; + GetOverlappedResult(L502_PCI_IFACE_FILE(hnd), &Ov, &NumberOfBytesRead, TRUE); + } + } + } + // ждём окончания выполнения запроса + if (err == X502_ERR_OK) { + if (WaitForSingleObject(Ov.hEvent, timeout) == WAIT_TIMEOUT) { + CancelIo(L502_PCI_IFACE_FILE(hnd)); + CloseHandle(Ov.hEvent); + GetOverlappedResult(L502_PCI_IFACE_FILE(hnd), &Ov, &NumberOfBytesRead, TRUE); + } else if(!GetOverlappedResult(L502_PCI_IFACE_FILE(hnd), &Ov, &NumberOfBytesRead, TRUE)) { + CancelIo(L502_PCI_IFACE_FILE(hnd)); + CloseHandle(Ov.hEvent); + err = X502_ERR_SEND; + } else { + CloseHandle(Ov.hEvent); + } + } + return err != X502_ERR_OK ? err : NumberOfBytesRead/4; +} + + + +static int32_t f_get_file_par(SP_DEVICE_INTERFACE_DETAIL_DATA *detail, TCHAR* filebuf, + const TCHAR* file, char* res, uint32_t req_size, + uint32_t* read_size) { + HANDLE ifile; + int32_t err = 0; + _tcscpy(filebuf, TEXT(detail->DevicePath)); + _tcscat(filebuf, file); + + ifile = CreateFile(filebuf, GENERIC_READ, + FILE_SHARE_READ, + NULL, OPEN_EXISTING, + 0, NULL); + if (ifile != INVALID_HANDLE_VALUE) { + DWORD read_cnt=0; + if (!ReadFile(ifile, res, req_size, &read_cnt, NULL)) { + err = X502_ERR_GET_INFO; + } else { + if (read_size!=NULL) + *read_size = read_cnt; + } + CloseHandle(ifile); + } else { + err = X502_ERR_GET_INFO; + } + return err; +} + + + + +static int f_fill_devlist(SP_DEVICE_INTERFACE_DETAIL_DATA *detail, + t_x502_devrec *info) { + int32_t err = X502_ERR_OK; + TCHAR *filename = malloc(sizeof(TCHAR)*21 + _tcslen(detail->DevicePath)); + + if (filename == NULL) { + err = X502_ERR_MEMORY_ALLOC; + } else { + err = f_get_file_par(detail, filename, TEXT("\\name"), info->devname, + sizeof(info->devname), NULL); + if (err == X502_ERR_OK) { + /* получаем серийный номер устройства */ + err = f_get_file_par(detail, filename, TEXT("\\sn"), info->serial, + sizeof(info->serial), NULL); + } + + /* получаем информацию, открыто ли устройство */ + if (err == X502_ERR_OK) { + char val = '0'; + f_get_file_par(detail, filename, TEXT("\\opened"), &val, 1, NULL); + if (!err && (val!='0')) + info->flags |= X502_DEVFLAGS_DEVREC_OPENED; + } + + /* получаем информацию, присутствует ли BlackFin */ + if (err == X502_ERR_OK) { + char val = '0'; + if ((f_get_file_par(detail, filename, TEXT("\\bf"), &val, 1, NULL)==0) + && (val != '0')) { + info->flags |= X502_DEVFLAGS_BF_PRESENT; + } + + if ((f_get_file_par(detail, filename, TEXT("\\dac"), &val, 1, NULL)==0) + && (val != '0')) { + info->flags |= X502_DEVFLAGS_DAC_PRESENT; + } + + if ((f_get_file_par(detail, filename, TEXT("\\gal"), &val, 1, NULL)==0) + && (val != '0')) { + info->flags |= X502_DEVFLAGS_GAL_PRESENT; + } + } + + if (err == X502_ERR_OK) { + err = l502_devlist_gen(info, detail); + } + free(filename); + } + return err; +} + +int32_t l502_port_free_iface_data(void *intptr) { + free(intptr); + return X502_ERR_OK; +} + + +int32_t l502_port_open(t_x502_hnd hnd, const t_x502_devrec *devinfo) { + SP_DEVICE_INTERFACE_DETAIL_DATA *detail = (SP_DEVICE_INTERFACE_DETAIL_DATA *)devinfo->internal->iface_data; + int32_t err = X502_ERR_OK; + HANDLE file = CreateFile(detail->DevicePath, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + if (file != INVALID_HANDLE_VALUE) { + hnd->iface_data = malloc(sizeof(t_pci_iface_data)); + if (hnd->iface_data == NULL) { + err = X502_ERR_MEMORY_ALLOC; + CloseHandle(file); + } else { + L502_PCI_IFACE_FILE(hnd) = file; + } + } else { + DWORD syserr = GetLastError(); + if (syserr == ERROR_ACCESS_DENIED) { + err = X502_ERR_DEVICE_ACCESS_DENIED; + } else if (syserr == ERROR_FILE_NOT_FOUND) { + err = X502_ERR_DEVICE_NOT_FOUND; + } else { + err = X502_ERR_DEVICE_OPEN; + } + } + return err; +} + +int32_t l502_port_close(t_x502_hnd hnd) { + if (hnd->iface_data !=NULL) { + CloseHandle(L502_PCI_IFACE_FILE(hnd)); + free(hnd->iface_data); + hnd->iface_data = NULL; + } + return X502_ERR_OK; +} + + + +X502_EXPORT(int32_t) L502_GetDevRecordsList(t_x502_devrec *list, uint32_t size, + uint32_t flags, uint32_t *devcnt) { + HDEVINFO infoSet; + SP_DEVINFO_DATA infoData; + DWORD index=0; + uint32_t curcnt=0; + int32_t err = X502_ERR_OK; + + + infoData.cbSize = sizeof(SP_DEVINFO_DATA); + + /* получаем список устройств с подержкой интерфейса lpcie */ + infoSet = SetupDiGetClassDevs(&GUID_LPCIE_INTERFACE,NULL, + NULL, + DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + + /* проходимся по всем устройствам из списка */ + while (SetupDiEnumDeviceInfo(infoSet, index, &infoData)) { + SP_DEVICE_INTERFACE_DATA intfData; + intfData.cbSize = sizeof(intfData); + /* получаем информацию о интерфейсе */ + if (SetupDiEnumDeviceInterfaces(infoSet, &infoData, &GUID_LPCIE_INTERFACE, + 0, &intfData)) { + DWORD req_size; + /* узнаем резмер детальной информации о интерфейсе (нужна для + получения имени устройства) */ + if (!SetupDiGetDeviceInterfaceDetail(infoSet, &intfData, NULL, + 0, &req_size, NULL) + && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { + SP_DEVICE_INTERFACE_DETAIL_DATA* detail = + (SP_DEVICE_INTERFACE_DETAIL_DATA*) malloc(req_size); + + if (detail != NULL) { + int detail_used = 0; + + /* пытаемся получить всю информацию */ + detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + if (SetupDiGetDeviceInterfaceDetail(infoSet, &intfData, + detail, req_size, + NULL, NULL)) { + t_x502_devrec info; + int info_used = 0; + detail_used = 1; + + X502_DevRecordInit(&info); + + /* получаем информацию о устройстве из служебных файлов, + * предоставляемых драйвером */ + if (f_fill_devlist(detail, &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); + } + + if (!detail_used) { + free(detail); + } + } else { + err = X502_ERR_MEMORY_ALLOC; + } + } + } + index++; + } + + + if (infoSet != NULL) { + SetupDiDestroyDeviceInfoList(infoSet); + } + + if (devcnt != NULL) + *devcnt = curcnt; + + return err != X502_ERR_OK ? err : curcnt > size ? size : curcnt ; +} diff --git a/main.c b/main.c index 85eaf81..f97cbe8 100644 --- a/main.c +++ b/main.c @@ -1049,7 +1049,7 @@ int main(int argc, char** argv) { if (hnd != NULL) { // получаем информацию // t_x502_info info; - err = X502_GetDevInfo(hnd, &info); +// err = X502_GetDevInfo(hnd, &info); if (err != X502_ERR_OK) { fprintf(stderr, "Ошибка получения серийного информации о модуле: %s!", X502_GetErrorString(err)); } else { @@ -1186,7 +1186,7 @@ int main(int argc, char** argv) { fclose(logfile_ptr); - printf("dumping to file: %s\n", logfilename); +// printf("dumping to file: %s\n", logfilename); printf("\n"); //printf("start streams sampling\n"); diff --git a/makefile b/makefile index 2ec8ae0..706e0a4 100644 --- a/makefile +++ b/makefile @@ -7,44 +7,107 @@ # # Ниже приведено несколько примеров в закоментированном виде -#--- Linux с заголовками и библиотекой в стандартных директориях: компилятор GCC -#CC = gcc - -#--- Вариант запуска из MSYS со стандартным 32-битным mingw -#CC = gcc -#X502API_LIBRARIES_DIR = "/c/Program Files/L-Card/lpcie/lib/mingw" -#X502API_INCLUDE_DIR = "/c/Program Files/L-Card/lpcie/include" - - -#--- 64-битный вариант mingw w64, идущий вместе с cygwin -------- -#CC = x86_64-w64-mingw32-gcc -#X502API_LIBRARIES_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/lib/mingw64" -#X502API_INCLUDE_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/include" - -#--- 32-битный вариант mingw w64, идущий вместе с cygwin -------- -#CC = i686-w64-mingw32-gcc -#X502API_LIBRARIES_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/lib/mingw" -#X502API_INCLUDE_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/include" - -#--- 32-битный вариант mingw, идущий вместе с cygwin -------- -#CC = i686-pc-mingw32-gcc -#X502API_LIBRARIES_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/lib/mingw" -#X502API_INCLUDE_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/include" - - -FLAGS = - -ifdef X502API_LIBRARIES_DIR - FLAGS += -L $(X502API_LIBRARIES_DIR) -endif - -ifdef X502API_INCLUDE_DIR - FLAGS += -I $(X502API_INCLUDE_DIR) -endif - - - -all: - $(CC) main.c $(FLAGS) -ll502api -le502api -lx502api -g -o BF_companion -clean: - -rm BF_companion +#--- Linux с заголовками и библиотекой в стандартных директориях: компилятор GCC +#CC = gcc + +#--- Вариант запуска из MSYS со стандартным 32-битным mingw +#CC = gcc +#X502API_LIBRARIES_DIR = "/c/Program Files/L-Card/lpcie/lib/mingw" +#X502API_INCLUDE_DIR = "/c/Program Files/L-Card/lpcie/include" + + +#--- 64-битный вариант mingw w64, идущий вместе с cygwin -------- +#CC = x86_64-w64-mingw32-gcc +#X502API_LIBRARIES_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/lib/mingw64" +#X502API_INCLUDE_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/include" + +#--- 32-битный вариант mingw w64, идущий вместе с cygwin -------- +#CC = i686-w64-mingw32-gcc +#X502API_LIBRARIES_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/lib/mingw" +#X502API_INCLUDE_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/include" + +#--- 32-битный вариант mingw, идущий вместе с cygwin -------- +#CC = i686-pc-mingw32-gcc +#X502API_LIBRARIES_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/lib/mingw" +#X502API_INCLUDE_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/include" + + +CC ?= gcc +CFLAGS ?= -O2 -g +BUILD_DIR ?= build + +X502API_ROOT := x502api-1.1.34 +X502_SRC_DIR := $(X502API_ROOT)/src +L502_SRC_DIR := $(X502API_ROOT)/devs/l502 +E502_SRC_DIR := $(X502API_ROOT)/devs/e502 +OSSPEC_DIR := $(X502API_ROOT)/lib/osspec +LTIMER_DIR := $(X502API_ROOT)/lib/ltimer +LCSPEC_DIR := $(X502API_ROOT)/lib/lcspec +CRC_DIR := $(X502API_ROOT)/lib/crc +LCSPEC_COMPILER ?= gcc +LTIMER_PORT := linux + +INCLUDES := \ + -I$(X502_SRC_DIR) \ + -I$(L502_SRC_DIR) \ + -I$(E502_SRC_DIR) \ + -I$(OSSPEC_DIR) \ + -I$(LTIMER_DIR) \ + -I$(LTIMER_DIR)/ports/$(LTIMER_PORT) \ + -I$(LCSPEC_DIR) \ + -I$(LCSPEC_DIR)/$(LCSPEC_COMPILER) \ + -I$(CRC_DIR) + +DEFINES := -DOSSPEC_USE_MUTEX -DOSSPEC_USE_EVENTS -DOSSPEC_USE_THREADS \ + -DX502API_VER_MAJOR=1 -DX502API_VER_MINOR=1 -DX502API_VER_PATCH=34 \ + -DENABLE_USB + +X502_SRCS := \ + x502api.c \ + x502api_streams.c \ + x502api_async.c \ + x502api_bf.c \ + x502api_config.c \ + x502api_eeprom.c \ + x502api_errs.c + +L502_SRCS := \ + l502api.c \ + l502api_compat.c \ + l502api_eeprom.c \ + l502api_bf.c \ + linux/l502_spec.c + +E502_SRCS := \ + e502api.c \ + e502api_usb.c \ + e502api_tcp.c \ + e502api_dnssd.c \ + e502api_eth_config.c + +OSSPEC_SRCS := $(OSSPEC_DIR)/osspec.c +LTIMER_SRCS := $(LTIMER_DIR)/ports/$(LTIMER_PORT)/lclock.c +CRC_SRCS := $(CRC_DIR)/fast_crc.c + +SRCS := \ + main.c \ + $(addprefix $(X502_SRC_DIR)/,$(X502_SRCS)) \ + $(addprefix $(L502_SRC_DIR)/,$(L502_SRCS)) \ + $(addprefix $(E502_SRC_DIR)/,$(E502_SRCS)) \ + $(OSSPEC_SRCS) \ + $(LTIMER_SRCS) \ + $(CRC_SRCS) + +OBJS := $(addprefix $(BUILD_DIR)/,$(SRCS:.c=.o)) + +$(BUILD_DIR)/%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -c $< -o $@ + +all: BF_companion + +BF_companion: $(OBJS) + $(CC) $(CFLAGS) $^ -lpthread -lusb-1.0 -lm -o $@ + +clean: + rm -rf $(BUILD_DIR) BF_companion diff --git a/plotter.py b/plotter.py index b506def..bcebbb4 100755 --- a/plotter.py +++ b/plotter.py @@ -1,4 +1,4 @@ -#!/usr/bin/pypy3 +#!/usr/bin/python3 import plotly.graph_objs as go from decimal import * from sys import argv @@ -105,5 +105,6 @@ if __name__ == "__main__": #chart.add_trace(go.Scattergl(x=data[key+"_N"], y=data[key], name=key, mode="markers+lines", text=data[key+"_hex"])) # chart.add_trace(go.Scattergl(x=data[key+"_N"], y=data[key], name=key, mode="lines+markers", text=data[key+"_hex"])) - chart.show() +# chart.show() + chart.write_html(argv[1] + ".html") diff --git a/x502/fast_crc_cfg.h b/x502/fast_crc_cfg.h new file mode 100644 index 0000000..7bc0fbc --- /dev/null +++ b/x502/fast_crc_cfg.h @@ -0,0 +1,31 @@ +/*================================================================================================* + * Конфигурация библиотеки FAST_CRC + *================================================================================================*/ + +#ifndef FAST_CRC_CFG_H_ +#define FAST_CRC_CFG_H_ + +#include "lcard_pstdint.h" + +/*================================================================================================*/ +/* Типы */ +#define FASTCRC_U8_TYPE uint8_t +#define FASTCRC_U16_TYPE uint16_t +#define FASTCRC_U32_TYPE uint32_t +#define FASTCRC_SIZE_TYPE size_t + +/* Начальные значения CRC */ +#define CRC16_START_VAL 0 +#define CRC32_START_VAL 0 +/*================================================================================================*/ + +/*================================================================================================*/ +/* Разрешение компиляции отдельных функций */ +#define FASTCRC_CRC16_ADD8 1 /* добавление в CRC16 байта */ +#define FASTCRC_CRC16_ADD16 1 /* добавление в CRC16 16-битного слова */ +#define FASTCRC_CRC16_BLOCK8 1 /* Вычисление CRC16 блока байтов */ +#define FASTCRC_CRC16_BLOCK16 1 /* Вычисление CRC16 блока 16-битных слов */ +#define FASTCRC_CRC32_BLOCK8 1 /* Вычисление CRC32 блока байтов */ +/*================================================================================================*/ + +#endif /* FAST_CRC_CFG_H_ */ diff --git a/x502/l502_bf_cmd_defs.h b/x502/l502_bf_cmd_defs.h new file mode 100644 index 0000000..6f37930 --- /dev/null +++ b/x502/l502_bf_cmd_defs.h @@ -0,0 +1,167 @@ +/*********************************************************************//** + @addtogroup cmd_process + @{ + @file l502_bf_cmd_defs.h Файл содержит определения, + которые используются для передачи команд от ПК в DSP + (определения команд, их параметров, результатов выполнения) + @date 28.03.2012 + @author Borisov Alexey + *************************************************************************/ +#ifndef L502_BF_CMD_DEFS_H_ +#define L502_BF_CMD_DEFS_H_ + +/** Максимальный размер данных, передавемых с командой в 32-битных словах */ +#define L502_BF_CMD_DATA_SIZE_MAX (1024) + + + +/** Статус команд управления сигнальным процессором */ +typedef enum { + L502_BF_CMD_STATUS_IDLE = 0x0, /**< Начальное состояние (команда вообщен не выполнялась) */ + L502_BF_CMD_STATUS_REQ = 0x5A01, /**< Передан запрос на обработку команды от ПК*/ + L502_BF_CMD_STATUS_PROGRESS = 0x5A02, /**< Сигнальный процессор начал обработку команды */ + L502_BF_CMD_STATUS_DONE = 0x5A03 /**< Команда выполнена. Результат выполнения в поле ret_code */ +} t_l502_bf_cmd_status; + + +/** Коды команд управления сигнальным процессором */ +typedef enum { + L502_BF_CMD_CODE_TEST = 0x01, /**< Запуск теста (параметр определяет тип теста) */ + L502_BF_CMD_CODE_SET_PARAM = 0x02, /**< Установить параметр (код параметра в поле param) */ + L502_BF_CMD_CODE_GET_PARAM = 0x03, /**< Прочитать текущее значение параметра */ + L502_BF_CMD_CODE_CONFIGURE = 0x04, /**< Сконфигурировать модуль в соответствии с ранее установленными параметрами */ + L502_BF_CMD_CODE_STREAM_EN = 0x05, /**< Разрешение потоков ввода/вывода */ + L502_BF_CMD_CODE_STREAM_DIS = 0x06, /**< Запрещение потоков ввода/вывода */ + L502_BF_CMD_CODE_STREAM_START = 0x07, /**< Запуск потоков ввода/вывода */ + L502_BF_CMD_CODE_STREAM_STOP = 0x08, /**< Останов потоков ввода/вывода */ + L502_BF_CMD_CODE_PRELOAD = 0x09, /**< Предзагрузка данных на ЦАП */ + L502_BF_CMD_CODE_ASYNC_OUT = 0x10, /**< Асинхронный вывод (куда - зависит от параметра) */ + L502_BF_CMD_CODE_ASYNC_DIG_IN = 0x11, /**< Асинхронный ввод с цифровых линий */ + L502_BF_CMD_CODE_ADC_GET_FRAME = 0x12, /**< Асинхронный ввод карда АЦП */ + L502_BF_CMD_CODE_FPGA_REG_WR = 0x13, /**< Запись в регистр FPGA (param --- адрес регистра, data0 --- значение) */ + L502_BF_CMD_CODE_FPGA_REG_RD = 0x14, /**< Чтение из регистра FPGA (param --- адрес регистра, ответ: resp0 --- значение) */ + L502_BF_CMD_CODE_GET_OUT_STATUS= 0x15, /**< Получение флагов статуса вывода */ +} t_l502_bf_cmd_code; + +#define L502_BF_CMD_CODE_USER 0x8000U /**< Код, с которого начинаются пользовательские команды */ + + + +/** @brief Варианты тестов + + Коды тестов, передающиеся в параметре команды #L502_BF_CMD_CODE_TEST */ +typedef enum { + L502_BF_CMD_TEST_STOP = 0x00, /**< Останов выполняемого теста */ + L502_BF_CMD_TEST_GET_RESULT = 0x01, /**< Получение результата теста */ + L502_BF_CMD_TEST_ECHO = 0x10, /**< Тест эхо - возвращает те же данные что передавались */ + L502_BF_CMD_TEST_SPORT = 0x11, /**< Тест интерфейса SPORT в кольцевом режиме */ + L502_BF_CMD_TEST_SDRAM = 0x12, /**< Тест SDRAM памяти */ + L502_BF_CMD_TEST_SPI = 0x13 /**< Тест интерфейса SPI */ +} t_l502_bf_test_code; + + +/** @brief Устанавливаемые параметры + + Коды пареметров, устанавливаемых командой #L502_BF_CMD_CODE_SET_PARAM или + получаемых с помщью команды #L502_BF_CMD_CODE_GET_PARAM */ +typedef enum { + L502_BF_PARAM_FIRM_VERSION = 0x00, /**< Версия прошивки - 4 байта */ + L502_BF_PARAM_STREAM_MODE = 0x01, /**< Режим работы (запущен поток или нет) */ + L502_BF_PARAM_ENABLED_STREAMS = 0x02, /**< Какие потоки разрешены */ + L502_BF_PARAM_MODULE_INFO = 0x03, /**< Запись информации о модуле */ + L502_BF_PARAM_IN_BUF_SIZE = 0x10, /**< Размер буфера на преием */ + L502_BF_PARAM_CYCLE_BUF_SIZE = 0x11, /**< Размер буфера для записи циклического сигнала */ + L502_BF_PARAM_LCH_CNT = 0x20, /**< Количество логических каналов в таблице */ + L502_BF_PARAM_LCH = 0x21, /**< Параметры логического канала */ + L502_BF_PARAM_ADC_FREQ_DIV = 0x22, /**< Делитель частоты АЦП */ + L502_BF_PARAM_REF_FREQ_SRC = 0x23, /**< Выбор опорной частоты */ + L502_BF_PARAM_ADC_FRAME_DELAY = 0x24, /**< Значение межкадровой задержки */ + L502_BF_PARAM_SYNC_MODE = 0x25, /**< Режим синхронизации */ + L502_BF_PARAM_SYNC_START_MODE = 0x26, /**< Условие запуска синхронных потоков сбора данных */ + L502_BF_PARAM_ADC_COEF = 0x27, /**< Установка коэффициентов для заданного диапазона АЦП */ + L502_BF_PARAM_DAC_COEF = 0x28, /**< Установка коэффициентов для заданного канала ЦАП */ + L502_BF_PARAM_DIN_FREQ_DIV = 0x30, /**< Делитель частоты цифрового ввода */ + L502_BF_PARAM_DAC_FREQ_DIV = 0x31, /**< Делитель частоты вывода на ЦАП */ + L502_BF_PARAM_IN_STEP_SIZE = 0x32, /**< Шаг для обработки входных данных */ + L502_BF_PARAM_IN_STREAM_MODE = 0x100 /**< Режим работы потока на ввод */ +} t_l502_bf_params; + + + + + +/** @brief Тип асинхронного вывода + + Код, задающий в параметре команды команды #L502_BF_CMD_CODE_ASYNC_OUT, + куда должно выводится передаваемое значение */ +typedef enum { + L502_BF_CMD_ASYNC_TYPE_DOUT = 0x0, /**< Вывод на цифровые линии */ + L502_BF_CMD_ASYNC_TYPE_DAC1 = 0x1, /**< Вывод на первый канал ЦАП */ + L502_BF_CMD_ASYNC_TYPE_DAC2 = 0x2 /**< Вывод на второй канал ЦАП */ +} t_l502_bf_cmd_async_type; + +/** Коды завершения команд */ +typedef enum { + L502_BF_ERR_SUCCESS = 0, /**< Команда выполнена успешно */ + L502_BF_ERR_FIRST_CODE = -512, /**< Код ошибки, с которого начинаются отсальные коды. + Используется, чтобы разделить на верхнем уровне ошибки библиотеки и + возвращенные сигнальным процессором */ + L502_BF_ERR_UNSUP_CMD = -512, /**< Неизвестный код команды */ + L502_BF_ERR_CMD_OVERRUN = -513, /**< Пришла команда до того, как была завершена предыдущая */ + L502_BF_ERR_INVALID_CMD_PARAMS = -514, /**< Неверное значение параметра команды */ + L502_BF_ERR_INSUF_CMD_DATA = -515, /**< Недостаточное кол-во данных передано с командой */ + L502_BF_ERR_STREAM_RUNNING = -516, /**< Команда не допустима при запущеном сборе, а сбор запущен */ + L502_BF_ERR_STREAM_STOPPED = -517, /**< Команда допустима только при запущеном сборе, а сбор остановлен */ + L502_BF_ERR_NO_TEST_IN_PROGR = -518, /**< Сейчас не выполняется никакого теста */ + L502_BF_ERR_TEST_VALUE = -519 /**< Считано неверное значение при выполнении теста */ +} t_l502_bf_err_code; + + +/** Режим работы сигнального процессора */ +typedef enum { + L502_BF_MODE_IDLE = 0, /**< Ждущий режим, поток не запущен */ + L502_BF_MODE_STREAM = 1, /**< Запущены потоки ввода-вывода */ + L502_BF_MODE_TEST = 2 /**< Тестовый режим */ +} t_l502_bf_mode; + +/** Возможности, поддерживаемые прошивкой blackfin */ +typedef enum { + L502_BF_FEATURE_FPGA_REG_ACCESS = 0x1, /**< Признак, что реализованы команды + прямого доступа к регистрам FPGA */ + L502_BF_FEATURE_OUT_STATUS_FLAGS = 0x2 /**< Признак, что реализована команда + #L502_BF_CMD_CODE_GET_OUT_STATUS */ +} t_l502_bf_features; + +/** @brief Параметры команды + + Структура описывает расположение полей в области памяти BlackFin, используемой + для передачи команд между персональным компьютером и сигнальным процессором */ +typedef struct { + uint16_t code; /**< Код команды из #t_l502_bf_cmd_code */ + uint16_t status; /**< Статус выполнения - в обработчике не изменяется */ + uint32_t param; /**< Параметр команды */ + int32_t result; /**< Код результата выполнения команды */ + uint32_t data_size; /**< Количество данных, переданных с командой или возвращенных с ответом в 32-битных словах */ + uint32_t data[L502_BF_CMD_DATA_SIZE_MAX]; /**< Данные, передаваемые с командой и/или в качестве результата */ +} t_l502_bf_cmd; + + +/** @brief Результат выполнения теста + + Структура описывает параметры выполняемого теста, возвращаемые в качестве + данных на команду #L502_BF_CMD_CODE_TEST с параметром + #L502_BF_CMD_TEST_GET_RESULT */ +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_l502_bf_test_res; + +/** @} */ + +#endif diff --git a/x502/lboot_req.h b/x502/lboot_req.h new file mode 100644 index 0000000..1bce1ec --- /dev/null +++ b/x502/lboot_req.h @@ -0,0 +1,270 @@ +/***************************************************************************//** + @file lcspec.h + + Файл содержит константы и типы, которые использует как загрузчик lboot, + так и приложение: + - константы и типы для запроса на перепрошивку со стороны application + - константы и типы для определения информации о прошивке, которая хранится + внутри прошивки и проверяется bootloader'ом + + !!Файл должен быть один (или одинаковый) для обоих проектов!! + + @date 16.11.2010 + @author: Borisov Alexey + ******************************************************************************/ + +#ifndef LBOOT_REQ_H_ +#define LBOOT_REQ_H_ + + +/** адрес, по которому должен быть сформирован запрос на перепрошивку */ +#define LBOOT_REQ_ADDR 0x10000000 +/** Адрес, начиная с кторого хранится информация о загрузчике */ +#define LBOOT_INFO_ADDR 0x200 + +/******************* размеры полей ***************************************/ +#define LBOOT_DEVNAME_SIZE 32 +#define LBOOT_SERIAL_SIZE 32 +#define LBOOT_SOFTVER_SIZE 32 +#define LBOOT_REVISION_SIZE 16 +#define LBOOT_IMPLEMENTATION_SIZE 16 +#define LBOOT_SPECINFO_SIZE 64 + +#define LBOOT_MAC_ADDR_SIZE 6 + +#define LBOOT_REQ_MAX_FILENAME_SIZE 256 +#define LBOOT_REQ_MAX_SIZE 4096 + + +/** различные коды режимов загрузки */ +#define LBOOT_BOOTMODE_MODBUS_RTU 0x100 +#define LBOOT_BOOTMODE_TFTP_CLIENT 0x200 +#define LBOOT_BOOTMODE_TFTP_SERVER 0x201 +#define LBOOT_BOOTMODE_USB 0x300 +#define LBOOT_BOOTMODE_CAN_LSS 0x400 + +#define LBOOT_BOOTMODE_TFTP LBOOT_BOOTMODE_TFTP_CLIENT + +//режим работы устройства (application/bootloader) - в modbus сразу за device info +#define LBOOT_MODE_BOOT 0x1 +#define LBOOT_MODE_APPL 0x2 + +#define LBOOT_TFTP_FLAGS_PACKED 0x2 + + +#define LBOOT_SEED_CODE_NOSIGN 0xFFAA5500 + + +/** признак, находящийся в информации о прошивке, по которому можно узнать + наличие этой информации. Признак означает, что поддерживается защита этой + информации CRC */ +#define LBOOT_INFO_SIGNATURE 0x4C42A55A + + + +/** общие для всех интерфейсов флаги, определяющие поведение загрузчика */ +typedef enum { + /** - при активном запросе прошивки (tftp) - признак, + что перезависывется резервная копия, а не основная прошивка + - при пассивном приеме (mbrtu, usb, can_lss) - признак, + что разрешено перезаписывать резервную копию */ + LBOOT_REQ_FLAGS_RECOVERY_WR = 0x01, + /** признак, что не нужно сверять имя устройства в прошивке */ + LBOOT_REQ_FLAGS_DONT_CHECK_NAME = 0x02, + /** не проверять, что записывается стабильная прошивка (установлен флаг + #LBOOT_APP_FLAGS_STABLE в информации о прошивке)*/ + LBOOT_REQ_FLAGS_DONT_CHECK_STABLE = 0x04, + /** разрешение записывать прошивку для разработчика (с установленным флагом + #LBOOT_APP_FLAGS_DEVELOP в информации о прошивке)*/ + LBOOT_REQ_FLAGS_ENABLE_DEVELOP = 0x08, + /** разрешение записи прошивки без подписи (если поддерживается загрузчиком) */ + LBOOT_REQ_FLAGS_ENABLE_NO_SIGN = 0x10 +} t_lboot_req_flags; + +/** флаги из #t_app_info */ +typedef enum { + /** признак, что прошивка стабильная */ + LBOOT_APP_FLAGS_STABLE = 0x1, + /** признак, что это прошивка для разработчиков */ + LBOOT_APP_FLAGS_DEVELOP = 0x2 +} t_lboot_app_flags; + + + + +#pragma pack(1) +/** Информация о программе, содержащаяся в прошивке */ +struct st_app_info { + uint32_t size; /**< размер информции о прошивке */ + uint32_t flags; /**< флаги */ + /** название устройства, для которого предназначена прошивка*/ + char devname[LBOOT_DEVNAME_SIZE]; +}; +typedef struct st_app_info t_app_info; + + + + +/** Информация о загрузчике, находящаяся непосредственно + в коде самого загрузчика */ +struct lboot_info_st { + /** версия загрузчика (младший байт - минорная, старший - мажорная) */ + uint16_t ver; + uint16_t flags; /**< флаги - резерв */ + /** серийный номер устройства */ + char serial[LBOOT_SERIAL_SIZE]; + uint8_t mac[6]; /**< mac-адрес устройства */ + /** ревизия платы (если вшита в загрузчик)*/ + char brd_revision[LBOOT_REVISION_SIZE]; + /** модификация (если вшита в загрузчик)*/ + char brd_impl[LBOOT_IMPLEMENTATION_SIZE]; + char creation_date[18]; /**< дата создания загрузчика */ + /** признак, что эта структура действительна (только с v1.3) */ + uint32_t sign; + /** размер структуры (включая два байта crc) + - для определения положения crc */ + uint32_t size; + char devname[LBOOT_DEVNAME_SIZE]; + /** поддерживаемые возможности загрузчика */ + uint32_t features; + uint32_t reserv; + uint8_t ip_addr[4]; /**< ip-адрес устройства по-умолчанию */ + uint8_t ip_mask[4]; /**< маска устройства по-умолчанию */ + uint8_t gate[4]; /**< адрес шлюза по-умолчанию */ + /** crc (но при проверке следует брать ее не отсюда, + а по адресу = адрес начала структуры + size - 2) */ + uint16_t crc; +}; +typedef struct lboot_info_st t_lboot_info; + +/** информация о устройстве и прошивке */ +struct lboot_devinfo_st { + char devname[LBOOT_DEVNAME_SIZE]; /**< название устройства */ + char serial[LBOOT_SERIAL_SIZE]; /**< серийный номер */ + char soft_ver[LBOOT_SOFTVER_SIZE]; /**< версия прошивки */ + char brd_revision[LBOOT_REVISION_SIZE]; /**< ревизия платы */ + char brd_impl[LBOOT_IMPLEMENTATION_SIZE]; /**< опции платы */ + char spec_info[LBOOT_SPECINFO_SIZE]; /**< резерв */ +}; +typedef struct lboot_devinfo_st t_lboot_devinfo; + + +/** стандартный заголовок запроса на перепрошивку */ +struct lboot_params_hdr_st { + uint32_t size; /**< размер структуры запроса (включая + специфические для интерфейса данные и crc) */ + uint32_t bootmode; /**< режим загрузки - определяет интерфейс */ + uint32_t flags; /**< общие флаги */ + uint32_t timeout; /**< таймаут в мс - сколько загрузчик будет ожидать + запроса на перепрошивку (0 - бесконечно) */ + uint32_t reserv[2]; /**< резерв */ + t_lboot_devinfo devinfo; /**< информация о устройстве и прошивке */ +}; +typedef struct lboot_params_hdr_st t_lboot_params_hdr; + + +//специфические для tftp параметры +struct st_lboot_specpar_tftp { + uint16_t flags; //спец флаги (резерв) + uint16_t server_port; + uint8_t mac[6]; //mac-адрес + uint8_t l_ip[4]; //свой ip-адрес + uint8_t mask[4]; //mask - маска сети + uint8_t gate[4]; //адрес шлюза + uint8_t r_ip[4]; //адрес сервера tftp + char filename[LBOOT_REQ_MAX_FILENAME_SIZE]; //имя файла с прошивкой + uint16_t crc; +}; +typedef struct st_lboot_specpar_tftp t_lboot_specpar_tftp; + +//параметры для запроса прошивки по tftp +struct st_lboot_params_tftp { + t_lboot_params_hdr hdr; + uint16_t tftp_flags; //спец флаги (резерв) + uint16_t server_port; + uint8_t mac[6]; //mac-адрес + uint8_t l_ip[4]; //свой ip-адрес + uint8_t mask[4]; //mask - маска сети + uint8_t gate[4]; //адрес шлюза + uint8_t r_ip[4]; //адрес сервера tftp + char filename[LBOOT_REQ_MAX_FILENAME_SIZE]; //имя файла с прошивкой + uint16_t crc; +}; +typedef struct st_lboot_params_tftp t_lboot_params_tftp; + + + +struct st_lboot_specpar_modbus_rtu { + uint16_t flags ; //спец флаги + uint8_t addr ; //адрес устройства в modbus сети + uint8_t parity ; + uint16_t res ; + uint32_t baud_rate; //скорость передачи по rs-485/232 + uint16_t crc; +}; +typedef struct st_lboot_specpar_modbus_rtu t_lboot_specpar_modbus_rtu; + +//параметры для перепрошивки по Modbus RTU +struct st_lboot_params_modbus_rtu { + t_lboot_params_hdr hdr ; + uint16_t flags ; //спец флаги + uint8_t addr ; //адрес устройства в modbus сети + uint8_t parity ; + uint16_t res ; + uint32_t baud_rate; //скорость передачи по rs-485/232 + uint16_t crc; +}; +typedef struct st_lboot_params_modbus_rtu t_lboot_params_modbus_rtu; + + + +struct st_lboot_specpar_usb { + uint16_t flags ; //спец флаги + uint16_t crc; +}; +typedef struct st_lboot_specpar_usb t_lboot_specpar_usb; + +//параметры для перепрошивки по USB +struct st_lboot_params_usb { + t_lboot_params_hdr hdr ; + uint16_t flags ; //спец флаги + uint16_t crc; +}; +typedef struct st_lboot_params_usb t_lboot_params_usb; + +struct st_lboot_specpar_can_lss { + uint16_t flags; + uint8_t br_index; /* BaudRate Index */ + uint8_t res; + uint32_t vid; + uint32_t pid; + uint32_t rev; + uint16_t crc; +}; +typedef struct st_lboot_specpar_can_lss t_lboot_specpar_can_lss; + +struct st_lboot_params_can_lss { + t_lboot_params_hdr hdr; + t_lboot_specpar_can_lss can; +}; +typedef struct st_lboot_params_can_lss t_lboot_params_can_lss; + + + +struct st_lboot_params { + t_lboot_params_hdr hdr ; + union { + t_lboot_specpar_tftp tftp; + t_lboot_specpar_modbus_rtu mbrtu; + t_lboot_specpar_usb usb; + t_lboot_specpar_can_lss can; + }; +}; +typedef struct st_lboot_params t_lboot_params; + +#pragma pack() + + + + +#endif /* LBOOT_REQ_H_ */ diff --git a/x502/lcard_pstdint.h b/x502/lcard_pstdint.h new file mode 100644 index 0000000..ebc63ab --- /dev/null +++ b/x502/lcard_pstdint.h @@ -0,0 +1,810 @@ +/* A portable stdint.h + **************************************************************************** + * BSD License: + **************************************************************************** + * + * Copyright (c) 2005-2011 Paul Hsieh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **************************************************************************** + * + * Version 0.1.12 + * + * The ANSI C standard committee, for the C99 standard, specified the + * inclusion of a new standard include file called stdint.h. This is + * a very useful and long desired include file which contains several + * very precise definitions for integer scalar types that is + * critically important for making portable several classes of + * applications including cryptography, hashing, variable length + * integer libraries and so on. But for most developers its likely + * useful just for programming sanity. + * + * The problem is that most compiler vendors have decided not to + * implement the C99 standard, and the next C++ language standard + * (which has a lot more mindshare these days) will be a long time in + * coming and its unknown whether or not it will include stdint.h or + * how much adoption it will have. Either way, it will be a long time + * before all compilers come with a stdint.h and it also does nothing + * for the extremely large number of compilers available today which + * do not include this file, or anything comparable to it. + * + * So that's what this file is all about. Its an attempt to build a + * single universal include file that works on as many platforms as + * possible to deliver what stdint.h is supposed to. A few things + * that should be noted about this file: + * + * 1) It is not guaranteed to be portable and/or present an identical + * interface on all platforms. The extreme variability of the + * ANSI C standard makes this an impossibility right from the + * very get go. Its really only meant to be useful for the vast + * majority of platforms that possess the capability of + * implementing usefully and precisely defined, standard sized + * integer scalars. Systems which are not intrinsically 2s + * complement may produce invalid constants. + * + * 2) There is an unavoidable use of non-reserved symbols. + * + * 3) Other standard include files are invoked. + * + * 4) This file may come in conflict with future platforms that do + * include stdint.h. The hope is that one or the other can be + * used with no real difference. + * + * 5) In the current verison, if your platform can't represent + * int32_t, int16_t and int8_t, it just dumps out with a compiler + * error. + * + * 6) 64 bit integers may or may not be defined. Test for their + * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX. + * Note that this is different from the C99 specification which + * requires the existence of 64 bit support in the compiler. If + * this is not defined for your platform, yet it is capable of + * dealing with 64 bits then it is because this file has not yet + * been extended to cover all of your system's capabilities. + * + * 7) (u)intptr_t may or may not be defined. Test for its presence + * with the test: #ifdef PTRDIFF_MAX. If this is not defined + * for your platform, then it is because this file has not yet + * been extended to cover all of your system's capabilities, not + * because its optional. + * + * 8) The following might not been defined even if your platform is + * capable of defining it: + * + * WCHAR_MIN + * WCHAR_MAX + * (u)int64_t + * PTRDIFF_MIN + * PTRDIFF_MAX + * (u)intptr_t + * + * 9) The following have not been defined: + * + * WINT_MIN + * WINT_MAX + * + * 10) The criteria for defining (u)int_least(*)_t isn't clear, + * except for systems which don't have a type that precisely + * defined 8, 16, or 32 bit types (which this include file does + * not support anyways). Default definitions have been given. + * + * 11) The criteria for defining (u)int_fast(*)_t isn't something I + * would trust to any particular compiler vendor or the ANSI C + * committee. It is well known that "compatible systems" are + * commonly created that have very different performance + * characteristics from the systems they are compatible with, + * especially those whose vendors make both the compiler and the + * system. Default definitions have been given, but its strongly + * recommended that users never use these definitions for any + * reason (they do *NOT* deliver any serious guarantee of + * improved performance -- not in this file, nor any vendor's + * stdint.h). + * + * 12) The following macros: + * + * PRINTF_INTMAX_MODIFIER + * PRINTF_INT64_MODIFIER + * PRINTF_INT32_MODIFIER + * PRINTF_INT16_MODIFIER + * PRINTF_LEAST64_MODIFIER + * PRINTF_LEAST32_MODIFIER + * PRINTF_LEAST16_MODIFIER + * PRINTF_INTPTR_MODIFIER + * + * are strings which have been defined as the modifiers required + * for the "d", "u" and "x" printf formats to correctly output + * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t, + * (u)least32_t, (u)least16_t and (u)intptr_t types respectively. + * PRINTF_INTPTR_MODIFIER is not defined for some systems which + * provide their own stdint.h. PRINTF_INT64_MODIFIER is not + * defined if INT64_MAX is not defined. These are an extension + * beyond what C99 specifies must be in stdint.h. + * + * In addition, the following macros are defined: + * + * PRINTF_INTMAX_HEX_WIDTH + * PRINTF_INT64_HEX_WIDTH + * PRINTF_INT32_HEX_WIDTH + * PRINTF_INT16_HEX_WIDTH + * PRINTF_INT8_HEX_WIDTH + * PRINTF_INTMAX_DEC_WIDTH + * PRINTF_INT64_DEC_WIDTH + * PRINTF_INT32_DEC_WIDTH + * PRINTF_INT16_DEC_WIDTH + * PRINTF_INT8_DEC_WIDTH + * + * Which specifies the maximum number of characters required to + * print the number of that type in either hexadecimal or decimal. + * These are an extension beyond what C99 specifies must be in + * stdint.h. + * + * Compilers tested (all with 0 warnings at their highest respective + * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32 + * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio + * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3 + * + * This file should be considered a work in progress. Suggestions for + * improvements, especially those which increase coverage are strongly + * encouraged. + * + * Acknowledgements + * + * The following people have made significant contributions to the + * development and testing of this file: + * + * Chris Howie + * John Steele Scott + * Dave Thorup + * John Dill + * + */ + +#ifndef LCARD_PSTDINT +#define LCARD_PSTDINT + +#include +#include + +/* + * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and + * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. + */ + +#if ((defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) \ + || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || (__WATCOMC__ >= 1250))) \ + || (defined(__GNUC__)) \ + || (defined (_MSC_VER) && (_MSC_VER >= 1600)) \ + || (defined (__BORLANDC__) && (__BORLANDC__ >= 0x560))) && !defined (_PSTDINT_H_INCLUDED) +#include +#define _PSTDINT_H_INCLUDED +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "20" +# endif +# ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif + +/* + * Something really weird is going on with Open Watcom. Just pull some of + * these duplicated definitions from Open Watcom's stdint.h file for now. + */ + +# if defined (__WATCOMC__) && __WATCOMC__ >= 1250 +# if !defined (INT64_C) +# define INT64_C(x) (x + (INT64_MAX - INT64_MAX)) +# endif +# if !defined (UINT64_C) +# define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) +# endif +# if !defined (INT32_C) +# define INT32_C(x) (x + (INT32_MAX - INT32_MAX)) +# endif +# if !defined (UINT32_C) +# define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX)) +# endif +# if !defined (INT16_C) +# define INT16_C(x) (x) +# endif +# if !defined (UINT16_C) +# define UINT16_C(x) (x) +# endif +# if !defined (INT8_C) +# define INT8_C(x) (x) +# endif +# if !defined (UINT8_C) +# define UINT8_C(x) (x) +# endif +# if !defined (UINT64_MAX) +# define UINT64_MAX 18446744073709551615ULL +# endif +# if !defined (INT64_MAX) +# define INT64_MAX 9223372036854775807LL +# endif +# if !defined (UINT32_MAX) +# define UINT32_MAX 4294967295UL +# endif +# if !defined (INT32_MAX) +# define INT32_MAX 2147483647L +# endif +# if !defined (INTMAX_MAX) +# define INTMAX_MAX INT64_MAX +# endif +# if !defined (INTMAX_MIN) +# define INTMAX_MIN INT64_MIN +# endif +# endif +#endif + +#ifndef _PSTDINT_H_INCLUDED +#define _PSTDINT_H_INCLUDED + +#ifndef SIZE_MAX +# define SIZE_MAX (~(size_t)0) +#endif + +/* + * Deduce the type assignments from limits.h under the assumption that + * integer sizes in bits are powers of 2, and follow the ANSI + * definitions. + */ + +#ifndef UINT8_MAX +# define UINT8_MAX 0xff +#endif +#ifndef uint8_t +# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S) + typedef unsigned char uint8_t; +# define UINT8_C(v) ((uint8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef INT8_MAX +# define INT8_MAX 0x7f +#endif +#ifndef INT8_MIN +# define INT8_MIN INT8_C(0x80) +#endif +#ifndef int8_t +# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S) + typedef signed char int8_t; +# define INT8_C(v) ((int8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef UINT16_MAX +# define UINT16_MAX 0xffff +#endif +#ifndef uint16_t +#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S) + typedef unsigned int uint16_t; +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +# define UINT16_C(v) ((uint16_t) (v)) +#elif (USHRT_MAX == UINT16_MAX) + typedef unsigned short uint16_t; +# define UINT16_C(v) ((uint16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT16_MAX +# define INT16_MAX 0x7fff +#endif +#ifndef INT16_MIN +# define INT16_MIN INT16_C(0x8000) +#endif +#ifndef int16_t +#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S) + typedef signed int int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +#elif (SHRT_MAX == INT16_MAX) + typedef signed short int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef UINT32_MAX +# define UINT32_MAX (0xffffffffUL) +#endif +#ifndef uint32_t +#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S) + typedef unsigned long uint32_t; +# define UINT32_C(v) v ## UL +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (UINT_MAX == UINT32_MAX) + typedef unsigned int uint32_t; +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# define UINT32_C(v) v ## U +#elif (USHRT_MAX == UINT32_MAX) + typedef unsigned short uint32_t; +# define UINT32_C(v) ((unsigned short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT32_MAX +# define INT32_MAX (0x7fffffffL) +#endif +#ifndef INT32_MIN +# define INT32_MIN INT32_C(0x80000000) +#endif +#ifndef int32_t +#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S) + typedef signed long int32_t; +# define INT32_C(v) v ## L +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (INT_MAX == INT32_MAX) + typedef signed int int32_t; +# define INT32_C(v) v +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#elif (SHRT_MAX == INT32_MAX) + typedef signed short int32_t; +# define INT32_C(v) ((short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +/* + * The macro stdint_int64_defined is temporarily used to record + * whether or not 64 integer support is available. It must be + * defined for any 64 integer extensions for new platforms that are + * added. + */ + +#undef stdint_int64_defined +#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S) +# if (__STDC__ && __STDC_VERSION__ >= 199901L) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# endif +#endif + +#if !defined (stdint_int64_defined) +# if defined(__GNUC__) +# define stdint_int64_defined + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC) +# define stdint_int64_defined + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +# define UINT64_C(v) v ## UI64 +# define INT64_C(v) v ## I64 +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "I64" +# endif +# endif +#endif + +#if !defined (LONG_LONG_MAX) && defined (INT64_C) +# define LONG_LONG_MAX INT64_C (9223372036854775807) +#endif +#ifndef ULONG_LONG_MAX +# define ULONG_LONG_MAX UINT64_C (18446744073709551615) +#endif + +#if !defined (INT64_MAX) && defined (INT64_C) +# define INT64_MAX INT64_C (9223372036854775807) +#endif +#if !defined (INT64_MIN) && defined (INT64_C) +# define INT64_MIN INT64_C (-9223372036854775808) +#endif +#if !defined (UINT64_MAX) && defined (INT64_C) +# define UINT64_MAX UINT64_C (18446744073709551615) +#endif + +/* + * Width of hexadecimal for number field. + */ + +#ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +#endif +#ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +#endif +#ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +#endif +#ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +#endif + +#ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "20" +#endif +#ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +#endif + +/* + * Ok, lets not worry about 128 bit integers for now. Moore's law says + * we don't need to worry about that until about 2040 at which point + * we'll have bigger things to worry about. + */ + +#ifdef stdint_int64_defined + typedef int64_t intmax_t; + typedef uint64_t uintmax_t; +# define INTMAX_MAX INT64_MAX +# define INTMAX_MIN INT64_MIN +# define UINTMAX_MAX UINT64_MAX +# define UINTMAX_C(v) UINT64_C(v) +# define INTMAX_C(v) INT64_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif +#else + typedef int32_t intmax_t; + typedef uint32_t uintmax_t; +# define INTMAX_MAX INT32_MAX +# define UINTMAX_MAX UINT32_MAX +# define UINTMAX_C(v) UINT32_C(v) +# define INTMAX_C(v) INT32_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH +# endif +#endif + +/* + * Because this file currently only supports platforms which have + * precise powers of 2 as bit sizes for the default integers, the + * least definitions are all trivial. Its possible that a future + * version of this file could have different definitions. + */ + +#ifndef stdint_least_defined + typedef int8_t int_least8_t; + typedef uint8_t uint_least8_t; + typedef int16_t int_least16_t; + typedef uint16_t uint_least16_t; + typedef int32_t int_least32_t; + typedef uint32_t uint_least32_t; +# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER +# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER +# define UINT_LEAST8_MAX UINT8_MAX +# define INT_LEAST8_MAX INT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define INT_LEAST16_MAX INT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# ifdef stdint_int64_defined + typedef int64_t int_least64_t; + typedef uint64_t uint_least64_t; +# define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER +# define UINT_LEAST64_MAX UINT64_MAX +# define INT_LEAST64_MAX INT64_MAX +# define INT_LEAST64_MIN INT64_MIN +# endif +#endif +#undef stdint_least_defined + +/* + * The ANSI C committee pretending to know or specify anything about + * performance is the epitome of misguided arrogance. The mandate of + * this file is to *ONLY* ever support that absolute minimum + * definition of the fast integer types, for compatibility purposes. + * No extensions, and no attempt to suggest what may or may not be a + * faster integer type will ever be made in this file. Developers are + * warned to stay away from these types when using this or any other + * stdint.h. + */ + +typedef int_least8_t int_fast8_t; +typedef uint_least8_t uint_fast8_t; +typedef int_least16_t int_fast16_t; +typedef uint_least16_t uint_fast16_t; +typedef int_least32_t int_fast32_t; +typedef uint_least32_t uint_fast32_t; +#define UINT_FAST8_MAX UINT_LEAST8_MAX +#define INT_FAST8_MAX INT_LEAST8_MAX +#define UINT_FAST16_MAX UINT_LEAST16_MAX +#define INT_FAST16_MAX INT_LEAST16_MAX +#define UINT_FAST32_MAX UINT_LEAST32_MAX +#define INT_FAST32_MAX INT_LEAST32_MAX +#define INT_FAST8_MIN INT_LEAST8_MIN +#define INT_FAST16_MIN INT_LEAST16_MIN +#define INT_FAST32_MIN INT_LEAST32_MIN +#ifdef stdint_int64_defined + typedef int_least64_t int_fast64_t; + typedef uint_least64_t uint_fast64_t; +# define UINT_FAST64_MAX UINT_LEAST64_MAX +# define INT_FAST64_MAX INT_LEAST64_MAX +# define INT_FAST64_MIN INT_LEAST64_MIN +#endif + +#undef stdint_int64_defined + +/* + * Whatever piecemeal, per compiler thing we can do about the wchar_t + * type limits. + */ + +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) +# include +# ifndef WCHAR_MIN +# define WCHAR_MIN 0 +# endif +# ifndef WCHAR_MAX +# define WCHAR_MAX ((wchar_t)-1) +# endif +#endif + +/* + * Whatever piecemeal, per compiler/platform thing we can do about the + * (u)intptr_t types and limits. + */ + +#if defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED) +# define STDINT_H_UINTPTR_T_DEFINED +#elif defined (_CVI_) +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +#ifndef STDINT_H_UINTPTR_T_DEFINED +# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) +# define stdint_intptr_bits 64 +# elif defined (__WATCOMC__) || defined (__TURBOC__) +# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) +# define stdint_intptr_bits 16 +# else +# define stdint_intptr_bits 32 +# endif +# elif defined (__i386__) || defined (_WIN32) || defined (WIN32) +# define stdint_intptr_bits 32 +# elif defined (__INTEL_COMPILER) +/* TODO -- what did Intel do about x86-64? */ +# endif + +# ifdef stdint_intptr_bits +# define stdint_intptr_glue3_i(a,b,c) a##b##c +# define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c) +# ifndef PRINTF_INTPTR_MODIFIER +# define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER) +# endif +# ifndef PTRDIFF_MAX +# define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef PTRDIFF_MIN +# define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef UINTPTR_MAX +# define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MAX +# define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MIN +# define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef INTPTR_C +# define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x) +# endif +# ifndef UINTPTR_C +# define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x) +# endif + typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t; + typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t; +# else +/* TODO -- This following is likely wrong for some platforms, and does + nothing for the definition of uintptr_t. */ + typedef ptrdiff_t intptr_t; +# endif +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +/* + * Assumes sig_atomic_t is signed and we have a 2s complement machine. + */ + +#ifndef SIG_ATOMIC_MAX +# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1) +#endif + +#endif + +#if defined (__TEST_PSTDINT_FOR_CORRECTNESS) + +/* + * Please compile with the maximum warning settings to make sure macros are not + * defined more than once. + */ + +#include +#include +#include + +#define glue3_aux(x,y,z) x ## y ## z +#define glue3(x,y,z) glue3_aux(x,y,z) + +#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,=) glue3(UINT,bits,_C) (0); +#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,=) glue3(INT,bits,_C) (0); + +#define DECL(us,bits) glue3(DECL,us,) (bits) + +#define TESTUMAX(bits) glue3(u,bits,=) glue3(~,u,bits); if (glue3(UINT,bits,_MAX) glue3(!=,u,bits)) printf ("Something wrong with UINT%d_MAX\n", bits) + +int main () { + DECL(I,8) + DECL(U,8) + DECL(I,16) + DECL(U,16) + DECL(I,32) + DECL(U,32) +#ifdef INT64_MAX + DECL(I,64) + DECL(U,64) +#endif + intmax_t imax = INTMAX_C(0); + uintmax_t umax = UINTMAX_C(0); + char str0[256], str1[256]; + + sprintf (str0, "%d %x\n", 0, ~0); + + sprintf (str1, "%d %x\n", i8, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i8 : %s\n", str1); + sprintf (str1, "%u %x\n", u8, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with u8 : %s\n", str1); + sprintf (str1, "%d %x\n", i16, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i16 : %s\n", str1); + sprintf (str1, "%u %x\n", u16, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with u16 : %s\n", str1); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i32 : %s\n", str1); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with u32 : %s\n", str1); +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i64 : %s\n", str1); +#endif + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with imax : %s\n", str1); + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with umax : %s\n", str1); + + TESTUMAX(8); + TESTUMAX(16); + TESTUMAX(32); +#ifdef INT64_MAX + TESTUMAX(64); +#endif + + return EXIT_SUCCESS; +} + +#endif + +#endif diff --git a/x502/osspec_cfg.h b/x502/osspec_cfg.h new file mode 100644 index 0000000..1c75041 --- /dev/null +++ b/x502/osspec_cfg.h @@ -0,0 +1,27 @@ +#ifndef OS_SPEC_CFG_H_ +#define OS_SPEC_CFG_H_ + +#include "x502api.h" + + +//#define OSSPEC_USE_MUTEX +//#define OSSPEC_USE_EVENTS +//#define OSSPEC_USE_THREADS + + +#define OSSPEC_ERR_MUTEX_INVALID_HANDLE X502_ERR_MUTEX_INVALID_HANDLE +#define OSSPEC_ERR_MUTEX_LOCK_TOUT X502_ERR_MUTEX_LOCK_TOUT +#define OSSPEC_ERR_MUTEX_RELEASE X502_ERR_MUTEX_RELEASE + + +#define OSSPEC_ERR_EVENT_INVALID_HANDLE OSSPEC_ERR_MUTEX_INVALID_HANDLE +#define OSSPEC_ERR_EVENT_WAIT_TOUT OSSPEC_ERR_MUTEX_LOCK_TOUT + + +#define OSSPEC_ERR_THREAD_INVALID_HANDLE OSSPEC_ERR_MUTEX_INVALID_HANDLE +#define OSSPEC_ERR_THREAD_WAIT_TOUT X502_ERR_THREAD_STOP + + + + +#endif diff --git a/x502/x502_eeprom.h b/x502/x502_eeprom.h new file mode 100644 index 0000000..c609515 --- /dev/null +++ b/x502/x502_eeprom.h @@ -0,0 +1,87 @@ +/***************************************************************************//** + @file x502_eeprom.h + @author Borisov Alexey + @date 14.03.2011 + + Файл содержет описание констант и типов, для описание формата информации, + записаной в EEPROM. Эти константы используются только внутри библиотеки и + в специальном софте (для калибровки и обновлениия прошивки ПЛИС) и не + доступны пользователю по-умолчанию + ******************************************************************************/ +#ifndef X502_EEPROM_H +#define X502_EEPROM_H + + +#define X502_EEPROM_CBR_SIGN 0x4C434352 //LCBR +#define X502_EEPROM_CBR_FROMAT 2 + +#define X502_EEPROM_CBR_SRC_ADC 1 +#define X502_EEPROM_CBR_SRC_DAC 2 + +// TODO: это может быть проблемой для поддержки совместимости: E502-P1 флеш в 2 раза больше +//#define X502_EEPROM_SIZE 0x400000UL +// Устаревшая константа с размером внешней флеш памяти в байтах, надо использовать значения переменной из hnd->info.flash_size +#define X502_EEPROM_SIZE 0x200000UL + +#define X502_EEPROM_ADDR_DESCR 0x1F0000UL +#define X502_DESCR_MAX_SIZE 0x010000UL + + + + + +#define X502_EEPROM_FORMAT 1 +#define X502_DESCR_MIN_SIZE (sizeof(t_x502_eeprom_hdr)+4) +#define X502_EEPROM_SIGN 0x4C524F4D + + +typedef struct { + uint32_t cbr_sign; + uint32_t cbr_size; + uint32_t format; + uint32_t src; + uint32_t flags; + uint32_t reserv[3]; + uint64_t time; + uint32_t channel_cnt; + uint32_t range_cnt; +} t_x502_eeprom_cbr_hdr; + +typedef struct { + t_x502_eeprom_cbr_hdr hdr; + t_x502_cbr_coef coefs[X502_ADC_RANGE_CNT]; +} t_x502_eeprom_cbr_adc; + +typedef struct { + t_x502_eeprom_cbr_hdr hdr; + t_x502_cbr_coef coefs[X502_DAC_CH_CNT]; +} t_x502_eeprom_cbr_dac; + +typedef struct { + uint32_t sign; + uint32_t size; + uint32_t format; + char name[X502_DEVNAME_SIZE]; + char serial[X502_SERIAL_SIZE]; + uint8_t factory_mac[X502_MAC_ADDR_SIZE]; + char res[64-18]; +} t_x502_eeprom_hdr; + + +typedef struct { + t_x502_eeprom_hdr hdr; + t_x502_eeprom_cbr_adc cbr_adc; + t_x502_eeprom_cbr_dac cbr_dac; + uint32_t crc; +} t_x502_descr; + + +typedef enum { + X502_EEPROM_PROT_ALL = 0, /**< Защищена вся область памяти */ + X502_EEPROM_PROT_WR_USER = 1, /**< Разрешено изменение только пользовательской части */ + X502_EEPROM_PROT_WR_SETTINGS = 2, /**< Кроме пользовательской части, разрешено изменение + области настроек, флага загрузки и второй копии прошивки ПЛИС */ + X502_EEPROM_PROT_WR_FPGA_FIRM = 3, /**< Разрешено изменение основной прошивки ПЛИС */ + X502_EEPROM_PROT_WR_ALL = 4, /**< Разрешена запись в любую область памяти */ +} t_x502_eeprom_prot_state; +#endif // X502_EEPROM_H diff --git a/x502/x502_fpga_regs.h b/x502/x502_fpga_regs.h new file mode 100644 index 0000000..7cfc34c --- /dev/null +++ b/x502/x502_fpga_regs.h @@ -0,0 +1,118 @@ +#ifndef X502_FPGA_REGS_H +#define X502_FPGA_REGS_H + +#define X502_BF_SDRAM_SIZE (32UL*1024*1024) + +#define X502_BF_MEMADDR_CMD 0xFF800800 + + +#define X502_BF_CMD_READ 0x0001 +#define X502_BF_CMD_WRITE 0x0002 +#define X502_BF_CMD_HIRQ 0x0004 +#define X502_BF_CMD_HDMA_RST 0x0008 + + + + +/********************* Адреса регистров блока IOHARD **************************/ +#define X502_REGS_IOHARD_BLOCK 0x0200 +//Адрес Control Table +#define X502_REGS_IOHARD_LTABLE (X502_REGS_IOHARD_BLOCK+0) +#define X502_REGS_IOHARD_LTABLE_MAX_SIZE 0x100 // Максимальный размер Control Table + +#define X502_REGS_IOHARD_LCH_CNT (X502_REGS_IOHARD_BLOCK+0x100) +#define X502_REGS_IOHARD_ADC_FREQ_DIV (X502_REGS_IOHARD_BLOCK+0x102) +#define X502_REGS_IOHARD_ADC_FRAME_DELAY (X502_REGS_IOHARD_BLOCK+0x104) +#define X502_REGS_IOHARD_DIGIN_FREQ_DIV (X502_REGS_IOHARD_BLOCK+0x106) +#define X502_REGS_IOHARD_IO_MODE (X502_REGS_IOHARD_BLOCK+0x108) +#define X502_REGS_IOHARD_GO_SYNC_IO (X502_REGS_IOHARD_BLOCK+0x10A) +#define X502_REGS_IOHARD_PRELOAD_ADC (X502_REGS_IOHARD_BLOCK+0x10C) +#define X502_REGS_IOHARD_ASYNC_OUT (X502_REGS_IOHARD_BLOCK+0x112) +#define X502_REGS_IOHARD_LED (X502_REGS_IOHARD_BLOCK+0x114) +#define X502_REGS_IOHARD_DIGIN_PULLUP (X502_REGS_IOHARD_BLOCK+0x116) +#define X502_REGS_IOHARD_OUTSWAP_BFCTL (X502_REGS_IOHARD_BLOCK+0x118) +#define X502_REGS_IOHARD_OUTSWAP_ERROR (X502_REGS_IOHARD_BLOCK+0x120) + + + +/********************* Адреса регистров блока IOARITH **************************/ +#define X502_REGS_IOARITH_BLOCK 0x0400 +#define X502_REGS_IOARITH_B10 X502_REGS_IOARITH_BLOCK +#define X502_REGS_IOARITH_B5 (X502_REGS_IOARITH_BLOCK+0x01) +#define X502_REGS_IOARITH_B2 (X502_REGS_IOARITH_BLOCK+0x02) +#define X502_REGS_IOARITH_B1 (X502_REGS_IOARITH_BLOCK+0x03) +#define X502_REGS_IOARITH_B05 (X502_REGS_IOARITH_BLOCK+0x04) +#define X502_REGS_IOARITH_B02 (X502_REGS_IOARITH_BLOCK+0x05) +#define X502_REGS_IOARITH_K10 (X502_REGS_IOARITH_BLOCK+0x08) +#define X502_REGS_IOARITH_K5 (X502_REGS_IOARITH_BLOCK+0x09) +#define X502_REGS_IOARITH_K2 (X502_REGS_IOARITH_BLOCK+0x0A) +#define X502_REGS_IOARITH_K1 (X502_REGS_IOARITH_BLOCK+0x0B) +#define X502_REGS_IOARITH_K05 (X502_REGS_IOARITH_BLOCK+0x0C) +#define X502_REGS_IOARITH_K02 (X502_REGS_IOARITH_BLOCK+0x0D) +#define X502_REGS_IOARITH_ADC_FREQ_DIV (X502_REGS_IOARITH_BLOCK+0x12) +#define X502_REGS_IOARITH_THRESHOLD (X502_REGS_IOARITH_BLOCK+0x15) +#define X502_REGS_IOARITH_N_CHAN_SYN (X502_REGS_IOARITH_BLOCK+0x16) +#define X502_REGS_IOARITH_IN_STREAM_ENABLE (X502_REGS_IOARITH_BLOCK+0x19) +#define X502_REGS_IOARITH_DIN_ASYNC (X502_REGS_IOARITH_BLOCK+0x1A) +#define X502_REGS_IOARITH_SYNC_TYPE (X502_REGS_IOARITH_BLOCK+0x20) + + +/********************* Адреса регистров блока управления BlackFin'ом **********/ +#define X502_REGS_BF_CTL_BLOCK 0 +#define X502_REGS_BF_CTL (X502_REGS_BF_CTL_BLOCK+0) +#define X502_REGS_BF_CMD (X502_REGS_BF_CTL_BLOCK+1) +#define X502_REGS_BF_STATUS (X502_REGS_BF_CTL_BLOCK+2) +#define X502_REGS_BF_IRQ (X502_REGS_BF_CTL_BLOCK+3) +#define X502_REGS_BF_IRQ_EN (X502_REGS_BF_CTL_BLOCK+4) +#define X502_REGS_BF_REQ_ADDR (X502_REGS_BF_CTL_BLOCK+5) +#define X502_REGS_BF_REQ_SIZE (X502_REGS_BF_CTL_BLOCK+6) +#define X502_REGS_BF_REQ_DATA (X502_REGS_BF_CTL_BLOCK+128) + +#define X502_BF_REQ_DATA_SIZE_MAX 128 +#define X502_BF_REQ_DATA_SIZE_MIN 8 + + + + +/* описание отдельных битов регистров */ + +#define X502_REGBIT_BF_STATUS_HWAIT_Pos 0 +#define X502_REGBIT_BF_STATUS_HWAIT_Msk (1UL << X502_REGBIT_BF_STATUS_HWAIT_Pos) + +#define X502_REGBIT_BF_STATUS_BUSY_Pos 1 +#define X502_REGBIT_BF_STATUS_BUSY_Msk (1UL << X502_REGBIT_BF_STATUS_BUSY_Pos) + + + +#define X502_REGBIT_BF_CTL_BF_RESET_Pos 1 +#define X502_REGBIT_BF_CTL_BF_RESET_Msk (0x1UL << X502_REGBIT_BF_CTL_BF_RESET_Pos) + + +#define X502_REGBIT_BF_CTL_HOST_WAIT_Pos 3 +#define X502_REGBIT_BF_CTL_HOST_WAIT_Msk (0x1UL << X502_REGBIT_BF_CTL_HOST_WAIT_Pos) + +#define X502_REGBIT_BF_CTL_DSP_MODE_Pos 4 +#define X502_REGBIT_BF_CTL_DSP_MODE_Msk (0x1UL << X502_REGBIT_BF_CTL_DSP_MODE_Pos) + +#define X502_REGBIT_BF_CTL_DBG_MODE_Pos 5 +#define X502_REGBIT_BF_CTL_DBG_MODE_Msk (0x1UL << X502_REGBIT_BF_CTL_DBG_MODE_Pos) + +#define X502_REGBIT_BF_CTL_CLK_DIV_Pos 8 +#define X502_REGBIT_BF_CTL_CLK_DIV_Msk (0xFUL << X502_REGBIT_BF_CTL_CLK_DIV_Pos) + +#define X502_REGBIT_ADC_SLV_CLK_LOCK_Pos 31 +#define X502_REGBIT_ADC_SLV_CLK_LOCK_Msk (0x1UL << X502_REGBIT_ADC_SLV_CLK_LOCK_Pos) + +#define X502_REGBIT_IOHARD_OUT_SWAP_Pos 0 +#define X502_REGBIT_IOHARD_OUT_SWAP_Msk (0x1UL << X502_REGBIT_IOHARD_OUT_SWAP_Pos) + +#define X502_REGBIT_IOHARD_OUT_TFS_EN_Pos 1 +#define X502_REGBIT_IOHARD_OUT_TFS_EN_Msk (0x1UL << X502_REGBIT_IOHARD_OUT_TFS_EN_Pos) + +#define X502_REGBIT_IOHARD_OUT_RING_Pos 2 +#define X502_REGBIT_IOHARD_OUT_RING_Msk (0x1UL << X502_REGBIT_IOHARD_OUT_RING_Pos) + +#define X502_REGBIT_IOHARD_OUT_RFS_EN_Pos 3 +#define X502_REGBIT_IOHARD_OUT_RFS_EN_Msk (0x1UL << X502_REGBIT_IOHARD_OUT_RFS_EN_Pos) + +#endif // E502_FPGA_REGS_H diff --git a/x502/x502api.c b/x502/x502api.c new file mode 100644 index 0000000..7b92433 --- /dev/null +++ b/x502/x502api.c @@ -0,0 +1,731 @@ +#include "x502api.h" +#include "x502api_private.h" +#include "x502_fpga_regs.h" + +#include +#include + + +static const double f_x502_scales[] = {10., 5., 2., 1., 0.5, 0.2}; +// В E16 только 4 диапазона! +static const double f_e16_scales[] = {10., 2.5, 0.625, 0.15625, 0., 0.}; + + +X502_EXPORT(double const*) E16_GetAdcRanges(uint32_t *ranges_len) { + if (ranges_len) { + *ranges_len = E16_ADC_RANGE_CNT; + } + return f_e16_scales; +} + +X502_EXPORT(double const*) E502_GetAdcRanges(uint32_t *ranges_len) { + if (ranges_len) { + *ranges_len = X502_ADC_RANGE_CNT; + } + return f_x502_scales; +} + +X502_EXPORT(double const*) X502_GetAdcRanges(t_x502_hnd hnd, uint32_t *ranges_len) { + int32_t err = X502_CHECK_HND(hnd); + if (err != X502_ERR_OK) { + if (x502_is_E16(hnd)) { + return E16_GetAdcRanges(ranges_len); + } else { + return E502_GetAdcRanges(ranges_len); + } + } + return NULL; +} + + +X502_EXPORT(t_x502_hnd) X502_Create(void) { + t_x502_hnd hnd = calloc(sizeof(t_x502), 1); + if (hnd != NULL) { + hnd->sign = X502_SIGN; + } + + return hnd; +} + +X502_EXPORT(int32_t) X502_Free(t_x502_hnd hnd) { + int32_t err = X502_CHECK_HND(hnd); + if (err == X502_ERR_OK) { + if (hnd->flags & PRIV_FLAGS_OPENED) + err = X502_Close(hnd); + + hnd->sign = 0; + free(hnd); + } + return err; +} + + + +X502_EXPORT(int32_t) X502_Close(t_x502_hnd hnd) { + int32_t err = X502_CHECK_HND(hnd); + if ((err == X502_ERR_OK) && (hnd->flags & PRIV_FLAGS_OPENED)) { + int32_t stop_err; + if (hnd->flags & PRIV_FLAGS_STREAM_RUN) { + /* остановка потока */ + err = X502_StreamsStop(hnd); + } + + hnd->flags &= ~PRIV_FLAGS_OPENED; + + + stop_err = hnd->iface_hnd->close(hnd); + if (err == X502_ERR_OK) + err = stop_err; + + if (hnd->mutex_cfg!=OSSPEC_INVALID_MUTEX) { + stop_err = osspec_mutex_destroy(hnd->mutex_cfg); + hnd->mutex_cfg = OSSPEC_INVALID_MUTEX; + if (err == X502_ERR_OK) + err = stop_err; + } + + if (hnd->mutex_bf!=OSSPEC_INVALID_MUTEX) { + stop_err = osspec_mutex_destroy(hnd->mutex_bf); + hnd->mutex_bf = OSSPEC_INVALID_MUTEX; + if (err == X502_ERR_OK) + err = stop_err; + } + } + return err; +} + +X502_EXPORT(int32_t) X502_IsOpened(t_x502_hnd hnd) { + int32_t err = X502_CHECK_HND(hnd); + if ((err == X502_ERR_OK) && !(hnd->flags & PRIV_FLAGS_OPENED)) { + err = X502_ERR_DEVICE_NOT_OPENED; + } + return err; +} + + + +X502_EXPORT(int32_t) X502_OpenByDevRecord(t_x502* hnd, const t_x502_devrec *devrec) { + int32_t err = X502_CHECK_HND(hnd); + if ((err == X502_ERR_OK) && ((devrec==NULL) || (devrec->sign!=X502_DEVREC_SIGN))) + err = X502_ERR_INVALID_DEVICE_RECORD; + if (X502_IsOpened(hnd) == X502_ERR_OK) { + X502_Close(hnd); + } + if (err == X502_ERR_OK) { + hnd->iface_hnd = (const t_x502_dev_iface *)(devrec->internal->iface); + memcpy(hnd->info.serial, devrec->serial, X502_SERIAL_SIZE); + memcpy(hnd->info.name, devrec->devname, X502_DEVNAME_SIZE); + hnd->iface = devrec->iface; + hnd->info.devflags = devrec->flags; + err = hnd->iface_hnd->open(hnd, devrec); + if (err == X502_ERR_OK) { + X502_SetLChannel(hnd, 0, 0, X502_LCH_MODE_COMM, X502_ADC_RANGE_10, 0); + hnd->set.lch_cnt = 1; + hnd->set.adc_frame_delay = 0; + hnd->set.sync_mode = X502_SYNC_INTERNAL; + hnd->set.sync_start_mode = X502_SYNC_INTERNAL; + + + hnd->set.ext_ref_freq = 0; + hnd->streams = 0; + + if (x502_is_E16(hnd)) { + hnd->set.ref_freq = E16_REF_FREQ_48000KHZ; + hnd->set.dac_range = E16_DAC_RANGE; + hnd->set.dac_code_max = E16_DAC_SCALE_CODE_MAX; + hnd->set.adc_code_max = E16_ADC_SCALE_CODE_MAX; + hnd->set.f_scales = f_e16_scales; + + hnd->set.adc_freq_div = E16_REF_FREQ_48000KHZ / E16_ADC_FREQ_DEFAULT; + hnd->set.din_freq_div = E16_REF_FREQ_48000KHZ / E16_DIN_FREQ_DEFAULT; + hnd->set.out_freq_div = E16_REF_FREQ_48000KHZ / E16_OUT_FREQ_DEFAULT; + } else { + hnd->set.ref_freq = X502_REF_FREQ_2000KHZ; + hnd->set.dac_range = X502_DAC_RANGE; + hnd->set.dac_code_max = X502_DAC_SCALE_CODE_MAX; + hnd->set.adc_code_max = X502_ADC_SCALE_CODE_MAX; + hnd->set.f_scales = f_x502_scales; + + hnd->set.adc_freq_div = 1; + hnd->set.din_freq_div = 1; + hnd->set.out_freq_div = 2; + } + + hnd->flags = PRIV_FLAGS_OPENED; + + + if (err == X502_ERR_OK) { + hnd->mutex_bf = osspec_mutex_create(); + if (hnd->mutex_bf == OSSPEC_INVALID_MUTEX) + err = X502_ERR_MUTEX_CREATE; + } else { + hnd->mutex_bf = OSSPEC_INVALID_MUTEX; + } + + if (err == X502_ERR_OK) { + hnd->mutex_cfg = osspec_mutex_create(); + if (hnd->mutex_cfg == OSSPEC_INVALID_MUTEX) + err = X502_ERR_MUTEX_CREATE; + } + + if (err == X502_ERR_OK) { + if (!(hnd->info.devflags & X502_DEVFLAGS_FPGA_LOADED)) { + err = X502_ERR_FPGA_NOT_LOADED; + } else { + + + /* определяем - в каком режиме работаем (BF или FPGA) */ + uint32_t bf_ctl; + err = hnd->iface_hnd->fpga_reg_read(hnd, X502_REGS_BF_CTL, &bf_ctl); + if (err == X502_ERR_OK) { + uint32_t mode = bf_ctl; + if (mode & X502_REGBIT_BF_CTL_DBG_MODE_Msk) { + hnd->mode = X502_MODE_DEBUG; + } else if (mode & X502_REGBIT_BF_CTL_DSP_MODE_Msk) { + hnd->mode = X502_MODE_DSP; + } else { + hnd->mode = X502_MODE_FPGA; + } + + + if (hnd->mode==X502_MODE_DSP) { + /* если blackfin находится в состоянии сброса, то возвращаемся в режим + FPGA, т.к. все команды к HostDMA все равно не выполнятся */ + if (!(bf_ctl & X502_REGBIT_BF_CTL_BF_RESET_Msk)) { + err = X502_SetMode(hnd, X502_MODE_FPGA); + } else { + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_BF_CMD, X502_BF_CMD_HDMA_RST); + /** @todo Для BlackFin проверить наличие прошивки */ + } + } + } + + /* проверка захвата PLL (сейчас невозможна в режиме DSP) */ + if ((err == X502_ERR_OK) && (hnd->mode!=X502_MODE_DSP)) { + uint32_t val; + err = hnd->iface_hnd->fpga_reg_read(hnd, X502_REGS_IOHARD_IO_MODE, &val); + if (err == X502_ERR_OK) { + if (!(val & X502_REGBIT_ADC_SLV_CLK_LOCK_Msk)) { + err = X502_ERR_REF_FREQ_NOT_LOCKED; + } + } + } + + /* читаем информацию о версии прошивки ПЛИС'ов и наличии опций */ + if (err == X502_ERR_OK) { + uint32_t hard_id=0; + int32_t id_err = hnd->iface_hnd->fpga_reg_read(hnd, hnd->iface_hnd->id_reg_addr, &hard_id); + if (id_err == X502_ERR_OK) { + hnd->info.fpga_ver = (hard_id >> 16) & 0x7FFF; + hnd->info.plda_ver = (hard_id >> 4) & 0xF; + hnd->info.board_rev = (hard_id >> 8) & 0xF; + FILL_HARD_ID_FLAGS(hnd->info.devflags, hard_id); + } + } + + /* если был запущен сбор - то останавливаем его */ + if ((err == X502_ERR_OK) && (hnd->mode==X502_MODE_FPGA)) { + hnd->last_dout = 0; + + if (hnd->iface_hnd->fpga_mode_init != NULL) + err = hnd->iface_hnd->fpga_mode_init(hnd); + + if (err == X502_ERR_OK) + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_GO_SYNC_IO, 0); + + if (err == X502_ERR_OK) + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_OUTSWAP_BFCTL, 0); + + if ((err == X502_ERR_OK) && (hnd->iface_hnd->stream_running != NULL)) { + int32_t running; + unsigned ch; + for (ch=0; (ch < X502_STREAM_CH_CNT) && (err == X502_ERR_OK); ch++) { + err = hnd->iface_hnd->stream_running(hnd, ch, &running); + if ((err == X502_ERR_OK) && running) { + err = hnd->iface_hnd->stream_stop(hnd, ch, 0); + } + } + } + } + } + } + } + /* читаем информацию из EEPROM (в первую очередь - + калибровочные коэффициенты) */ + if (err == X502_ERR_OK || err == X502_ERR_FPGA_NOT_LOADED) { + int i; + + for (i=0; i < X502_ADC_RANGE_CNT; i++) { + hnd->info.cbr.adc[i].offs = 0; + hnd->info.cbr.adc[i].k = 1.; + } + + for (i=0; i < X502_DAC_CH_CNT; i++) { + hnd->info.cbr.dac[i].offs = 0; + hnd->info.cbr.dac[i].k = 1; + } + + x502_check_eeprom(hnd, 0); + } + + /* записываем конфигурацию по умолчанию, чтобы быть уверенным, + что установлена нужная конфигурация */ + if ((err == X502_ERR_OK) && (hnd->mode==X502_MODE_FPGA)) + err = X502_Configure(hnd, 0); + //if (!err) + // err = _fpga_reg_write(hnd, L502_REGS_IOHARD_PRELOAD_ADC, 1); + + if ((err != X502_ERR_OK) && (err != X502_ERR_FPGA_NOT_LOADED) && (err != X502_ERR_REF_FREQ_NOT_LOCKED)) + X502_Close(hnd); + } + + return err; +} + + +X502_EXPORT(int32_t) X502_Open(t_x502_hnd hnd, const char* serial, + const char *devname, t_x502_get_devinfo_list_cb get_list) { + int32_t err = X502_CHECK_HND(hnd); + int32_t get_info_res = 0; + uint32_t fnd_cnt; + + if (err == X502_ERR_OK) { + get_info_res = get_list(NULL, 0, 0, &fnd_cnt); + if (get_info_res < 0) { + err = get_info_res; + } else if (!fnd_cnt) { + err = X502_ERR_DEVICE_NOT_FOUND; + } + } + + if (err == X502_ERR_OK) { + t_x502_devrec *info_list = malloc(sizeof(t_x502_devrec)*fnd_cnt); + if (info_list==NULL) { + err = X502_ERR_MEMORY_ALLOC; + } else { + /* получаем информацию по всем устройствам драйвера lpcie */ + get_info_res = get_list(info_list, fnd_cnt, 0, NULL); + if (get_info_res < 0) { + err = get_info_res; + } else { + int32_t i, ser_size=0, fnd=0, open_err = 0; + + if (serial!=NULL) { + /* если серийный задан - смотрим его размер, отсекая + * все что начинается с признака кноца строки */ + for (ser_size=0; (ser_size < X502_SERIAL_SIZE) && + !((serial[ser_size]=='\0') || + (serial[ser_size]=='\n') || + (serial[ser_size]=='\r')); ser_size++) + {} + } + + for (i=0; !fnd && (i < get_info_res); i++) { + /* ищем устройство L502 с совпадающим серийным */ + if (((devname==NULL) || !strcmp(info_list[i].devname, devname)) && + ((ser_size==0) || !strncmp(serial, info_list[i].serial, + ser_size))) { + /* пробуем открыть устройство */ + err = X502_OpenByDevRecord(hnd, &info_list[i]); + + /* если серийный номер не был указан, то сохраняем + код ошибки и идем дальше, пробовать открыть + следующее устройство */ + if (err && (ser_size==0)) { + open_err = err; + err = 0; + } else { + /* иначе заканчиваем поиск */ + fnd = 1; + } + } + } + + /* если не нашли устройство - устанавливаем соотвествующий код + ошибки */ + if (!fnd) { + err = open_err ? open_err : X502_ERR_DEVICE_NOT_FOUND; + } + } + + + X502_FreeDevRecordList(info_list, fnd_cnt); + free(info_list); + } + } + return err; +} + +X502_EXPORT(int32_t) X502_GetSerialList(char serials[][X502_SERIAL_SIZE], uint32_t size, + uint32_t flags, uint32_t *devcnt, const char *devname, + t_x502_get_devinfo_list_cb get_list) { + uint32_t fnd_cnt=0, put_cnt=0; + /* получаем количество устройств, поддерживаемых драйвером lpcie */ + int32_t res = get_list(NULL, 0, flags, &fnd_cnt); + if ((res>=0) && fnd_cnt) { + t_x502_devrec *info_list = malloc(sizeof(t_x502_devrec)*fnd_cnt); + if (info_list==NULL) { + res = X502_ERR_MEMORY_ALLOC; + } else { + /* получаем информацию по всем устройствам драйвера lpcie */ + res = get_list(info_list, fnd_cnt, flags, NULL); + if (res>0) { + int32_t i; + for (i=0; i < res; i++) { + /* проверяем, что это устройство - E502 */ + if (!strcmp(info_list[i].devname, devname)) { + /* если есть место в списке, то сохраняем серийный номер + устройства */ + if (put_cnt < size) { + memcpy(serials[put_cnt], info_list[i].serial, + X502_SERIAL_SIZE); + } + put_cnt++; + } + } + } + + X502_FreeDevRecordList(info_list, fnd_cnt); + free(info_list); + } + } + + if (devcnt != NULL) + *devcnt = put_cnt; + + return res < 0 ? res : put_cnt > size ? (int32_t)size : (int32_t)put_cnt; +} + +X502_EXPORT(int32_t) X502_FreeDevRecordList(t_x502_devrec *list, uint32_t size) { + uint32_t i; + int32_t err = X502_ERR_OK; + if (list!=NULL) { + for (i=0; (i < size) && (err==X502_ERR_OK); i++) { + if (list[i].sign!=X502_DEVREC_SIGN) { + err = X502_ERR_INVALID_DEVICE_RECORD; + } else { + t_x502_devrec_inptr *devinfo_ptr = (t_x502_devrec_inptr*)list[i].internal; + if (devinfo_ptr != NULL) { + const t_x502_dev_iface *iface = (const t_x502_dev_iface *)devinfo_ptr->iface; + if (iface!=NULL) + iface->free_devinfo_ptr(devinfo_ptr); + + } + list[i].internal = NULL; + } + } + } + return err; +} + + +X502_EXPORT(int32_t) X502_GetDevInfo(t_x502_hnd hnd, t_x502_info* info) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if ((err == X502_ERR_OK) && (info==NULL)) + err = X502_ERR_INVALID_POINTER; + + if (err == X502_ERR_OK) + *info = hnd->info; + + return err; +} + + + + +X502_EXPORT(int32_t) X502_GetNextExpectedLchNum(t_x502_hnd hnd, uint32_t *lch) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if ((err == X502_ERR_OK) && (lch==NULL)) + err = X502_ERR_INVALID_POINTER; + if (err == X502_ERR_OK) + *lch = hnd->proc_adc_ch; + return err; +} + +#define STREAM_IN_WRD_TYPE(wrd) wrd & 0x80000000 ? STREAM_IN_WRD_ADC : \ + (wrd & 0xFF000000) == 0x0 ? STREAM_IN_WRD_DIN : \ + ((wrd & 0xFF000000)>>24) == 0x01 ? STREAM_IN_WRD_MSG : STREAM_IN_WRD_USR + + +X502_EXPORT(int32_t) X502_ProcessAdcData(t_x502_hnd hnd, const uint32_t* src, double *dest, + uint32_t *size, uint32_t flags) { + if (size == NULL) + return X502_ERR_INVALID_POINTER; + return X502_ProcessDataWithUserExt(hnd, src, *size, flags, dest, size, + NULL, NULL, NULL, NULL); +} + +X502_EXPORT(int32_t) X502_ProcessData(t_x502_hnd hnd, const uint32_t *src, uint32_t size, uint32_t flags, + double *adc_data, uint32_t *adc_data_size, + uint32_t *din_data, uint32_t *din_data_size) { + return X502_ProcessDataWithUserExt(hnd, src, size, flags, adc_data, adc_data_size, + din_data, din_data_size, NULL, NULL); +} + +X502_EXPORT(int32_t) X502_ProcessDataWithUserExt(t_x502_hnd hnd, const uint32_t* src, uint32_t size, + uint32_t flags, double *adc_data, + uint32_t *adc_data_size, uint32_t *din_data, uint32_t *din_data_size, + uint32_t *usr_data, uint32_t *usr_data_size) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if ((err == X502_ERR_OK) && (adc_data_size==NULL) && (adc_data!=NULL)) + err = X502_ERR_INVALID_POINTER; + if ((err == X502_ERR_OK) && (din_data_size==NULL) && (din_data!=NULL)) + err = X502_ERR_INVALID_POINTER; + if ((err == X502_ERR_OK) && (usr_data_size==NULL) && (usr_data!=NULL)) + err = X502_ERR_INVALID_POINTER; + if (err == X502_ERR_OK) { + uint32_t adc_cnt = 0, din_cnt=0, usr_cnt = 0; + uint32_t i; + + for (i=0; (i> 24) & 0xF; + uint32_t ch_mode = (wrd >> 28) & 0x3; + int32_t val; + uint32_t range; + + /* проверяем совпадение каналов */ + switch (ch_mode) { + case 0: + ch_mode = X502_LCH_MODE_DIFF; + break; + case 1: + ch_mode = X502_LCH_MODE_COMM; + break; + case 2: + ch_mode = X502_LCH_MODE_COMM; + ch_num+=16; + break; + case 3: + ch_mode = X502_LCH_MODE_ZERO; + break; + } + + if (!(flags & X502_PROC_FLAGS_DONT_CHECK_CH) && + ((hnd->set.lch[hnd->proc_adc_ch].mode != ch_mode) || + (hnd->set.lch[hnd->proc_adc_ch].ch != ch_num))) { + err = X502_ERR_PROC_INVALID_CH_NUM; + } else { + /* проверяем формат - пришло откалиброванное 24-битное слово, + или неоткалиброванное 16-битное */ + if (wrd & 0x40000000) { + val = wrd & 0xFFFFFF; + if (wrd & 0x800000) + val |= 0xFF000000; + + range = hnd->set.lch[hnd->proc_adc_ch].range; + } else { + range = (wrd >> 16) & 0x7; + if (!(flags & X502_PROC_FLAGS_DONT_CHECK_CH) && + (range != hnd->set.lch[hnd->proc_adc_ch].range)) { + err = X502_ERR_PROC_INVALID_CH_RANGE; + } else { + val = wrd & 0xFFFF; + if (wrd & 0x8000) + val |= 0xFFFF0000; + } + } + } + + if (err == X502_ERR_OK) { + double res_val = val; + if (flags & X502_PROC_FLAGS_VOLT) { + res_val = hnd->set.f_scales[range]*res_val/hnd->set.adc_code_max; + } + + + if ((adc_data!=NULL) && (adc_cnt<*adc_data_size)) { + *adc_data++ = res_val; + adc_cnt++; + } else if (adc_data==NULL) { + adc_cnt++; + } + + if (++hnd->proc_adc_ch ==hnd->set.lch_cnt) + hnd->proc_adc_ch = 0; + } + } + break; + case STREAM_IN_WRD_DIN: + if ((din_data!=NULL) && (din_cnt<*din_data_size)) { + *din_data++ = wrd & 0x3FFFF; + din_cnt++; + } else if (din_data==NULL) { + din_cnt++; + } + break; + case STREAM_IN_WRD_MSG: + err = wrd==X502_STREAM_IN_MSG_OVERFLOW ? X502_ERR_STREAM_OVERFLOW : + X502_ERR_UNSUP_STREAM_MSG; + break; + case STREAM_IN_WRD_USR: + if ((usr_data!=NULL) && (usr_cnt<*usr_data_size)) { + *usr_data++ = wrd; + usr_cnt++; + } else if (usr_data==NULL) { + usr_cnt++; + } + break; + default: + break; + } + } + + + if (adc_data_size!=NULL) + *adc_data_size = adc_cnt; + if (din_data_size!=NULL) + *din_data_size = din_cnt; + if (usr_data_size!=NULL) + *usr_data_size = usr_cnt; + } + + return err; +} + +uint32_t prepare_dac_wrd(t_x502_hnd hnd, double val, uint32_t flags, const t_x502_cbr_coef* coef) { + uint16_t wrd = 0; + if (flags & X502_DAC_FLAGS_VOLT) { + val = (val / hnd->set.dac_range) * hnd->set.dac_code_max; + } + if (flags & X502_DAC_FLAGS_CALIBR) { + val = (val + coef->offs) * coef->k; + } + if (val > SHRT_MAX) { + wrd = SHRT_MAX; + } else + if (val < SHRT_MIN) { + wrd = SHRT_MIN; + } else { + wrd = val; + } + return wrd; +} + +X502_EXPORT(int32_t) X502_PrepareData(t_x502_hnd hnd, const double* dac1, const double* dac2, + const uint32_t* digout, uint32_t size, int32_t flags, + uint32_t* out_buf) { + int err = X502_CHECK_HND_OPENED(hnd); + if ((err == X502_ERR_OK) && (out_buf==NULL)) + err = X502_ERR_INVALID_POINTER; + if ((err == X502_ERR_OK) && ((dac1==NULL) && (dac2==NULL) && (digout==NULL))) + err = X502_ERR_INVALID_POINTER; + + + if (err == X502_ERR_OK) { + uint32_t i; + for (i = 0; (i < size) && (err == X502_ERR_OK); i++) { + if ((dac1 != NULL) && (hnd->streams & X502_STREAM_DAC1)) { + uint32_t wrd = prepare_dac_wrd(hnd, *dac1++, flags, &hnd->info.cbr.dac[0]); + *out_buf++ = wrd | X502_STREAM_OUT_WORD_TYPE_DAC1; + } + if ((dac2 != NULL) && (hnd->streams & X502_STREAM_DAC2)) { + uint32_t wrd = prepare_dac_wrd(hnd, *dac2++, flags, &hnd->info.cbr.dac[1]); + *out_buf++ = wrd | X502_STREAM_OUT_WORD_TYPE_DAC2; + } + if ((digout != NULL) && (hnd->streams & X502_STREAM_DOUT)) { + uint32_t wrd = *digout++; + *out_buf++ = (wrd &0x3FFFF) + | X502_STREAM_OUT_WORD_TYPE_DOUT; + } + } + } + return err; +} + +X502_EXPORT(int32_t) X502_FpgaRegWrite(t_x502_hnd hnd, uint32_t reg, uint32_t val) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) { + err = hnd->iface_hnd->fpga_reg_write(hnd, reg & 0xFFFF, val); + } + return err; +} + +X502_EXPORT(int32_t) X502_FpgaRegRead(t_x502_hnd hnd, uint32_t reg, uint32_t *val) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) { + err = hnd->iface_hnd->fpga_reg_read(hnd, reg, val); + } + return err; +} + + +X502_EXPORT(uint32_t) X502_GetLibraryVersion(void) { + return (X502API_VER_MAJOR << 24) | (X502API_VER_MINOR<<16) | + (X502API_VER_PATCH << 8); +} + + +X502_EXPORT(int32_t) X502_DevRecordInit(t_x502_devrec *rec) { + if (rec!=NULL) { + memset(rec, 0, sizeof(t_x502_devrec)); + rec->sign = X502_DEVREC_SIGN; + } + return X502_ERR_OK; +} + +X502_EXPORT(int32_t) X502_LedBlink(t_x502_hnd hnd) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) { + int32_t err2; + err = hnd->mode == X502_MODE_DSP ? bf_fpga_reg_wr(hnd, X502_REGS_IOHARD_LED, 0) : + X502_FpgaRegWrite(hnd, X502_REGS_IOHARD_LED, 0); + SLEEP_MS(200); + err2 = hnd->mode == X502_MODE_DSP ? bf_fpga_reg_wr(hnd, X502_REGS_IOHARD_LED, 1) : + X502_FpgaRegWrite(hnd, X502_REGS_IOHARD_LED, 1); + if (err == X502_ERR_OK) + err = err2; + } + return err; +} + +X502_EXPORT(int32_t) X502_SetDigInPullup(t_x502_hnd hnd, uint32_t pullups) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) { + uint32_t val = 0; + if (pullups & X502_PULLUPS_DI_L) + val |= 0x1; + if (pullups & X502_PULLUPS_DI_H) + val |= 0x2; + if (pullups & X502_PULLUPS_DI_SYN1) + val |= 0x4; + if (pullups & X502_PULLUPS_DI_SYN2) + val |= 0x8; + if (pullups & X502_PULLDOWN_CONV_IN) + val |= 0x10; + if (pullups & X502_PULLDOWN_START_IN) + val |= 0x20; + if (pullups & E16_MODE_RELAY_ON) + val |= 0x40; + + err = hnd->mode == X502_MODE_DSP ? bf_fpga_reg_wr(hnd, X502_REGS_IOHARD_DIGIN_PULLUP, val) : + X502_FpgaRegWrite(hnd, X502_REGS_IOHARD_DIGIN_PULLUP, val); + } + return err; +} + + +X502_EXPORT(int32_t) X502_CheckFeature(t_x502_hnd hnd, uint32_t feature) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) { + err = hnd->iface_hnd->check_feature(hnd, feature); + } + return err; +} + +#ifdef _WIN32 +BOOL WINAPI DllMain(HINSTANCE hmod, DWORD reason, LPVOID resvd) { + switch (reason) { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +#endif diff --git a/x502/x502api.h b/x502/x502api.h new file mode 100644 index 0000000..ea284fb --- /dev/null +++ b/x502/x502api.h @@ -0,0 +1,2700 @@ +#ifndef X502API_H +#define X502API_H + + + +#include "lcard_pstdint.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include "Windows.h" + + #define X502_EXPORT(type) type APIENTRY +#else + #ifndef APIENTRY + #define APIENTRY + #endif + + #if __GNUC__ >= 4 + #define X502_EXPORT(type) __attribute__ ((visibility("default"))) type + #else + #define X502_EXPORT(type) type + #endif +#endif + + +/***************************************************************************//** + @addtogroup const_list Константы и перечисления + @{ + *****************************************************************************/ + + +/** Максимальное количество логических каналов в таблице */ +#define X502_LTABLE_MAX_CH_CNT 256 + +/** Максимальное количество логических каналов в таблице для E16 */ +#define E16_LTABLE_MAX_CH_CNT 128 + +/** Количество диапазонов для измерения напряжений */ +#define X502_ADC_RANGE_CNT 6 +#define E16_ADC_RANGE_CNT 4 + +/** Количество каналов АЦП в режиме с общей землей */ +#define X502_ADC_COMM_CH_CNT 32 + +/** Количество каналов АЦП в дифференциальном режиме */ +#define X502_ADC_DIFF_CH_CNT 16 + + +/** Максимальное значение для аппаратного усреднения по логическому каналу */ +#define X502_LCH_AVG_SIZE_MAX 128 +/** Максимальное значения делителя частоты АЦП */ +#define X502_ADC_FREQ_DIV_MAX (1024*1024) +/** Максимальное значения делителя частоты АЦП + *для модуля E16 по отношению к опорной частоте E16_REF_FREQ_48000KHZ */ +#define E16_ADC_FREQ_DIV_MAX (0x800000) +/** Максимальное значение делителя частоты синхронного цифрового ввода */ +#define X502_DIN_FREQ_DIV_MAX (1024*1024) +/** Максимальное значение делителя частоты синхронного цифрового ввода + *для модуля E16 по отношению к опорной частоте E16_REF_FREQ_48000KHZ */ +#define E16_DIN_FREQ_DIV_MAX (0x800000) + +/** Значения частоты АЦП по умолчанию, + * используется в функции X502_SetAdcFreq и X502_CalcAdcFreq2 если параметр f_acq = 0 */ +#define E16_ADC_FREQ_DEFAULT 500000 +/** Значения частоты цифрового входа DIN по умолчанию, + * используется в функции X502_SetDinFreq и X502_CalcDinFreq2 если параметр f_din = 0 */ +#define E16_DIN_FREQ_DEFAULT 500000 +/** Значения частоты ЦАП и цифровых выходов по умолчанию, + * используется в функции X502_SetOutFreq и X502_CalcOutFreq2 если параметр f_dout = 0 */ +#define E16_OUT_FREQ_DEFAULT 500000 + + +/** Минимальное значение делителя частоты синхронного вывода */ +#define X502_OUT_FREQ_DIV_MIN 2 +/** Максимальное значение делителя частоты синхронного вывода */ +#define X502_OUT_FREQ_DIV_MAX 1024 +/** Максимальное значение делителя частоты синхронного вывода + *для модуля E16 по отношению к опорной частоте E16_REF_FREQ_48000KHZ */ +#define E16_OUT_FREQ_DIV_MAX 0x8000 +/** Значение делителя частоты вывода по умолчанию (которое также всегда + используется в L-502 с версией прошивки ПЛИС ниже 0.5) */ +#define X502_OUT_FREQ_DIV_DEFAULT 2 + +/** Минимальное значение делителя частоты синхронного вывода + при внешней опорной частоте ниже #X502_OUT_FREQ_REF_LF_VAL */ +#define X502_OUT_FREQ_DIV_MIN_REF_LF 1 +/** Значение внешней опорной частоты, ниже которой разрешено устанавливать + значение делителя частоты генерации до #X502_OUT_FREQ_DIV_MIN_REF_LF */ +#define X502_OUT_FREQ_REF_LF_VAL 700000 + + +/** Максимальное значение межкадровой задержки для АЦП */ +#define X502_ADC_INTERFRAME_DELAY_MAX (0x1FFFFF) + +/** Таймаут по умолчанию для выполнения команды к BlackFin*/ +#define X502_BF_CMD_DEFAULT_TOUT 500 + +/** Код АЦП, соответствующий максимальному значению шкалы */ +#define X502_ADC_SCALE_CODE_MAX 6000000 +/** Код АЦП, соответствующий максимальному значению шкалы */ +#define E16_ADC_SCALE_CODE_MAX (0x80 * 0x7fff) +/** Код ЦАП, соответствующий максимальному значению шкалы */ +#define X502_DAC_SCALE_CODE_MAX 30000 +#define E16_DAC_SCALE_CODE_MAX 0x7fff + +/** Максимальное количество символов в строке с названием устройства */ +#define X502_DEVNAME_SIZE 32 +/** Максимальное количество символов в строке с серийным номером */ +#define X502_SERIAL_SIZE 32 +/** Максимальное количество символов в строке с идентификатором board implementation */ +#define X502_IMPLEMENTATION_SIZE 16 +/** Максимальное количество символов в строке с описанием подключения */ +#define X502_LOCATION_STR_SIZE 64 +/** Размер MAC-адреса для Ethernet интерфейса */ +#define X502_MAC_ADDR_SIZE 6 +/** Размер строки с описанием экземпляра устройства */ +#define X502_INSTANCE_NAME_SIZE 64 +/** Максимальный размер строки с паролем на настройки */ +#define X502_PASSWORD_SIZE 32 + +/** Максимально возможное значение внешней опорной частоты */ +#define X502_EXT_REF_FREQ_MAX 1500000 + +/** Максимально возможное значение внешней опорной частоты для E502-P1 */ +#define E502_P1_EXT_REF_FREQ_MAX 2000000 + + +/** Размер пользовательской области Flash-памяти */ +#define X502_FLASH_USER_SIZE 0x100000 + +/** Стандартный таймаут на выполнение запроса к BlackFin в мс */ +#define X502_BF_REQ_TOUT 500 + + +/** Диапазон ЦАП в вольтах */ +#define X502_DAC_RANGE 5. +#define E16_DAC_RANGE 10. + +/** Количество каналов ЦАП */ +#define X502_DAC_CH_CNT 2 + +/** Количество цифровых выходов у модуля */ +#define X502_DOUT_LINES_CNT 16 + + +/** слово в потоке, означающее, что произошло переполнение */ +#define X502_STREAM_IN_MSG_OVERFLOW 0x01010000 + + +/** Значение поля сигнатуры в записи о устройстве #t_x502_devrec. Признак, + что запись действительна (устанавливается функциями по получению записей + о устройствах) */ +#define X502_DEVREC_SIGN 0x4C524543 + + +/** Коды ошибок библиотеки */ +typedef enum { + /** Функция выполнена без ошибок */ + X502_ERR_OK = 0, + /** В функцию передан недействительный описатель модуля */ + X502_ERR_INVALID_HANDLE = -1, + /** Ошибка выделения памяти */ + X502_ERR_MEMORY_ALLOC = -2, + /** Попытка открыть уже открытое устройство */ + X502_ERR_ALREADY_OPENED = -3, + /** Устройство с заданными параметрами не найдено в системе */ + X502_ERR_DEVICE_NOT_FOUND = -4, + /** Доступ к устройству запрещен (Как правило из-за того, что устройство + уже открыто в другой программе) */ + X502_ERR_DEVICE_ACCESS_DENIED = -5, + /** Ошибка открытия устройства */ + X502_ERR_DEVICE_OPEN = -6, + /** В функцию передан недействительный указатель */ + X502_ERR_INVALID_POINTER = -7, + /** Функция не может быть выполнена при запущенном потоке сбора данных */ + X502_ERR_STREAM_IS_RUNNING = -8, + /** Ошибка чтения данных синхронного ввода */ + X502_ERR_RECV = -9, + /** Ошибка записи данных для синхронного вывода */ + X502_ERR_SEND = -10, + /** Произошло переполнение внутреннего буфера для потока синхронного ввода */ + X502_ERR_STREAM_OVERFLOW = -11, + /** Неизвестное сообщение в потоке синхронного ввода */ + X502_ERR_UNSUP_STREAM_MSG = -12, + /** Ошибка создания системного мьютекса */ + X502_ERR_MUTEX_CREATE = -13, + /** Неверный описатель мьютекса */ + X502_ERR_MUTEX_INVALID_HANDLE = -14, + /** Истекло время ожидания освобождения мьютекса */ + X502_ERR_MUTEX_LOCK_TOUT = -15, + /** Ошибка освобождения мьютекса */ + X502_ERR_MUTEX_RELEASE = -16, + /** Недостаточно системных ресурсов */ + X502_ERR_INSUFFICIENT_SYSTEM_RESOURCES= -17, + /** Данная возможность еще не реализована */ + X502_ERR_NOT_IMPLEMENTED = -18, + /** Недостаточный размер массива */ + X502_ERR_INSUFFICIENT_ARRAY_SIZE = -19, + /** Ошибка чтения регистра FPGA */ + X502_ERR_FPGA_REG_READ = -20, + /** Ошибка записи регистра FPGA */ + X502_ERR_FPGA_REG_WRITE = -21, + /** Сбор данных уже остановлен */ + X502_ERR_STREAM_IS_NOT_RUNNING = -22, + /** Ошибка освобождения интерфейса */ + X502_ERR_INTERFACE_RELEASE = -23, + /** Ошибка запуска потока */ + X502_ERR_THREAD_START = -24, + /** Ошибка останова потока */ + X502_ERR_THREAD_STOP = -25, + /** Устройство было отключено */ + X502_ERR_DEVICE_DISCONNECTED = -26, + /** Неверный размер ответа на управляющий запрос */ + X502_ERR_IOCTL_INVALID_RESP_SIZE = -27, + /** Неверный тип устройства */ + X502_ERR_INVALID_DEVICE = -28, + /** Недействительная запись о устройстве */ + X502_ERR_INVALID_DEVICE_RECORD = -29, + /** Неверный описатель конфигурации модуля */ + X502_ERR_INVALID_CONFIG_HANDLE = -30, + /** Связь с устройством закрыта или не была установлена */ + X502_ERR_DEVICE_NOT_OPENED = -31, + /** Данная операция не доступна для текущего интерфейса связи с устройством */ + X502_ERR_INVALID_OP_FOR_IFACE = -32, + /** Не загружен ПЛИС модуля */ + X502_ERR_FPGA_NOT_LOADED = -33, + /** Неверная конфигурация USB-устройства */ + X502_ERR_INVALID_USB_CONFIGURATION = -34, + /** Неверный описатель контекста поиска устройств в сети */ + X502_ERR_INVALID_SVC_BROWSE_HANDLE = -35, + /** Неверный описатель записи о сервисе */ + X502_ERR_INVALID_SVC_RECORD_HANDLE = -36, + /** Не запущена программа обнаружения устройств в локальной сети */ + X502_ERR_DNSSD_NOT_RUNNING = -37, + /** Ошибка при обращении к программе обнаружения устройств в локальной сети */ + X502_ERR_DNSSD_COMMUNICATION = -38, + /** Превышен таймаут запроса параметров автообнаруженного сетевого устройства */ + X502_ERR_SVC_RESOLVE_TIMEOUT = -39, + /** Ошибка в кодировке имени экземпляра устройства */ + X502_ERR_INSTANCE_NAME_ENCODING = -40, + /** Экземпляры модулей не совпадают */ + X502_ERR_INSTANCE_MISMATCH = -41, + /** Возможность не поддерживается текущей версией прошивки устройства */ + X502_ERR_NOT_SUP_BY_FIRMWARE = -42, + /** Возможность не поддерживается текущей версией драйвера устройства */ + X502_ERR_NOT_SUP_BY_DRIVER = -43, + /** Превышено время ожидания установления циклического сигнала на вывод */ + X502_ERR_OUT_CYCLE_SETUP_TOUT = -44, + /** Неизвестный код поддерживаемой возможности */ + X502_ERR_UNKNOWN_FEATURE_CODE = -45, + + + /** Задан неверный размер логической таблицы */ + X502_ERR_INVALID_LTABLE_SIZE = -102, + /** Задан неверный номер логического канала */ + X502_ERR_INVALID_LCH_NUMBER = -103, + /** Неверно задано значение диапазона АЦП */ + X502_ERR_INVALID_LCH_RANGE = -104, + /** Неверно задан режим измерения для логического канала */ + X502_ERR_INVALID_LCH_MODE = -105, + /** Неверно задан номер физического канала при настройке логического */ + X502_ERR_INVALID_LCH_PHY_NUMBER = -106, + /** Неверно задан размер усреднения для логического канала */ + X502_ERR_INVALID_LCH_AVG_SIZE = -107, + /** Неверно задан делитель частоты сбора данных АЦП */ + X502_ERR_INVALID_ADC_FREQ_DIV = -108, + /** Неверно задан делитель частоты синхронного ввода цифровых линий */ + X502_ERR_INVALID_DIN_FREQ_DIV = -109, + /** Неверно задан режим работы модуля */ + X502_ERR_INVALID_MODE = -110, + /** Неверный номер канала ЦАП */ + X502_ERR_INVALID_DAC_CHANNEL = -111, + /** Неверный код выбора опорной частоты синхронизации */ + X502_ERR_INVALID_REF_FREQ = -112, + /** Неверно задано значение межкадровой задержки */ + X502_ERR_INVALID_INTERFRAME_DELAY = -113, + /** Неверно задан режим синхронизации */ + X502_ERR_INVALID_SYNC_MODE = -114, + /** Неверно задан номер потока данных */ + X502_ERR_INVALID_STREAM_CH = -115, + /** Неверно задан делитель частоты синхронного вывода */ + X502_ERR_INVALID_OUT_FREQ_DIV = -116, + + /** Ошибка захвата опорной частоты синхронизации */ + X502_ERR_REF_FREQ_NOT_LOCKED = -131, + /** Управляющий запрос к драйверу завершен с ошибкой */ + X502_ERR_IOCTL_FAILD = -132, + /** Истек таймаут ожидания завершения выполнения управляющего запроса к драйверу */ + X502_ERR_IOCTL_TIMEOUT = -133, + /** Ошибка получения информации о устройстве от драйвера */ + X502_ERR_GET_INFO = -134, + /** За время ожидания не было считано новое слово с цифровых линий */ + X502_ERR_DIG_IN_NOT_RDY = -135, + /** Принято недостаточно слов от модуля */ + X502_ERR_RECV_INSUFFICIENT_WORDS = -136, + /** Попытка выполнить операцию, требующую наличие ЦАП, при его отсутствии */ + X502_ERR_DAC_NOT_PRESENT = -137, + /** Передано недостаточно слов в модуль */ + X502_ERR_SEND_INSUFFICIENT_WORDS = -138, + /** Не пришло ответа на переданную команду */ + X502_ERR_NO_CMD_RESPONSE = -139, + + /** Неверный номер канала в обрабатываемом потоке синхронного ввода */ + X502_ERR_PROC_INVALID_CH_NUM = -140, + /** Неверный код диапазона в обрабатываемом потоке синхронного ввода */ + X502_ERR_PROC_INVALID_CH_RANGE = -141, + /** Задан неверный адрес во Flash-памяти */ + X502_ERR_FLASH_INVALID_ADDR = -142, + /** Задан неверный размер блока данных при работе с Flash-памятью */ + X502_ERR_FLASH_INVALID_SIZE = -143, + /** Истек таймаут ожидания завершения записи во Flash-память */ + X502_ERR_FLASH_WRITE_TOUT = -144, + /** Истек таймаут ожидания завершения стирания блока Flash-памяти */ + X502_ERR_FLASH_ERASE_TOUT = -145, + /** Заданная область для стирания Flash-памяти нарушает границу блока в 4 Кбайт */ + X502_ERR_FLASH_SECTOR_BOUNDARY = -146, + + /** Не удалось открыть сокет для соединения */ + X502_ERR_SOCKET_OPEN = -147, + /** Превышено время подключения */ + X502_ERR_CONNECTION_TOUT = -148, + /** Соединение закрыто другой устройством */ + X502_ERR_CONNECTION_CLOSED_BY_DEV = -149, + /** Не удалось установить заданный размер буфера сокета */ + X502_ERR_SOCKET_SET_BUF_SIZE = -150, + /** Соединение для передачи данных не установлено */ + X502_ERR_NO_DATA_CONNECTION = -151, + /** Не удалось дождаться сообщения о завершении потока */ + X502_ERR_NO_STREAM_END_MSG = -152, + /** Соединение было сброшено другой стороной */ + X502_ERR_CONNECTION_RESET = -153, + /** Не удалось найти хост с указанным адресом */ + X502_ERR_HOST_UNREACHABLE = -154, + /** Ошибка установления TCP-соединения */ + X502_ERR_TCP_CONNECTION_ERROR = -155, + + + + + /** Не удалось открыть файл прошивки BlackFin */ + X502_ERR_LDR_FILE_OPEN = -180, + /** Ошибка чтения из фала прошивки BlackFin */ + X502_ERR_LDR_FILE_READ = -181, + /** Неверный формат файла прошивки BlackFin */ + X502_ERR_LDR_FILE_FORMAT = -182, + /** Используются возможность LDR-файла, недоступные при записи прошивки + BlackFin по HDMA */ + X502_ERR_LDR_FILE_UNSUP_FEATURE = -183, + /** Неверный стартовый адрес программы в прошивке BlackFin */ + X502_ERR_LDR_FILE_UNSUP_STARTUP_ADDR = -184, + /** Истек таймаут выполнения запроса на чтения/запись памяти BlackFin */ + X502_ERR_BF_REQ_TIMEOUT = -185, + /** Команда для BlackFin все еще находится в процессе обработки */ + X502_ERR_BF_CMD_IN_PROGRESS = -186, + /** Истекло время выполнения управляющей команды процессором BlackFin */ + X502_ERR_BF_CMD_TIMEOUT = -187, + /** Возвращено недостаточно данных в ответ на команду к BlackFin */ + X502_ERR_BF_CMD_RETURN_INSUF_DATA = -188, + /** Истек таймаут ожидания готовности процессора BlackFin к записи прошивки */ + X502_ERR_BF_LOAD_RDY_TOUT = -189, + /** Попытка выполнить операцию для которой нужен сигнальный процессор при + отсутствии сигнального процессора в модуле */ + X502_ERR_BF_NOT_PRESENT = -190, + /** Неверный адрес памяти BlackFin при записи или чтении по HDMA */ + X502_ERR_BF_INVALID_ADDR = -191, + /** Неверный размер данных, передаваемых с управляющей командой в BlackFin */ + X502_ERR_BF_INVALID_CMD_DATA_SIZE = -192 +} t_x502_errs; + + +/** Интерфейс соединения с модулем */ +typedef enum { + X502_IFACE_UNKNOWN = 0, /**< Неизвестный интерфейс */ + X502_IFACE_USB = 1, /**< Устройство подключено по USB */ + X502_IFACE_ETH = 2, /**< Устройство подключено по Ethernet через TCP/IP */ + X502_IFACE_PCI = 3 /**< Устройство подключено по PCI/PCIe */ +} t_x502_iface; + +/** Флаги, управляющие поиском присутствующих модулей */ +typedef enum { + /** Признак, что нужно вернуть серийные номера только тех устройств, + которые еще не открыты */ + X502_GETDEVS_FLAGS_ONLY_NOT_OPENED = 1 +} t_x502_getdevs_flags; + + + +/** @brief Флаги для управления цифровыми выходами + + Флаги управления цифровыми выходами. Могут быть объединены через логическое + “ИЛИ” со значениями цифровых выходов при асинхронном выводе с помощью + X502_AsyncOutDig() или переданы в X502_PrepareData() при синхронном выводе.*/ +typedef enum { + X502_DIGOUT_WORD_DIS_H = 0x00020000, /**< Запрещение (перевод в третье состояние) + старшей половины цифровых выходов */ + X502_DIGOUT_WORD_DIS_L = 0x00010000, /**< Запрещение младшей половины + цифровых выходов */ + E16_DIGOUT_WORD_DIS = 0x00030000 /**< Для модуля E16 поддерживается только полное + запрещение цифровых выходов */ +} t_x502_digout_word_flags; + +/** Константы для выбора опорной частоты */ +typedef enum { + E16_REF_FREQ_48000KHZ = 48000000, /**< Частота 48МГц для E16 */ + X502_REF_FREQ_2000KHZ = 2000000, /**< Частота 2МГц */ + X502_REF_FREQ_1500KHZ = 1500000 /**< Частота 1.5МГц */ +} t_x502_ref_freq; + + +/** поля io_mode в регистре io_hard */ +typedef enum { + X502_MODE_REF_FREQ_2000 = 0, /**< Частота 2МГц */ + X502_MODE_REF_FREQ_1500 = 2, /**< Частота 1.5МГц */ + E16_MODE_REF_FREQ_48000 = 3 /**< Частота 48МГц для E16*/ +} t_x502_io_mode; + +/** Диапазоны измерения для канала АЦП */ +typedef enum { + X502_ADC_RANGE_10 = 0, /**< Диапазон +/-10V */ + X502_ADC_RANGE_5 = 1, /**< Диапазон +/-5V */ + X502_ADC_RANGE_2 = 2, /**< Диапазон +/-2V */ + X502_ADC_RANGE_1 = 3, /**< Диапазон +/-1V */ + X502_ADC_RANGE_05 = 4, /**< Диапазон +/-0.5V */ + X502_ADC_RANGE_02 = 5 /**< Диапазон +/-0.2V */ +} t_x502_adc_range; + +/** Диапазоны измерения для канала АЦП E16 */ +typedef enum { + E16_ADC_RANGE_10000 = 0, /**< Диапазон +/-10V */ + E16_ADC_RANGE_2500 = 1, /**< Диапазон +/-2.5V */ + E16_ADC_RANGE_625 = 2, /**< Диапазон +/-0.625V */ + E16_ADC_RANGE_156 = 3, /**< Диапазон +/-0.156V */ +} t_e16_adc_range; + +/** Режим измерения для логического канала */ +typedef enum { + X502_LCH_MODE_COMM = 0, /**< Измерение напряжения относительно общей земли */ + X502_LCH_MODE_DIFF = 1, /**< Дифференциальное измерение напряжения */ + X502_LCH_MODE_ZERO = 2 /**< Измерение собственного нуля */ +} t_x502_lch_mode; + +/** @brief Режимы синхронизации + + Режимы задания источника частоты синхронизации и признака начала + синхронного ввода-вывода */ +typedef enum { + X502_SYNC_INTERNAL = 0, /**< Внутренний сигнал */ + X502_SYNC_EXTERNAL_MASTER = 1, /**< От внешнего мастера по разъему межмодульной синхронизации */ + X502_SYNC_DI_SYN1_RISE = 2, /**< По фронту сигнала DI_SYN1 */ + X502_SYNC_DI_SYN1_FALL = 3, /**< По спаду сигнала DI_SYN1 */ + X502_SYNC_DI_SYN2_RISE = 6, /**< По фронту сигнала DI_SYN2 */ + X502_SYNC_DI_SYN2_FALL = 7, /**< По спаду сигнала DI_SYN2 */ + /** Только для E16! + Важно правильно установить направление TRIG или INT на вход + снятием флага E16_MODE_TRIG_OUTPUT или E16_MODE_INT_OUTPUT + */ + E16_SYNC_TRIG_RISE = 2, /**< По фронту сигнала TRIG */ + E16_SYNC_TRIG_FALL = 3, /**< По спаду сигнала TRIG */ + E16_SYNC_INT_RISE = 6, /**< По фронту сигнала INT */ + E16_SYNC_INT_FALL = 7, /**< По спаду сигнала INT */ + /** Аналоговая синхронизация старта, только для E16*/ + E16_SYNC_ADC_ABOVE_LEVEL = 8, /**< Выше уровня */ + E16_SYNC_ADC_BELOW_LEVEL = 9, /**< Ниже уровня */ + E16_SYNC_ADC_EDGE_RISE = 10, /**< По переходу сигнала снизу вверх */ + E16_SYNC_ADC_EDGE_FALL = 11 /**< По переходу сигнала сверху вниз */ +} t_x502_sync_mode; + +/** Флаги, управляющие обработкой принятых данных */ +typedef enum { + /** Признак, что нужно преобразовать значения АЦП в вольты */ + X502_PROC_FLAGS_VOLT = 0x00000001, + /** Признак, что не нужно проверять совпадение номеров каналов + в принятых данных с каналами из логической таблицы. + Может использоваться при нестандартной прошивке BlackFin + при передаче в ПК не всех данных. */ + X502_PROC_FLAGS_DONT_CHECK_CH = 0x00010000 +} t_x502_proc_flags; + + +/** Флаги для обозначения синхронных потоков данных */ +typedef enum { + X502_STREAM_ADC = 0x01, /**< Поток данных от АЦП */ + X502_STREAM_DIN = 0x02, /**< Поток данных с цифровых входов */ + X502_STREAM_DAC1 = 0x10, /**< Поток данных первого канала ЦАП */ + X502_STREAM_DAC2 = 0x20, /**< Поток данных второго канала ЦАП */ + X502_STREAM_DOUT = 0x40, /**< Поток данных на цифровые выводы */ + /** Объединение всех флагов, обозначающих потоки данных на ввод */ + X502_STREAM_ALL_IN = X502_STREAM_ADC | X502_STREAM_DIN, + /** Объединение всех флагов, обозначающих потоки данных на вывод */ + X502_STREAM_ALL_OUT = X502_STREAM_DAC1 | X502_STREAM_DAC2 | X502_STREAM_DOUT +} t_x502_streams; + +/** Константы, определяющие тип передаваемого отсчета из ПК в модуль */ +typedef enum { + X502_STREAM_OUT_WORD_TYPE_DOUT = 0x0, /**< Цифровой вывод */ + X502_STREAM_OUT_WORD_TYPE_DAC1 = 0x40000000, /**< Код для 1-го канала ЦАП */ + X502_STREAM_OUT_WORD_TYPE_DAC2 = 0x80000000 /**< Код для 2-го канала ЦАП */ +} t_x502_stream_out_wrd_type; + +/** Режим работы модуля */ +typedef enum { + X502_MODE_FPGA = 0, /**< Все потоки данных передаются через ПЛИС минуя + сигнальный процессор BlackFin */ + X502_MODE_DSP = 1, /**< Все потоки данных передаются через сигнальный + процессор, который должен быть загружен + прошивкой для обработки этих потоков */ + X502_MODE_DEBUG = 2 /**< Отладочный режим */ +} t_x502_mode; + +/** @brief Номера каналов ЦАП + + Номер каналов ЦАП для указания в X502_AsyncOutDac() */ +typedef enum { + X502_DAC_CH1 = 0, /**< Первый канал ЦАП */ + X502_DAC_CH2 = 1 /**< Второй канал ЦАП */ +} t_x502_dac_ch; + +/** @brief Флаги, используемые при выводе данных на ЦАП + + Флаги, комбинацию которых можно передать в X502_AsyncOutDac() или + X502_PrepareData(), чтобы определить действия, которые должны выполнить + эти функции с переданным значением перед выводом их на ЦАП */ +typedef enum { + /** Указывает, что значение задано в Вольтах и при выводе его нужно + перевести его в коды ЦАП. Если флаг не указан, то считается, что значение + изначально в кодах */ + X502_DAC_FLAGS_VOLT = 0x0001, + /** Указывает, что нужно применить калибровочные коэффициенты перед + выводом значения на ЦАП. */ + X502_DAC_FLAGS_CALIBR = 0x0002 +} t_x502_dacout_flags; + + + +/** Номера каналов для передачи потоков данных */ +typedef enum { + X502_STREAM_CH_IN = 0, /**< Общий канал на ввод */ + X502_STREAM_CH_OUT = 1 /**< Общий канал на вывод */ +} t_x502_stream_ch; + + +/** @brief Цифровые линии, на которых можно включить подтягивающие резисторы + + Флаги, определяющие на каких цифровых входах должны быть включены + подтягивающие резисторы. Для разных модулей доступны разные наборы флагов. */ +typedef enum { + X502_PULLUPS_DI_H = 0x01, /**< Старшая половина цифровых входов (только для L502) */ + X502_PULLUPS_DI_L = 0x02, /**< Младшая половина цифровых входов (только для L502) */ + X502_PULLUPS_DI_SYN1 = 0x04, /**< Линия SYN1 */ + X502_PULLUPS_DI_SYN2 = 0x08, /**< Линия SYN2 */ + X502_PULLDOWN_CONV_IN = 0x10, /**< Подтяжка к 0 линии межмодульной + синхронизации CONV_IN (только для E502) */ + X502_PULLDOWN_START_IN = 0x20, /**< Подтяжка к 0 линии межмодульной + синхронизации START_IN (только для E502) */ + + E16_MODE_TRIG_OUTPUT = 0x04, /**< E16: Переключение TRIG на выход, иначе вход*/ + E16_MODE_INT_OUTPUT = 0x08, /**< E16: Переключение INT на выход, иначе вход*/ + E16_MODE_TRIG_START_OUTPUT = 0x10, /**< E16: Сигнал START на выходе TRIG, иначе CONV (TRIG должен быть включен на выход) */ + E16_MODE_INT_START_OUTPUT = 0x20, /**< E16: Сигнал START на выходе INT, иначе CONV (INT должен быть включен на выход) */ + E16_MODE_RELAY_ON = 0x40, /**< E16: Включить реле */ +} t_x502_pullups; + + +/** Флаги, определяющие наличие опций в модуле и наличие необходимых параметров */ +typedef enum { + /** Признак наличия двухканального канального ЦАП */ + X502_DEVFLAGS_DAC_PRESENT = 0x00000001, + /** Признак наличия гальваноразвязки */ + X502_DEVFLAGS_GAL_PRESENT = 0x00000002, + /** Признак наличия сигнального процессора BlackFin */ + X502_DEVFLAGS_BF_PRESENT = 0x00000004, + /** 3 бита, отвечающие за тип использумого процессора/контроллера (0 --- ADSP-BF523) */ + X502_DEVFLAGS_PROC_TYPE = 0x00000038, + /** 2 бита, отвечающие за тип используемого ЦАП (0 --- AD5542AARUZ, 1 --- DAC8581IPW) */ + X502_DEVFLAGS_DAC_TYPE = 0x000000C0, + + /** Признак, что устройство поддерживает интерфейс USB */ + X502_DEVFLAGS_IFACE_SUPPORT_USB = 0x00000100, + /** Признак, что устройство поддерживает Ethernet */ + X502_DEVFLAGS_IFACE_SUPPORT_ETH = 0x00000200, + /** Признак, что устройство поддерживает интерфейс PCI/PCI-Express */ + X502_DEVFLAGS_IFACE_SUPPORT_PCI = 0x00000400, + + /** Признак, что устройство выполнено в индустриалном исполнении */ + X502_DEVFLAGS_INDUSTRIAL = 0x00008000, + + /** Признак, что во Flash-памяти присутствует информация о модуле */ + X502_DEVFLAGS_FLASH_DATA_VALID = 0x00010000, + /** Признак, что во Flash-памяти присутствуют действительные калибровочные + коэффициенты АЦП */ + X502_DEVFLAGS_FLASH_ADC_CALIBR_VALID = 0x00020000, + /** Признак, что во Flash-памяти присутствуют действительные калибровочные + коэффициенты ЦАП */ + X502_DEVFLAGS_FLASH_DAC_CALIBR_VALID = 0x00040000, + + /** Признак, что присутствует прошивка ПЛИС и она успешно была загружена */ + X502_DEVFLAGS_FPGA_LOADED = 0x00800000, + /** Признак, что устройство уже открыто (действителен только внутри t_x502_devrec) */ + X502_DEVFLAGS_DEVREC_OPENED = 0x01000000, + /** Признак, что присутствует прошивка ПЛИС и она успешно была загружена ожидаем GD32 для того чтобы остальные регистры работали */ + X502_DEVFLAGS_FPGA_AWAITING_GD32 = 0x02000000, + /** Признак, что присутствует GD32 и это E502-P1 */ + X502_DEVFLAGS_GD_PRESENT = 0x02000000 +} t_x502_dev_flags; + +/** @brief Тип содержимого строки с расположением устройства + + Данное поле определяет содержимое поля location в структуре #t_x502_devrec */ +typedef enum { + /** В поле расположения устройства не содержится информации */ + X502_LOCATION_TYPE_NONE = 0, + /** В поле расположения устройства содержится строка с адресом устройства */ + X502_LOCATION_TYPE_ADDR = 1, + /** В поле расположения устройства содержится строка с именем экземпляра */ + X502_LOCATION_TYPE_INSTANCE_NAME = 2 +} t_x502_location_type; + + +/** @brief Флаги для режима циклического вывода + + Данные флаги могут быть переданы в X502_OutCycleSetup() и X502_OutCycleStop() */ +typedef enum { + /** Флаг указывает, что останов или смена сигнала могут произойти без ожидания + конца цикла предыдущего сигнала. Это позволяет выполнить переключение + быстрее (однако все равно может быть поставлено на передачу до 256 КСемплов, + которые должны будут быть переданы), но точка смены или останова + может быть в любом месте периода */ + X502_OUT_CYCLE_FLAGS_FORCE = 0x01, + /** Флаг указывает, что функция должна дождаться полной загрузки сигнала + и установки сигнала на вывод (для X502_OutCycleSetup()) или завершения + генерации циклического сигнала (для X502_OutCycleStop()). Без него + функции только посылают команду модулю, возвращая сразу управление. + + Данное ожидание может занимать значительное время в зависимости от размера сигнала + (а также размера предыдущего сигнала в случае смены или останова генерации без + #X502_OUT_CYCLE_FLAGS_FORCE). Данную проверку можно сделать и + отдельной функций X502_OutCycleCheckSetupDone(). + Данный флаг имеет значения только в тех случаях, когда поддерживается + функция X502_OutCycleCheckSetupDone(), в противном случае он игнорируется. */ + X502_OUT_CYCLE_FLAGS_WAIT_DONE = 0x02 +} t_x502_out_cycle_flags; + + + +/** @brief Дополнительные возможности модуля + + Коды возможностей модуля, которые могут поддерживаться или нет в зависимости + от типа модуля, версий прошивок и т.п. */ +typedef enum { + /** Поддержка установки делителя частоты вывода, отличного от #X502_OUT_FREQ_DIV_DEFAULT */ + X502_FEATURE_OUT_FREQ_DIV = 1, + /** Возможность чтения флагов состояния вывода с помощью X502_OutGetStatusFlags() */ + X502_FEATURE_OUT_STATUS_FLAGS = 2 +} t_x502_features; + +/** Флаги состояния для синхронного вывода */ +typedef enum { + /** Флаг указывает, что в настоящее время буфер в модуле на передачу пуст */ + X502_OUT_STATUS_FLAG_BUF_IS_EMPTY = 0x01, + /** Флаг указывает, что было опустошение буфера на вывод с начала старта синхронного + ввода-вывода или с момента последнего чтения статуса с помощью + X502_OutGetStatusFlags() (в зависимости от того, что было последним) */ + X502_OUT_STATUS_FLAG_BUF_WAS_EMPTY = 0x02 +} t_x502_out_status_flags; + + +/** @} */ + + +/***************************************************************************//** + @addtogroup type_list Типы данных + @{ + *****************************************************************************/ + +/** @brief Внутренняя информация записи о устройстве + + Непрозрачная структура с информацией, достаточной для установления + с ним связи. Зависит от типа устройства, интерфейса подключения и не + доступна пользователю напрямую, а используется библиотекой в + X502_OpenByDevRecord() */ +typedef struct st_x502_devrec_inptr t_x502_devrec_inptr; + +/** @brief Запись о устройстве + + Структура, описывающая устройство, по которой с ним можно установить соединение */ +typedef struct { + uint32_t sign; /**< Признак действительной структуры. + Если запись действительна (соответствует какому-либо устройству), + то должен быть равен #X502_DEVREC_SIGN) */ + char devname[X502_DEVNAME_SIZE]; /**< Название устройства */ + char serial[X502_SERIAL_SIZE]; /**< Серийный номер */ + char location[X502_LOCATION_STR_SIZE]; /**< Описание подключения (если есть) */ + uint32_t flags; /**< Флаги из #t_x502_dev_flags, описывающие устройство */ + uint8_t iface; /**< Интерфейс, по которому подключено устройство */ + uint8_t location_type; /**< Определяет, что именно сохранено в поле location + (одно значение из #t_x502_location_type) */ + char res[122]; /**< Резерв */ + t_x502_devrec_inptr* internal; /**< Непрозрачный указатель на структуру с + дополнительной информацией, необходимой + для открытия устройства */ +} t_x502_devrec; + + +/** @brief Описатель модуля + + Непрозрачный указатель на структуру, + содержащую информацию о настройках модуля и текущем соединении с ним. + Пользовательской программе не доступны поля структуры напрямую, а только + через функции библиотеки. + Функции управления модулем принимают описатель модуля своим первым параметром. + Описатель модуля создается с помощью X502_Create() и в конце работы + освобождается с помощью X502_Free(). */ +typedef struct st_x502* t_x502_hnd; + +/** @brief Список серийный номеров + + Тип определяет массив серийных номеров для количества модулей, определяемого + на этапе работы программы. */ +typedef char (*t_x502_serial_list)[X502_SERIAL_SIZE]; + +/** @brief Калибровочные коэффициенты диапазона. + + Структура содержит калибровочные значения смещения нуля и коэффициента + шкалы для одного диапазона АЦП или ЦАП.Результирующее значение АЦП + вычисляется как (val - offs) * k, где val - неоткалиброванное значение */ +typedef struct { + double offs; /**< смещение нуля */ + double k; /**< коэффициент шкалы */ +} t_x502_cbr_coef; + + +/** @brief Калибровочные коэффициенты модуля + + Структура, содержащая все калибровочные коэффициенты, которые + используются модулем L-502/E-502 */ +typedef struct { + /** Калибровочные коэффициенты АЦП */ + t_x502_cbr_coef adc[X502_ADC_RANGE_CNT]; + uint32_t res1[64]; /**< Резерв */ + /** Калибровочные коэффициенты ЦАП */ + t_x502_cbr_coef dac[X502_DAC_CH_CNT]; + uint32_t res2[20]; /**< Резерв */ +} t_x502_cbr; + +/** @brief Информация о модуле L-502/E-502 + + Структура, содержащая постоянную информация о модуле L-502/E-502, которая как правило + не изменяется после открытия */ +typedef struct { + char name[X502_DEVNAME_SIZE]; /**< Название устройства ("L502" или "E502" или "E16") */ + char serial[X502_SERIAL_SIZE]; /**< Серийный номер */ + + uint32_t devflags; /**< Флаги из #t_x502_dev_flags, описывающие наличие + в модуле определенных опций */ + uint16_t fpga_ver; /**< Версия ПЛИС (старший байт - мажорная, младший - минорная) */ + uint8_t plda_ver; /**< Версия ПЛИС, управляющего аналоговой частью */ + uint8_t board_rev; /**< Ревизия платы */ + uint32_t mcu_firmware_ver; /**< Версия прошивки контроллера Cortex-M4. Действительна только для модуля E-502 */ + uint32_t flash_size; /**< Размер внешней flash-памяти в байтах */ + uint8_t factory_mac[X502_MAC_ADDR_SIZE]; /**< Заводской MAC-адрес --- действителен только для + устройств с Ethernet-интерфейсом */ + uint8_t res[106]; /**< Резерв */ + t_x502_cbr cbr; /**< Заводские калибровочные коэффициенты (из Flash-памяти) */ +} t_x502_info; + +/** @} */ + + +/** @addtogroup func_list Функции + @{ **/ + +/***************************************************************************//** + @addtogroup func_hnd Функции для создания и освобождения описателя модуля + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Создание описателя модуля + + Создание описателя модуля, для последующей работы с модулем E-502 или L-502. + В случае успешного выделения памяти инициализирует поля описателя + значениями по умолчанию. + @return NULL в случае ошибки, иначе - описатель модуля +*******************************************************************************/ +X502_EXPORT(t_x502_hnd) X502_Create(void); + +/***************************************************************************//** + @brief Освобождение описателя модуля + + Освобождение памяти, выделенной под описатель модуля с помощью X502_Create(). + После этого описатель уже использовать нельзя, независимо от возвращенного + значения! + @param[in] hnd Описатель устройства + @return Код ошибки +*******************************************************************************/ +X502_EXPORT(int32_t) X502_Free(t_x502_hnd hnd); +/** @} */ + + + + + + + +/***************************************************************************//** + @addtogroup func_devrec Функции для работы с записями об устройстве + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Открыть соединение с модулем по записи о устройстве + + Функция устанавливает соединение с модулем E-502 или L-502 по записи об этом устройстве. + Необходимые действия зависят от того, на какое устройство подключенное по + какому интерфейсу ссылается запись. Сами записи создаются специальными + функциями (свои для каждого типа модуля и интерфейса подключения) и не + должны изменяться пользователем вручную. + + @param[in] hnd Описатель модуля. + @param[in] devrec Запись о устройстве, содержащая необходимую информацию + для установления с ним связи + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_OpenByDevRecord(t_x502_hnd hnd, const t_x502_devrec *devrec); + +/***************************************************************************//** + @brief Освобождение записей об устройствах + + Функция очищает ресурсы, выделенные при инициализации записи о устройстве + под информацию, необходимую для открытия устройства. + Данная функция должна вызываться после инициализации + записи о устройстве одной из соответствующих функций после того, когда + запись уже не нужна. После установки связи с устройством через + X502_OpenByDevRecord() запись не используется в дальнейшем и ее можно + при желании сразу освободить, не закрывая соединения с устройством. + Функция может очистить сразу несколько записей из массива (если очищается + одна, то в качестве размера допустимо указывать 1). + + @param[in] list Массив записей о устройстве или указатель на единственную + запись, ресурсы которой (которых) нужно освободить. + @param[in] size Количество записей в массиве + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_FreeDevRecordList(t_x502_devrec *list, uint32_t size); + +/** @} */ + + +/***************************************************************************//** + @addtogroup func_open Функции для открытия и получения информации о модуле + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Закрытие соединения с модулем + + Функция разрывает соединение с модулем E-502/L-502, если оно было ранее установлено + (в противном случае ничего не делает). + Описатель модуля не освобождается. + Память под описатель модуля должна быть освобождена вызовом X502_Free(). + @param[in] hnd Описатель модуля. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_Close(t_x502_hnd hnd); + + +/***************************************************************************//** + @brief Проверка, открыто ли соединение с модулем + + Функция проверяет, открыто ли в данный момент соединение с модулем. Если + соединение открыто, то функция возвращает #X502_ERR_OK. Если же закрыто, то + функция возвращает ошибку #X502_ERR_DEVICE_NOT_OPENED. + + @param[in] hnd Описатель модуля. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_IsOpened(t_x502_hnd hnd); + + + +/***************************************************************************//** + @brief Получение информации о модуле + + Получение информации о модуле L-502/E-502, с которым установлена связь. + @param[in] hnd Описатель модуля. + @param[out] info Информация о модуле (смотри описание типа #t_x502_info). + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_GetDevInfo(t_x502_hnd hnd, t_x502_info* info); + + +/** @} */ + +/***************************************************************************//** + @addtogroup func_config Функции для изменения настроек модуля + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Передача установленных настроек в модуль + + Функция выполняет запись текущих настроек (которые были установлены + с помощью функций X502_SetXXX) в модуль. + Должна вызываться перед запуском потока данных. + @param[in] hnd Описатель модуля. + @param[in] flags Флаги (резерв - должно быть равно 0). + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_Configure(t_x502_hnd hnd, uint32_t flags); + + +/***************************************************************************//** + @brief Установка параметров логического канала + + Функция устанавливает параметры заданного логического канала в логической + таблице АЦП. + @param[in] hnd Описатель модуля. + @param[in] lch Номер логического канала. + (от 0 до #X502_LTABLE_MAX_CH_CNT-1 или до #E16_LTABLE_MAX_CH_CNT-1 для E16) + @param[in] phy_ch Номер физического канала АЦП, начиная с 0 + (0-15 для дифференциального режима, + 0-31 для режима с общей землей) + @param[in] mode Режим измерения канал АЦП (значение типа #t_x502_lch_mode) + @param[in] range Диапазон измерения канала (значение типа #t_x502_adc_range) + @param[in] avg Коэффициент усреднения по каналу (не реализовано в E16). Нулевое значение + соответствует значению коэффициента, определенного + библиотекой. Для явного задания коэффициента усреднения + нужно перед значение от 1 (отсутствие усреднения) до + #X502_LCH_AVG_SIZE_MAX. + В случае если значение усреднения превышает делитель + частоты, то это значение будет скорректировано + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_SetLChannel(t_x502_hnd hnd, uint32_t lch, uint32_t phy_ch, + uint32_t mode, uint32_t range, uint32_t avg); + + +/***************************************************************************//** + @brief Получение массива диапазонов модуля E16 + + Возвращаем указатель на массив: + double ranges[] = {10, 2.5, 0.625, 0.15625}; + + @param[in] ranges_len Указатель куда записывает размер возвращаемого массива + @return Указатель на массив double размером ranges_len или NULL в случае ошибки. +*******************************************************************************/ +X502_EXPORT(double const*) E16_GetAdcRanges(uint32_t *ranges_len); + +/***************************************************************************//** + @brief Получение массива диапазонов модуля E502 + + Возвращаем указатель на массив: + double ranges[] = {10., 5., 2., 1., 0.5, 0.2}; + + @param[in] ranges_len Указатель куда записывает размер возвращаемого массива + @return Указатель на массив double размером ranges_len или NULL в случае ошибки. +*******************************************************************************/ +X502_EXPORT(double const*) E502_GetAdcRanges(uint32_t *ranges_len); + +/***************************************************************************//** + @brief Получение массива диапазонов модуля + + Для модуля E16: + double ranges[] = {10, 2.5, 0.625, 0.15625}; + Для модуля E502: + double ranges[] = {10., 5., 2., 1., 0.5, 0.2}; + + @param[in] hnd Описатель модуля. + @param[in] ranges_len Указатель куда записывает размер возвращаемого массива + @return Указатель на массив double размером ranges_len или NULL в случае ошибки. +*******************************************************************************/ +X502_EXPORT(double const*) X502_GetAdcRanges(t_x502_hnd hnd, uint32_t *ranges_len); + +/***************************************************************************//** + @brief Установка количества логических каналов + + Функция устанавливает количество логических каналов в логической таблице АЦП. + @param[in] hnd Описатель модуля + @param[in] lch_cnt Количество логических каналов + (от 1 до #X502_LTABLE_MAX_CH_CNT или до #E16_LTABLE_MAX_CH_CNT для E16) + @return Код ошибки +*******************************************************************************/ +X502_EXPORT(int32_t) X502_SetLChannelCount(t_x502_hnd hnd, uint32_t lch_cnt); + + +/***************************************************************************//** + @brief Получение количества логических каналов + + Функция возвращает установленное ранее с помощью X502_SetLChannelCount() + количество логических каналов в управляющей таблице АЦП. + @param[in] hnd Описатель модуля + @param[out] lch_cnt Количество логических каналов + @return Код ошибки +*******************************************************************************/ +X502_EXPORT(int32_t) X502_GetLChannelCount(t_x502_hnd hnd, uint32_t* lch_cnt); + +/***************************************************************************//** + @brief Установка делителя частоты сбора для АЦП + + Частота сбора АЦП получается как результат деления опорной частоты + синхронизации (как в случае внешней, так и внутренней) на делитель, + устанавливаемый этой функцией. + + Альтернативой этой функции служит X502_SetAdcFreq(), которая рассчитывает + этот делитель на основании переданной требуемой частоты сбора АЦП. + + @param[in] hnd Описатель модуля. + @param[in] adc_freq_div Делитель частоты АЦП (от 1 до #X502_ADC_FREQ_DIV_MAX). + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_SetAdcFreqDivider(t_x502_hnd hnd, uint32_t adc_freq_div); + +/***************************************************************************//** + @brief Установка значения межкадровой задержки для АЦП + + Функция устанавливает межкадровую задержку для АЦП, то есть количество + периодов опорной частоты синхронизации, которое будет пропущено после + проведения измерения последнего канала логической таблицы до проведения + измерения, соответствующего первому логическому каналу следующего кадра. + + Альтернативой может являться функция X502_SetAdcFreq(), которая рассчитывает + значение межкадровой задержки по заданным параметрам частоты сбора и частоты + следования кадров (частоты сбора на логический канал). + + @param[in] hnd Описатель модуля. + @param[in] delay Значение межкадровой задержки (от 0 до + #X502_ADC_INTERFRAME_DELAY_MAX) + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_SetAdcInterframeDelay(t_x502_hnd hnd, uint32_t delay); + +/***************************************************************************//** + @brief Установка делителя частоты синхронного ввода с цифровых линий. + + Частота синхронного ввода данных с цифровых входов получается как результат + деления опорной частоты синхронизации на делитель, устанавливаемый этой + функцией. + + Альтернативой этой функции служит X502_SetDinFreq(), которая рассчитывает + этот делитель на основании переданной требуемой частоты синхронного ввода + с цифровых линий. + + @param[in] hnd Описатель модуля. + @param[in] din_freq_div Делитель частоты синхронного ввода с цифровых линий + (от 1 до #X502_DIN_FREQ_DIV_MAX). + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_SetDinFreqDivider(t_x502_hnd hnd, uint32_t din_freq_div); + + + +/***************************************************************************//** + @brief Установка делителя частоты синхронного вывода + + Частота синхронного вывода данных получается как результат деления опорной + частоты синхронизации на делитель, устанавливаемый этой функцией. + Используется общая частота вывода для каждого канала ЦАП и для цифровых + линий (вывод осуществляется параллельно). Частота вывода не может быть + больше половины опорной частоты. + + Альтернативой этой функции служит X502_SetOutFreq(), которая рассчитывает + этот делитель на основании переданной требуемой частоты синхронного вывода. + + @note Для модуля L-502, чтобы была возможность установить делитель, отличный от + #X502_OUT_FREQ_DIV_DEFAULT, необходимо обновить прошивку ПЛИС до версии 0.5 или выше. + Для модуля E-502 возможность всегда поддерживается. Проверить программно наличие + данной возможности можно с помощью функции X502_CheckFeature(). + + @param[in] hnd Описатель модуля. + @param[in] out_freq_div Делитель частоты синхронного вывода + (от #X502_OUT_FREQ_DIV_MIN до #X502_OUT_FREQ_DIV_MAX). + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_SetOutFreqDivider(t_x502_hnd hnd, uint32_t out_freq_div); + + + +/***************************************************************************//** + @brief Установка частоты сбора АЦП + + Функция подбирает делитель частоты АЦП так, чтобы полученная частота сбора + была наиболее близка к указанной в параметре f_acq. Функция возвращает + в этом же параметре реальную частоту, которая была установлена. + + Так же функция может подобрать значение межкадровой задержки так, чтобы + частота следования кадров (частота сбора на логический канал) была наиболее + близка к указанному значению. + Для этого следует передать требуемое значение в переменной f_frame (в ней + также по завершению будет возвращено значение установленной частоты). + Если в качестве f_frame передан нулевой указатель, то будет установлена + нулевая межкадровая задержка. + + Если необходимо изменить значение опорной частоты, то данная функция должна + быть вызвана после X502_SetSyncMode() и X502_SetRefFreq() / X502_SetExtRefFreqValue(), + в противном случае полученные делители будут давать неверное значение частоты. + + Если устанавливается частота кадров, то функция должна вызываться после + того, как было заданно нужное количество логических каналов в управляющей + таблице с помощью X502_SetLChannelCount(). + + При использовании внешней опорной частоты синхронизации эта функция будет + давать верный результат, только если эта внешняя частота соответствует + значению, установленному с помощью X502_SetRefFreq(). + + + @param[in] hnd Описатель модуля. + @param[in,out] f_acq На входе принимает требуемое значения частоты сбора + АЦП в Герцах. На выходе возвращает реально + установленное значение частоты. + @param[in,out] f_frame На входе принимает требуемое значение частоты сбора + кадров (частоты сбора на логический канал) АЦП + в Герцах. На выходе возвращает реально + установленное значение. Если передан нулевой + указатель, то устанавливает максимальную частоту + сбора кадров (нулевую межкадровую задержку). + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_SetAdcFreq(t_x502_hnd hnd, double *f_acq, double *f_frame); + + +/***************************************************************************//** + @brief Установка частоты синхронного ввода с цифровых входов + + Функция подбирает делитель частоты ввода значений с цифровых входов так, чтобы + полученная частота ввода была наиболее близка к указанной. Функция возвращает + в этом же параметре реальную частоту, которая была установлена. + + Если необходимо изменить значение опорной частоты синхронизации, то данная + функция должна быть вызвана после X502_SetSyncMode() и + X502_SetRefFreq()/ X502_SetExtRefFreqValue(), в противном случае полученный + делитель будет давать неверное значение частоты. + + При использовании внешней опорной частоты синхронизации эта функция будет + давать верный результат, только если эта внешняя частота соответствует + значению, установленному с помощью X502_SetRefFreq(). + + @param[in] hnd Описатель модуля. + @param[in,out] f_din На входе принимает требуемое значения частоты ввода + с цифровых входов в Герцах. На выходе возвращает + реально установленное значение частоты. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_SetDinFreq(t_x502_hnd hnd, double *f_din); + + + +/***************************************************************************//** + @brief Установка частоты синхронного вывода + + Функция подбирает делитель частоты синхронного вывода так, чтобы + полученная частота была наиболее близка к указанной. Функция возвращает + в этом же параметре реальную частоту, которая была установлена. + + Если необходимо изменить значение опорной частоты синхронизации, то данная + функция должна быть вызвана после X502_SetSyncMode() и + X502_SetRefFreq() / X502_SetExtRefFreqValue(), в противном случае + полученный делитель будет давать неверное значение частоты. + + При использовании внешней опорной частоты синхронизации эта функция будет + давать верный результат, только если эта внешняя частота соответствует + значению, установленному с помощью X502_SetRefFreq(). + + @note Для модуля L-502, чтобы была возможность установить частоту, отличную от + опорной частоты, деленной на #X502_OUT_FREQ_DIV_DEFAULT, необходимо обновить + прошивку ПЛИС до версии 0.5 или выше. + Для модуля E-502 возможность всегда поддерживается. + Проверить программно наличие данной возможности можно с помощью + функции X502_CheckFeature(). + + + @param[in] hnd Описатель модуля. + @param[in,out] f_dout На входе принимает требуемое значения частоты + синхронного вывода в Герцах. На выходе возвращает + реально установленное значение частоты. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_SetOutFreq(t_x502_hnd hnd, double *f_dout); + + + +/***************************************************************************//** + @brief Получить текущие значения частот сбора АЦП + + Функция возвращает ткущие установленные для модуля значения частоты сбора + и частоты кадров АЦП (частоты на логический канал) в Герцах, которые были + установлены до этого с помощью X502_SetAdcFreq() или с помощью функций + X502_SetAdcFreqDivider() / X502_SetAdcInterframeDelay(). + + @param[in] hnd Описатель модуля. + @param[out] f_acq Если не NULL, то на выходе возвращается текущее + значение частоты сбора АЦП. + @param[out] f_frame Если не NULL, то на выходе возвращается текущее + значение частоты кадров АЦП. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_GetAdcFreq(t_x502_hnd hnd, double *f_acq, double *f_frame); + + +/***************************************************************************//** + @brief Установка значения внутренней опорной частоты синхронизации + + Функция задает значение внутренней опорной частоты синхронизации, от которой + получаются все частоты синхронного ввода/вывода посредством деления + на определенный делитель. + + Данная функция при внутренней опорной частете выбирает одну из двух доступных + частот в 2МГц или 1.5 МГц(2МГц является значением по умолчанию), для задания + которых можно введены константы из #t_x502_ref_freq. + + При использовании внешней опорной частоты следует использовать + X502_SetExtRefFreqValue(). + + Для модуля E-502 вывод на ЦАП при опорной частоте 1.5 МГц работает только + для версии прошивки PLDA 1 или выше. + + @param[in] hnd Описатель модуля. + @param[in] freq Значение из #t_x502_ref_freq, которое задает + выбранную опорную частоту. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_SetRefFreq(t_x502_hnd hnd, uint32_t freq); + +/***************************************************************************//** + @brief Установка значения внешней опорной частоты синхронизации + + При установке внешней опорной частоты (вызов X502_SetSyncMode() со занчением, + отличным от #X502_SYNC_INTERNAL) данная функция позволяет задать + частоту внешней опорной частоты, котороя может быть любая, но не превышать + 1.5 МГц. + + Данная функция не влияет на настройки самого модуля, однако установка корректного + значения позволяет правильно установить нужную частоту сбора функциями + X502_SetAdcFreq(), X502_SetDinFreq() и X502_SetOutFreq(), а также корректно + рассчитать значения по умолчанию для размера буфера и шага передачи данных + между модулем и ПК. + + Данная функция доступна в библиотеке версии 1.1.4 или выше. + + @param[in] hnd Описатель модуля. + @param[in] freq Значение внешней опорной частоты в Гц. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_SetExtRefFreqValue(t_x502_hnd hnd, double freq); + +/***************************************************************************//** + @brief Установка значения для аналоговой синхронизации (только для E16) + + @param[in] hnd Описатель модуля. + @param[in] phy_ch Номер физического канала АЦП, начиная с 0 + (0-15 для дифференциального режима, + 0-31 для режима с общей землей) + @param[in] mode Режим измерения канал АЦП (значение типа #t_x502_lch_mode) + @param[in] range Диапазон измерения канала (значение типа #t_x502_adc_range) + @param[in] value Значение в Вольтах. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_SetAdcSyncStartValue(t_x502_hnd hnd, uint32_t phy_ch, uint32_t mode, uint32_t range, double value); + +/***************************************************************************//** + @brief Получение значения опорной частоты синхронизации + + Данная функция возвращает текущее значение опорной частоты синхронизации, + которое используется библиотекой в функциях X502_SetAdcFreq(), X502_SetDinFreq() + и X502_SetOutFreq(), а также при рассчете параметров передачи данных между + модулем и ПК. + + При внутренней опорной частоте используется значение, установленное + X502_SetRefFreq() (1.5 или 2 Мгц), при внешней --- частота, установленная + с помощью функции X502_SetExtRefFreqValue(). + + Данная функция доступна в библиотеке версии 1.1.4 или выше. + + @param[in] hnd Описатель модуля. + @param[in] freq Значение внешней опорной частоты в Гц. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_GetRefFreqValue(t_x502_hnd hnd, double *freq); + + + + +/***************************************************************************//** + @brief Установка режима генерации частоты синхронизации + + Функция устанавливает кто будет генератором опорной частоты синхронизации - + сам модуль или будет использоваться внешний сигнал. + + В режиме #X502_SYNC_INTERNAL модуль сам будет генерировать для себя + частоту синхронизации с частотой, заданной X502_SetRefFreq(). + При этом запуск генерации будет осуществлен по вызову X502_StreamsStart() + или по условию, заданому в X502_SetSyncStartMode(), а останов + по X502_StreamsStop(). + + В остальных режимах сбор будет осуществляться по внешнему сигналу + синхронизации. + + @param[in] hnd Описатель модуля. + @param[in] sync_mode Значение из #t_x502_sync_mode, определяющее кто + будет источником частоты синхронизации. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_SetSyncMode(t_x502_hnd hnd, uint32_t sync_mode); + +/***************************************************************************//** + @brief Установка режима запуска частоты синхронизации + + Функция устанавливает условие запуска синхронного ввода/вывода данных. + + Если с помощью X502_SetSyncMode() установлен режим синхронизации + #X502_SYNC_INTERNAL, то по заданному данной функцией условию модуль начнет + генерировать частоту синхронизации, в противном случае по заданному условию + модуль начнет использовать внешне заданную частоту синхронизации + (т.е. до выполнения условия сигнал синхронизации на заданном входе будет + игнорироваться). + + Режимы задания условия запуска синхронизации имеют те же значения, + что и режимы задания самой частоты (см. тип #t_x502_sync_mode). + В случае #X502_SYNC_INTERNAL запуск осуществляется при выполнении функции + X502_StreamsStart(), в противном случае - после выполнения + X502_StreamsStart() модуль начинает ожидать заданного данной функцией условия. + Т.е. даже при задании внешних источников синхронизации, все равно необходимо + вызывать X502_StreamsStart(). + + @param[in] hnd Описатель модуля. + @param[in] sync_start_mode Значение из #t_x502_sync_mode, определяющее + условие запуска частоты синхронизации. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_SetSyncStartMode(t_x502_hnd hnd, uint32_t sync_start_mode); + + +/***************************************************************************//** + @brief Установить режим работы модуля + + Функция устанавливает режим работы модуля, который определяет будет ли + потоки данных обрабатывать ПЛИС или сигнальный процессор BlackFin. + При включении питания модулем всегда управляет ПЛИС. + После загрузки прошивки с помощью X502_BfLoadFirmware() модуль переходит + в режим управления сигнальным процессором. + + Данная функция может использоваться для ручной установки режима, + например, для возврата в режим управления ПЛИС или для переключения в режим + управления сигнальным процессором, если прошивка уже была загружена + (например, через JTAG интерфейс при отладке). + + @param[in] hnd Описатель модуля. + @param[in] mode Режим работы модуля из #t_x502_mode. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_SetMode(t_x502_hnd hnd, uint32_t mode); +/***************************************************************************//** + @brief Получение текущего режима работы модуля + + Функция возвращает текущий режим работы модуля. + @param[in] hnd Описатель модуля. + @param[out] mode В данном параметре возвращается текущий режим + работы модуля (из #t_x502_mode). + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_GetMode(t_x502_hnd hnd, uint32_t* mode); +/***************************************************************************//** + @brief Установить коэффициенты для калибровки значений АЦП + + Функция записывает в ПЛИС коэффициенты для калибровки значений АЦП. + При открытии модуля, библиотека считывает калибровочные коэффициенты из + защищенной области Flash-памяти модуля и записывает их в ПЛИС для + выполнения калибровки на лету. + + Результирующее значение АЦП вычисляется по формуле (val - offs) * k, где val --- + некалиброванное значение. + + Данная функция позволяет изменить используемые коэффициенты в то время, пока + не запущен синхронный сбор данных. При этом изменяются только текущие + коэффициенты, а заводские калибровочные коэффициенты из Flash-памяти + сохраняют свое значение и при следующем открытии будут восстановлены. + + @param[in] hnd Описатель модуля. + @param[in] range Диапазон АЦП (из #t_x502_adc_range). + @param[in] k Устанавливаемое значение коэффициента шкалы. + @param[in] offs Устанавливаемое значение смещения нуля. + @return Код ошибки. + ***************************************************************************/ +X502_EXPORT(int32_t) X502_SetAdcCoef(t_x502_hnd hnd, uint32_t range, double k, double offs); + +/***************************************************************************//** + @brief Получение текущих калибровочных коэффициентов АЦП + + Функция возвращает текущие калибровочные коэффициенты для заданного диапазона + измерения АЦП. Эти коэффициенты могут отличаться от заводских значений, + сохраненных во Flash-памяти модуля, например, если пользователь использовал + X502_SetAdcCoef() для установки своих коэффициентов. + + @param[in] hnd Описатель модуля. + @param[in] range Диапазон АЦП (из #t_x502_adc_range). + @param[in] k В данной переменной возвращается текущий коэффициент + шкалы. + @param[in] offs В данной переменной возвращается текущее смещение нуля. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_GetAdcCoef(t_x502_hnd hnd, uint32_t range, double* k, double* offs); + + + +/***************************************************************************//** + @brief Установить коэффициенты для калибровки значений ЦАП + + Функция устанавливает калибровочные коэффициенты для заданного канала АЦП, + которые будут использоваться функциями x502api для калибровки выводимых + значений ЦАП, если указан фалаг #X502_DAC_FLAGS_CALIBR. + + Откалиброванное значение ЦАП в кодах получается как + (val + offs) * k, где val --- некалиброванное значение (в кодах). + + При открытии модуля, библиотека считывает калибровочные коэффициенты из + защищенной области Flash-памяти модуля и использует их. + + Данная функция нужна только если пользователь хочет использовать свои + коэффициенты. При этом она не изменяет значения во Flash-памяти, т.е. + при следующем открытии модуля коэффициенты будут снова восстановлены из + Flash-памяти. + + @param[in] hnd Описатель модуля. + @param[in] ch Канал ЦАП (из #t_x502_dac_ch). + @param[in] k Устанавливаемое значение коэффициента шкалы. + @param[in] offs Устанавливаемое значение смещения нуля. + @return Код ошибки. + ***************************************************************************/ +X502_EXPORT(int32_t) X502_SetDacCoef(t_x502_hnd hnd, uint32_t ch, double k, double offs); + + +/***************************************************************************//** + @brief Получение текущих калибровочных коэффициентов ЦАП + + Функция возвращает текущие калибровочные коэффициенты для заданного канала ЦАП. + Эти коэффициенты могут отличаться от заводских значений, + сохраненных во Flash-памяти модуля, например, если пользователь использовал + X502_SetDacCoef() для установки своих коэффициентов. + + @param[in] hnd Описатель модуля. + @param[in] ch Канал ЦАП (из #t_x502_dac_ch). + @param[in] k В данной переменной возвращается текущий коэффициент + шкалы. + @param[in] offs В данной переменной возвращается текущее смещение нуля. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_GetDacCoef(t_x502_hnd hnd, uint32_t ch, double* k, double* offs); + + + +/***************************************************************************//** + @brief Рассчет частоты сбора АЦП + + Исходя из заданных параметров, функция подбирает делитель частоты + АЦП и значение межкадровой задержки так, чтобы полученные частоты были + наиболее близки к заданным, и возвращает полученные значения частот. + + В отличие от X502_SetAdcFreq(), данная функция предназначена получения + скорректированной частоты без использования описателя модуля и + только рассчитывает результирующие параметры, не изменяя настройки. + + Для модуля E16 в параметре ref_freq следует передавать E16_REF_FREQ_48000KHZ, + либо использовать функцию X502_CalcAdcFreq2! + + + @param[in] ref_freq Значение опорной частоты в Гц (внешней или внутренней) + @param[in] lch_cnt Количество логических каналов, которое будет использовано. + Необходимо для расчета межкадровой задержки. Если + в качестве f_frame передан нулевой указатель, то + может быть равно 0. + @param[in,out] f_acq На входе принимает требуемое значения частоты сбора + АЦП в Герцах. На выходе возвращает рассчитанное + значение частоты, которая может быть установлена. + @param[in,out] f_frame На входе принимает требуемое значение частоты сбора + кадров (частоты сбора на логический канал) АЦП + в Герцах. На выходе возвращает рассчитанное значение. + Если передан нулевой указатель, то задержка + рассчитана не будет. + Если передано значение меньше или равное нуля, то + будет рассчитана максимальная частота кадров + (с нулевой межкадровой задержкой). + @param[out] result_freq_div В данном параметре возвращается рассчитанное + значения делителя частоты АЦП. Может быть передан + нулевой указатель, если это значение явно знать + не требуется. + @param[out] result_frame_delay В данном параметре возвращается рассчитанное + значение межкадровой задержки. Может быть передан + нулевой указатель, если это значение явно знать + не требуется. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_CalcAdcFreq(double ref_freq, uint32_t lch_cnt, double *f_acq, + double *f_frame, uint32_t *result_freq_div, uint32_t *result_frame_delay); + + /***************************************************************************//** + @brief Рассчет частоты сбора АЦП + + Исходя из заданных параметров, функция подбирает делитель частоты + АЦП и значение межкадровой задержки так, чтобы полученные частоты были + наиболее близки к заданным, и возвращает полученные значения частот. + + В отличие от X502_SetAdcFreq(), данная функция предназначена получения + скорректированной частоты и только рассчитывает результирующие параметры, не изменяя настройки. + Описатель модуля в этой функции нужен для того чтобы различать модули E16 и X502. + + + @param[in] hnd Описатель модуля. + @param[in] ref_freq Значение опорной частоты в Гц (внешней или внутренней) + @param[in] lch_cnt Количество логических каналов, которое будет использовано. + Необходимо для расчета межкадровой задержки. Если + в качестве f_frame передан нулевой указатель, то + может быть равно 0. + @param[in,out] f_acq На входе принимает требуемое значения частоты сбора + АЦП в Герцах. На выходе возвращает рассчитанное + значение частоты, которая может быть установлена. + @param[in,out] f_frame На входе принимает требуемое значение частоты сбора + кадров (частоты сбора на логический канал) АЦП + в Герцах. На выходе возвращает рассчитанное значение. + Если передан нулевой указатель, то задержка + рассчитана не будет. + Если передано значение меньше или равное нуля, то + будет рассчитана максимальная частота кадров + (с нулевой межкадровой задержкой). + @param[out] result_freq_div В данном параметре возвращается рассчитанное + значения делителя частоты АЦП. Может быть передан + нулевой указатель, если это значение явно знать + не требуется. + @param[out] result_frame_delay В данном параметре возвращается рассчитанное + значение межкадровой задержки. Может быть передан + нулевой указатель, если это значение явно знать + не требуется. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_CalcAdcFreq2(t_x502_hnd hnd, double ref_freq, uint32_t lch_cnt, double *f_acq, + double *f_frame, uint32_t *adc_freq_div, uint32_t *adc_frame_delay); + +/***************************************************************************//** + @brief Рассчет частоты синхронного ввода с цифровых входов + + Исходя из заданных параметров, функция подбирает делитель частоты ввода значений + с цифровых входов так, чтобы полученная частота ввода была наиболее близка к указанной, + и возвращает полученное значение частоты. + + В отличие от X502_SetDinFreq(), данная функция предназначена получения + скорректированной частоты без использования описателя модуля и + только рассчитывает результирующие параметры, не изменяя настройки. + + Для модуля E16 в параметре ref_freq следует передавать E16_REF_FREQ_48000KHZ, + либо использовать функцию X502_CalcDinFreq2! + + @param[in] ref_freq Значение опорной частоты в Гц (внешней или внутренней) + @param[in,out] f_din На входе принимает требуемое значения частоты ввода + с цифровых входов в Герцах. На выходе возвращает + рассчитанное значение частоты, которое может быть + установлено. + @param[out] result_freq_div В данном параметре возвращается рассчитанное + значения делителя частоты синхронного ввода цифровых + линий. + Может быть передан нулевой указатель, + если это значение явно знать не требуется. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_CalcDinFreq(double ref_freq, double *f_din, uint32_t *result_freq_div); + +/***************************************************************************//** + @brief Рассчет частоты синхронного ввода с цифровых входов + + Исходя из заданных параметров, функция подбирает делитель частоты ввода значений + с цифровых входов так, чтобы полученная частота ввода была наиболее близка к указанной, + и возвращает полученное значение частоты. + + В отличие от X502_SetDinFreq(), данная функция только рассчитывает результирующие параметры, не изменяя настройки. + + @param[in] hnd Описатель модуля. + @param[in] ref_freq Значение опорной частоты в Гц (внешней или внутренней) + @param[in,out] f_din На входе принимает требуемое значения частоты ввода + с цифровых входов в Герцах. На выходе возвращает + рассчитанное значение частоты, которое может быть + установлено. + @param[out] result_freq_div В данном параметре возвращается рассчитанное + значения делителя частоты синхронного ввода цифровых + линий. + Может быть передан нулевой указатель, + если это значение явно знать не требуется. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_CalcDinFreq2(t_x502_hnd hnd, double ref_freq, double *f_din, uint32_t *result_freq_div); + +/***************************************************************************//** + @brief Рассчет частоты синхронного вывода + + Исходя из заданных параметров, функция подбирает делитель частоты синхронного + вывода так, чтобы полученная частота была наиболее близка к указанной, + и возвращает полученное значение частоты. + + В отличие от X502_SetOutFreq(), данная функция предназначена получения + скорректированной частоты без использования описателя модуля и + только рассчитывает результирующие параметры, не изменяя настройки. + + Функция предполагает, что модуль поддерживает изменение частоты вывода + (см. требования к модулю для этого в описании функции X502_SetOutFreq()). + + Для модуля E16 в параметре ref_freq следует передавать E16_REF_FREQ_48000KHZ, + либо использовать функцию X502_CalcOutFreq2! + + @param[in] ref_freq Значение опорной частоты в Гц (внешней или внутренней) + @param[in,out] f_dout На входе принимает требуемое значения частоты + синхронного вывода в Герцах. На выходе возвращает + рассчитанное значение частоты, которое может быть + установлено. + @param[out] result_freq_div В данном параметре возвращается рассчитанное + значения делителя частоты синхронного вывода. + Может быть передан нулевой указатель, + если это значение явно знать не требуется. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_CalcOutFreq(double ref_freq, double *f_dout, uint32_t *result_freq_div); + +/***************************************************************************//** + @brief Рассчет частоты синхронного вывода + + Исходя из заданных параметров, функция подбирает делитель частоты синхронного + вывода так, чтобы полученная частота была наиболее близка к указанной, + и возвращает полученное значение частоты. + + В отличие от X502_SetOutFreq(), данная функция предназначена получения + скорректированной частоты без использования описателя модуля и + только рассчитывает результирующие параметры, не изменяя настройки. + + Функция предполагает, что модуль поддерживает изменение частоты вывода + (см. требования к модулю для этого в описании функции X502_SetOutFreq()). + + @param[in] hnd Описатель модуля. + @param[in] ref_freq Значение опорной частоты в Гц (внешней или внутренней) + @param[in,out] f_dout На входе принимает требуемое значения частоты + синхронного вывода в Герцах. На выходе возвращает + рассчитанное значение частоты, которое может быть + установлено. + @param[out] result_freq_div В данном параметре возвращается рассчитанное + значения делителя частоты синхронного вывода. + Может быть передан нулевой указатель, + если это значение явно знать не требуется. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_CalcOutFreq2(t_x502_hnd hnd, double ref_freq, double *f_dout, uint32_t *result_freq_div); + +/** @} */ + + +/***************************************************************************//** + @addtogroup func_async Функции асинхронного ввода-вывода + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Асинхронный вывод данных на канал ЦАП + + Функция выводит указанное значение на указанный канал ЦАП. Значение может + быть задано как в кодах, так и в Вольтах, и к нему могут быть применены + калибровочные коэффициенты (определяется флагами). + + Функция может вызываться либо когда синхронный сбор не запущен, либо при + запущенном сборе данных, если синхронный вывод по этому каналу ЦАП + не разрешен. + + @note Функция не работает в случае, если модуль находится в ожидании внешнего + условия для запуска синхронного ввода (была вызвана X502_StreamsStart() при + внешней синхронизации старта, но сам сигнал запуска еще не возник). + + @param[in] hnd Описатель модуля. + @param[in] ch Номер канала ЦАП (из #t_x502_dac_ch). + @param[in] data Выводимое значение на ЦАП (в кодах или вольтах) + @param[in] flags Флаги из #t_x502_dacout_flags. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_AsyncOutDac(t_x502_hnd hnd, uint32_t ch, double data, uint32_t flags); + +/***************************************************************************//** + @brief Асинхронный вывод данных на цифровые выходы + + Функция выводит указанное значение на цифровые выходы модуля. + Формат значения аналогичен X502_PrepareData() - в младших 16 битах + указывается выводимое значение, а в старшие - флаги (с помощью которых можно + перевести одну из половин в третье состояние). + + Функция может вызываться либо когда синхронный сбор не запущен, либо при + запущенном сборе данных, если синхронный вывод по цифровым линиям не разрешен. + + Можно использовать маску, чтобы вывести только на часть выводов, оставив + остальные неизменными, однако следует учесть, что после открытия связи с + модулем необходимо сперва сделать вывод на все линии, после чего уже + можно использовать маску при последующих вызовах. + + @note Функция не работает в случае, если модуль находится в ожидании внешнего + условия для запуска синхронного ввода (была вызвана X502_StreamsStart() при + внешней синхронизации старта, но сам сигнал запуска еще не возник). + + @param[in] hnd Описатель модуля. + @param[in] val Младшая половина - выводимое значение, старшая - + флаги из #t_x502_digout_word_flags. + @param[in] msk Маска - указанные в маске биты не будут изменяться + с предыдущего выведенного состояния (распространяется + и на старшую половину val). + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_AsyncOutDig(t_x502_hnd hnd, uint32_t val, uint32_t msk); + + +/***************************************************************************//** + @brief Асинхронный ввод значений с цифровых входов + + Функция считывает текущее значение цифровых входов. + При этом синхронный сбор цифровых входов не должен быть запущен (не разрешен + поток #X502_STREAM_DIN). + + Так как модули E-502/L-502 не поддерживают аппаратно асинхронный ввод, то если на + момент вызова этой функции не запущен синхронный ввод/вывод с помощью + X502_StreamsStart(), то данная функция на время выполнения запускает + синхронный сбор и останавливает его как только будет получено одно новое + значение цифровых входов, для модулей E16 этого не требуется. + + Для модуля E16 17 бит - состояние входа INT, 18 бит - TRIG + + @note Функция не работает в случае, если модуль находится в ожидании внешнего + условия для запуска синхронного ввода (была вызвана X502_StreamsStart() при + внешней синхронизации старта, но сам сигнал запуска еще не возник). + + @param[in] hnd Описатель модуля. + @param[out] din При успешном выполнении в этой переменной + возвращается текущее состояние цифровых входов. + Действительны младшие 18 бит, старшие 14 - резерв. + Часть битов при этом объединены с линиями синхронизации, + при этом это объединение зависит от типа модуля. + Подробнее описано в разделе с различиями модулей + E-502 и L-502. + Резервные биты могут быть использованы в последующих + версиях, не следует считать, что они всегда будут + равны нулю! + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_AsyncInDig(t_x502_hnd hnd, uint32_t* din); + + +/***************************************************************************//** + @brief Асинхронный ввод одного кадра АЦП + + Функия производит однократный ввод кадра в соответствии с заранее + установленной логической таблицей. Частота сбора АЦП соответствует частоте, + установленной с помощью X502_SetAdcFreq(). Частота следования кадров + значения не имеет. Сам кадр вводится синхронно, но при последовательном + вызове X502_AsyncGetAdcFrame() для измерения нескольких кадров задержка + между этими кадрами не определена. + + Функция так же выполняет обработку принятых данных АЦП, аналогично + X502_ProcessAdcData(), и принимает набор флагов, аналогичный + X502_ProcessAdcData(). + + Для работы этой функции не должен быть разрешен синхронный ввод АЦП + и цифровых линий. + + Так как аппаратно асинхронный ввод в плате отсутствует, то эта функция + в случае не запущенного потока запускает его внутри себя, принимает один + кадр данных и после этого останавливает синхронный сбор. + + @param[in] hnd Описатель модуля. + @param[in] flags Флаги из t_x502_proc_flags + @param[in] tout Таймаут на выполнение функции в мс + @param[out] data Массив, в котором в случае успеха будут возвращены + отсчеты кадра АЦП. Должен быть размером, достаточным + для хранения отсчетов типа double в количестве, + равном количеству установленных логических каналов + в управляющей таблице АЦП. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_AsyncGetAdcFrame(t_x502_hnd hnd, uint32_t flags, + uint32_t tout, double* data); + +/** @} */ + + + + + +/***************************************************************************//** + @addtogroup func_streams Функции для работы с синхронным потоковым вводом-выводом + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Разрешение синхронных потоков на ввод/вывод + + Функция разрешает прием/передачу для указанных потоков + Не указанные потоки сохраняют свое разрешенное или запрещенное состояние. + Может вызываться как до X502_Configure(), так и после. + Разрешенные потоки устанавливаются как правило до вызова X502_StreamsStart(). + + При желании в некоторых ситуациях можно изменять состав разрешенных потоков + во время запущенного сбора данных, однако если эти потоки сильно различаются + в частоте, то рассчитанные библиотекой значения буфера и шага прерывания + могут не подходить для изменившихся значений (см. @ref sect_sync_mode_buf) + @param[in] hnd Описатель модуля. + @param[in] streams Набор флагов #t_x502_streams, указывающих, какие потоки + должны быть разрешены. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_StreamsEnable(t_x502_hnd hnd, uint32_t streams); +/****************************************************************************//** + @brief Запрещение синхронных потоков на ввод/вывод + + Функция запрещает передачу синхронных данных для указанных потоков. + Не указанные потоки сохраняют свое разрешенное или запрещенное состояние. + Функция, противоположная по смыслу X502_StreamsEnable(). + @param[in] hnd Описатель модуля. + @param[in] streams Набор флагов #t_x502_streams, указывающих, какие потоки + должны быть запрещены. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_StreamsDisable(t_x502_hnd hnd, uint32_t streams); + +/****************************************************************************//** + @brief Получить значение, какие синхронные потоки разрешены + + Функция позволяет получить набор флагов, которые указывают, какие синхронные + потоки сейчас разрешены. + + @param[in] hnd Описатель модуля. + @param[out] streams Набор флагов #t_x502_streams, указывающих, какие потоки + сейчас разрешены. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_GetEnabledStreams(t_x502_hnd hnd, uint32_t* streams); + +/***************************************************************************//** + @brief Запуск синхронных потоков ввода/вывода + + Функция запуска синхронных потоков данных. Все синхронные потоки тактируются + от общей опорной частоты. Если был установлен внутренний старт синхронизации, + то синхронизация потоков начнется при выполнении данной функции, в противном + случае по данной функции модуль перейдет в состояние ожидания внешнего + признака начальной синхронизации. + + Также функция осуществляет инициализацию канала DMA на ввод данных из платы, + если был разрешен поток АЦП или синхронного ввода цифровых линий, + и инициализацию канала DMA на вывод, если был разрешен хотя бы один поток на + вывод, но не была вызвана функция X502_PreloadStart() (однако в этом случае + начало вывода не совпадет с началом ввода). + + @param[in] hnd Описатель модуля. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_StreamsStart(t_x502_hnd hnd); + +/***************************************************************************//** + @brief Останов синхронных потоков ввода/вывода + + Функция останова синхронных потоков ввода/вывода данных. После выполнению + этой функции модуль завершает генерацию опорной частоты синхронизации (или + использовать внешнюю частоту синхронизации) и останавливает синхронную + передачу данных. + @param[in] hnd Описатель модуля. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_StreamsStop(t_x502_hnd hnd); + + +/***************************************************************************//** + @brief Проверка, запущен ли синхронный ввод/вывод + + Функция проверяет запущен ли синхронный ввод вывод с помощью X502_StreamsStart() + или какой-либо внутренней логикой в прошивки BlackFin. + Если сбор данных не запущен, то функция возвращает ошибку + #X502_ERR_STREAM_IS_NOT_RUNNING, если запущен, то нулевой код ошибки + + @param[in] hnd Описатель модуля. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_IsRunning(t_x502_hnd hnd); + + + +/***************************************************************************//** + @brief Чтение данных АЦП и цифровых входов из модуля + + Функция считывает данные от модуля, которые были приняты в промежуточный + буфер в драйвере или библиотеке. + Функция принимает отсчеты в специальном индексном формате, + в котором содержится информация, что это за данные (значения цифровых входов + или отсчеты АЦП) и дополнительная информация для АЦП (номер канала, режим). + Для разбора полученных отсчетов используется функция X502_ProcessData(). + + Если в буфере сейчас находится меньше отсчетов, чем было запрошено, + то функция будет ожидать пока придет заданное количество данных или + пока не истечет указанный таймаут. В последнем случае функция возвратит + столько отсчетов, сколько было в буфере при истечении таймаута. + + Количество готовых для чтения отсчетов в буфере драйвера можно при желании + узнать с помощью функции X502_GetRecvReadyCount(). + + До вызовов X502_Recv() синхронный поток сбора данных должен быть уже запущен + с помощью X502_StreamsStart(). + + @param[in] hnd Описатель модуля. + @param[out] buf Буфер, в которые будут сохранены отсчеты. + @param[in] size Количество считываемых отсчетов (32-битных слов). + @param[in] tout Таймаут на прием данных в мс. + @return Если < 0 - код ошибки. + Если >= 0 - количество считанных слов. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_Recv(t_x502_hnd hnd, uint32_t* buf, uint32_t size, uint32_t tout); + + +/***************************************************************************//** + @brief Передача потоковых данных ЦАП и цифровых выходов в модуль + + Функция записывает данные на передачу в промежуточный буфер, после + чего эти данные будут переданы в модуль. + Данные должны быть в специальном формате, который определяет, что это за + данные (цифровые выходы, канал ЦАП1 или канал ЦАП2). Подготовить данные + в нужном формате можно с помощью X502_PrepareData(). + + Если промежуточный буфер на передачу заполнен, то функция будет ждать пока + он не освободится или пока не истечет указанный таймаут. + Количество свободного места в буфере можно при желании узнать с помощью + функции X502_GetSendReadyCount(). + + Возвращение означает, что данные записаны в промежуточный буфер, а не + то что они уже дошли до модуля и выведены. + + Перед вызовом этой функции должна быть запущена предзагрузка данных на + вывод с помощью X502_PreloadStart(). + + @param[in] hnd Описатель модуля. + @param[in] buf Буфер со словами, которые необходимо передать модулю + @param[in] size Количество передаваемых отсчетов (32-битных слов). + @param[in] tout Таймаут на передачу (в буфер драйвера) данных в мс. + @return Если < 0 - код ошибки. + Если >= 0 - количество записанных слов. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_Send(t_x502_hnd hnd, const uint32_t* buf, uint32_t size, uint32_t tout); + + + +/***************************************************************************//** + @brief Обработка принятых отсчетов АЦП от модуля + + Функция выполняет обработку отсчетов АЦП, прочитанных с помощью X502_Recv(). + Функция проверяет служебную информацию из входного массива и переводит отсчеты + АЦП либо в коды, либо в вольты (если указан флаг #X502_PROC_FLAGS_VOLT). + + Функция используется, когда не запущен синхронный ввод с цифровых линий и + отсчеты АЦП являются единственными приходящими от модуля данными (если + в принятом потоке будут другие данные - то они будут отброшены). + + Если запущен синхронный ввод с цифровых линий, то следует использовать + X502_ProcessData(), которая выделяет данные с цифровых линий в отдельный + массив. + + @param[in] hnd Описатель модуля. + @param[in] src Входной массив отсчетов, принятых с помощью X502_Recv(). + @param[out] dest Массив, в который будут сохранены преобразованные данные + от АЦП. + @param[in,out] size На входе - количество слов в массиве src, на выходе - + количество сохраненных преобразованных значений в + массиве dest + @param[in] flags Набор флагов из #t_x502_proc_flags + @return Код ошибки. + ****************************************************************************/ +X502_EXPORT(int32_t) X502_ProcessAdcData(t_x502_hnd hnd, const uint32_t* src, + double *dest, uint32_t *size, uint32_t flags); + +/***************************************************************************//** + @brief Обработка принятых от модуля данных + + Функция выполняет обработку данных, прочитанных с помощью X502_Recv(). + Функция проверяет служебную информацию из входного массива, разбивает данные + на два массива - данные от АЦП, которые переводятся в тип double, и данные + от синхронного цифрового ввода. + + Данные от АЦП так же могут быть переведены в вольты. При этом данные АЦП + приходят от модуля уже откалиброванными с помощью калибровочных коэффициентов, + так как калибровка выполняется аппаратно. Если данные АЦП не переводятся + в Вольты и при этом не были изменены заводские калибровочные коэффициенты, + то возвращенное значение равное #X502_ADC_SCALE_CODE_MAX соответствует + напряжению равному максимальному для используемого диапазона. + + Кроме того, функция разбирает сообщения, передаваемые в потоке данных + (например, сообщение о переполнении буфера). + @param[in] hnd Описатель модуля. + @param[in] src Входной массив отсчетов, принятый с помощью X502_Recv(). + @param[in] size Количество отсчетов (32-битных слов) в массиве src. + @param[in] flags Набор флагов из #t_x502_proc_flags, управляющих + поведением функции. Может быть указано несколько флагов + через логическое "ИЛИ". + @param[out] adc_data Массив, в который будут сохранены данные от АЦП, + преобразованные в соответствии с указанными флагами. + Может быть NULL, если не нужно сохранять данные от АЦП + (тогда adc_data_size должен быть тоже NULL, или в + переменной передан размер 0). + @param[in,out] adc_data_size На входе в данном параметре передается резмер + буфера adc_data. Если данных от АЦП во входном массиве + будет больше adc_data_size, то в adc_data будет + сохранено только первые adc_data_size отсчетов. + На выходе при успешном завершении функции в данную + переменную записывается количество сохранных отсчетов + АЦП. + Указатель может быть равен NULL, если adc_data = NULL + @param[out] din_data Массив, в который будут сохранены отчеты с синхронного + цифрового ввода. Каждое слово соответствуют состоянию + всех цифровых входов в формате, описанном в функции + X502_AsyncInDig(). + @param[in,out] din_data_size Аналогично параметру adc_data_size в этом + параметре передается размер буфера din_data в отсчетах, + а на выходе сохраняется количество реально сохраненных + отсчетов цифрового ввода. Может быть NULL, если + din_data = NULL. + @return Код ошибки. + ****************************************************************************/ +X502_EXPORT(int32_t) X502_ProcessData(t_x502_hnd hnd, const uint32_t* src, uint32_t size, + uint32_t flags, double *adc_data, uint32_t *adc_data_size, + uint32_t *din_data, uint32_t *din_data_size); + +/***************************************************************************//** + @brief Обработка принятых от модуля данных с пользовательскими данными + + Функция аналогична X502_ProcessData(), но позволяет также выделить + пользовательские данные из потока. Пользовательскими данными считаются все + отсчеты, которые не являются данными АЦП, данными цифрового ввода + или сообщениями. + Пользовательские данные складываются без изменений в массив usr_data + (если он не равен нулю). + Данная функция предназначена в первую очередь для программистов, которые + будут использовать модифицированную прошивку сигнального процессора BlackFin. + @param[in] hnd Описатель модуля. + @param[in] src Входной массив отсчетов, принятый с помощью + X502_Recv(). + @param[in] size Количество отсчетов (32-битных слов) в массиве src. + @param[in] flags Набор флагов из #t_x502_proc_flags. + @param[out] adc_data Массив, в который будут сохранены данные от АЦП + (см. X502_ProcessData()). + @param[in,out] adc_data_size см. X502_ProcessData() + @param[out] din_data Массив, в который будут сохранены отчеты с + синхронного цифрового ввода. См. X502_ProcessData(). + @param[in,out] din_data_size см. X502_ProcessData(). + @param[out] usr_data Массив, в который будут сохранены пользовательские + данные без изменения их формата. + @param[in,out] usr_data_size В этом параметре передается размер буфера usr_data + а на выходе сохраняется количество реально сохраненных + отсчетов пользовательских данных. + Может быть NULL только если usr_data = NULL. + @return Код ошибки. + ****************************************************************************/ +X502_EXPORT(int32_t) X502_ProcessDataWithUserExt(t_x502_hnd hnd, const uint32_t* src, uint32_t size, + uint32_t flags, double *adc_data, + uint32_t *adc_data_size, uint32_t *din_data, + uint32_t *din_data_size, + uint32_t *usr_data, uint32_t *usr_data_size); + + + +/***************************************************************************//** + @brief Подготовка данных для вывода в модуль + + Функция принимает данные из трех массивов - данные на цифровые выходы, + отсчеты первого и второго канала ЦАП. В качестве массива может быть передан + нулевой указатель, если данные из этого источника не требуются. + Все используемые массивы должны быть одинакового размера и функция их + равномерно перемешивает в общий поток, преобразуя в нужный для модуля формат. + + Выходной массив должен будет содержать n*size отсчетов, где n - количество + используемых входных массивов (от 1 до 3). + + Значения цифровых выходов представляют собой 32-битные слова, младшие 16-бит + которых определяют значения выводов, а старшие - флаги из + #t_x502_digout_word_flags, которые могут использоваться в частности для + перевода одной (или обеих) из половин выводов в третье состояние. + + В качестве значений ЦАП могут использоваться как коды, так и Вольты, + в зависимости от переданных флагов, и к выводимым значениям могут быть + применены калибровочные коэффициенты. + Если используются коды ЦАП с включенной калибровкой, то код + #X502_DAC_SCALE_CODE_MAX определяет код, соответствующий +5V. + + @param[in] hnd Описатель модуля. + @param[in] dac1 Входной массив отсчетов первого канала ЦАП или + NULL, если не используется. + @param[in] dac2 Входной массив отсчетов второго канала ЦАП или + NULL, если не используется. + @param[in] digout Входной массив со значениями цифровых выводов + или NULL, если не используется. + @param[in] size Размер каждого из используемых входных массивов. + @param[in] flags Флаги, управляющие работой функции, из + #t_x502_dacout_flags. + @param[out] out_buf Выходной массив, в который будут сохранены + сформированные отсчеты. Должен быть размера + n*size (n - количество используемых входных + массивов) + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_PrepareData(t_x502_hnd hnd, const double* dac1, const double* dac2, + const uint32_t* digout, uint32_t size, int32_t flags, + uint32_t* out_buf); + +/***************************************************************************//** + @brief Получить количество отсчетов в буфере потока на ввод + + Функция возвращает количество отсчетов, которые были приняты из модуля + во внутренний буфер и готовы для считывания с помощью X502_Recv(). + То есть если в X502_Recv() передать значение, которое вернула данная функция, + то X502_Recv() вернет это количество данных без ожидания (так как они уже + в буфере). + При работе по Ethernet данная функция возвращает корректное значение только + для ОС Windows. + @param[in] hnd Описатель модуля. + @param[out] rdy_cnt Количество готовых к приему отсчетов. + @return Код ошибки. + ***************************************************************************/ +X502_EXPORT(int32_t) X502_GetRecvReadyCount(t_x502_hnd hnd, uint32_t *rdy_cnt); + + +/***************************************************************************//** + @brief Получить размер свободного места в буфере потока на вывод + + Функция возвращает количество отсчетов, соответствующее свободному месту + в буфере на передачу в модуль. + Это количество отсчетов гарантированно может быть передано с помощью + X502_Send() без ожидания. + Данная функция не реализована при работе по интерфейсу Ethernet (TCP). + @param[in] hnd Описатель модуля. + @param[out] rdy_cnt Количество слов, которому соответствует + свободное место в буфере на передачу. + @return Код ошибки. + ***************************************************************************/ +X502_EXPORT(int32_t) X502_GetSendReadyCount(t_x502_hnd hnd, uint32_t *rdy_cnt); + +/***************************************************************************//** + @brief Получить номер следующего ожидаемого логического канала АЦП для + обработки + + Функция возвращает номер логического канала АЦП, который должен быть + обработан первым при следующем вызове X502_ProcessData()/ + X502_ProcessAdcData() в случае, если поток данных непрерывен. + + По сути, это номер логического канала, следующий за логическим каналом + последнего обработанного до этого отсчета АЦП. + Может быть использовано при обработке блоков данных не кратных целому + количеству кадров. + Если перед X502_ProcessData() вызывать данную функцию, то она вернет номер + логического канала, соответствующий первому отсчету АЦП, + обработанному последующим вызовом X502_ProcessData(). + + Например, если установлено 7 логических каналов, а в X502_ProcessData() + передано для обработки кратное 7 количество отсчетов, то последующий вызов + X502_GetNextExpectedLchNum() вернет номер канала равный 0 (так как обработано + целое число кадров и ожидается снова начало кадра). + Если в X502_ProcessData() передан массив с 7*n + 5 отсчетами АЦП, то следующим + ожидаемым каналом будет логический канал с номером 5 (обработаны каналы + 0,1,2,3,4 из неполного кадра). + + @param[in] hnd Описатель модуля. + @param[out] lch Номер логического канала (начиная с нуля). + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_GetNextExpectedLchNum(t_x502_hnd hnd, uint32_t *lch); + + + +/***************************************************************************//** + @brief Начало подготовки вывода синхронных данных + + Функция должна вызываться перед началом предзагрузки потоковых синхронных + данных на вывод. Для начала выдачи синхронных данных одновременно с началом + синхронного ввода, к моменту начала сбора часть данных должна быть уже + загружена в модуль до вызова X502_StreamsStart(). + + Данная функция инициализирует канал обмена на передачу данных на вывод. + После вызова этой функции можно загрузить часть данных на вывод + с помощью X502_Send(). + + @param[in] hnd Описатель модуля. + @return Код ошибки. + ***************************************************************************/ +X502_EXPORT(int32_t) X502_PreloadStart(t_x502_hnd hnd); + + + +/***************************************************************************//** + @brief Начало загрузки циклического сигнала на вывод + + По вызову этой функции в драйвере (для L502) или в памяти контроллера модуля + (для E502) выделяется место под циклический буфер на вывод. + Должна вызываться перед загрузкой циклических данных с помощью + X502_Send(). + + Для успешного выполнения должен быть свободный буфер (используется + двойная буферизация) - т.е. функция не может быть вызвана сразу после предыдущего + X502_OutCycleSetup(). Кроме того не должен был быть использован потоковый + вывод. + + Для L-502 размер максимальный размер буфера определяется только размером, + который позволяет выделить система на уровне драйвера. + Для E-502 размер ограничен памятью встроенного контроллера. + Подробнее смотри в описании различий E-502 и L-502. + + @param[in] hnd Описатель модуля. + @param[in] size Количество отсчетов в выводимом циклическом сигнале + суммарно для всех используемых каналов вывода. + @return Код ошибки. + ***************************************************************************/ +X502_EXPORT(int32_t) X502_OutCycleLoadStart(t_x502_hnd hnd, uint32_t size); + +/***************************************************************************//** + @brief Установка ранее загруженного циклического сигнала на вывод + + По вызову этой функции ранее загруженный циклический буфер становится активным. + Если синхронный ввод-вывод запущен (через X502_StreamsStart()), то по этой + функции сигнал будет выдаваться на выход, иначе выдача начнется при запуске + синхронного ввода-вывода. + + Если до этого уже выводился циклический сигнал, то смена на новый произойдет + в конце цикла предыдущего сигнала, если не указан флаг + #X502_OUT_CYCLE_FLAGS_FORCE. + + Если не указан флаг #X502_OUT_CYCLE_FLAGS_WAIT_DONE, то функция только дает + команду на установку сигнала, не ожидая непосредственной смены сигнала или + загрузки сигнала. В частности для одновременного запуска синхронного ввода + и вывода необходимо сделать загрузку первого циклического сигнала с данным + флагом, чтобы гарантировать, что сигнал полностью загружен к моменту запуска + синхронного ввода-вывода через X502_StreamsStart(). + + Данная функция должна быть вызвана только после вызова X502_OutCycleLoadStart() + и загрузки указанного в ней количества отсчетов в буфер! + + @param[in] hnd Описатель модуля. + @param[in] flags Флаги из #t_x502_out_cycle_flags. + @return Код ошибки. + ***************************************************************************/ +X502_EXPORT(int32_t) X502_OutCycleSetup(t_x502_hnd hnd, uint32_t flags); + + +/***************************************************************************//** + @brief Останов вывода циклического сигнала + + По вызову этой функции прекращается выдача ранее установленного циклического + сигнала с помощью X502_OutCycleSetup(). Остановка осуществляется + после выдачи последнего отсчета в периоде, что позволяет знать какие значения + останутся на выходах. + + При вызове же X502_StreamsStop() (или при запрещении всех потоков + на вывод через X502_StreamsDisable()) останов всех потоков происходит сразу + и точная точка останова неизвестна. + + При этом необходимо учитывать, что сама функция по умолчанию только делает запрос + на останов, а реально останов произойдет позже. Если вызвать X502_StreamsStop() + до завершения останова, то последний отсчет будет неизвестен, т.е. необходимо + дождаться завершения останова, для чего может например быть использован + флаг #X502_OUT_CYCLE_FLAGS_WAIT_DONE. + + @param[in] hnd Описатель модуля. + @param[in] flags Флаги из #t_x502_out_cycle_flags. + @return Код ошибки. + ***************************************************************************/ +X502_EXPORT(int32_t) X502_OutCycleStop(t_x502_hnd hnd, uint32_t flags); + + +/***************************************************************************//** + @brief Проверка, завершена ли установка или останов циклического сигнала + + Функция проверяет, завершена ли установка циклического сигнала после вызова + X502_OutCycleSetup() или завершена ли остановка генерации циклического + сигнала после вызова X502_OutCycleStop(). По своему назначению аналогична флагу + #X502_OUT_CYCLE_FLAGS_WAIT_DONE в описанных выше функциях, но позволяет + выполнить ожидание вручную (с проверкой других условий). + + Функция доступна в библиотеки, начиная с версии 1.1.2, при этом для работы + функции необходима версия прошивки ARM не ниже 1.0.2 для модуля E-502 или + версия драйвера не ниже 1.0.9 для L-502. В отличие от флага, если данные условия + не выполняются, то функция явно вернет ошибку #X502_ERR_NOT_SUP_BY_FIRMWARE + или #X502_ERR_NOT_SUP_BY_DRIVER. + + Ожидание завершения может быть необходимо при вызове X502_OutCycleSetup() + при загрузке первого сигнала до вызова X502_StreamsStart(), если требуется, + чтобы выдача первых отсчетов на ЦАП совпала по времени с моментом запуска ввода, + т.к. иначе загрузка сигнала в модуль может не завершиться к моменту запуска + и выдача сигнала начнется с задержкой (актуально для E502). + + При последующих вызовах X502_OutCycleSetup() для смены уже установленного + сигнала установка считается завершенной после завершения загрузки сигнала и + непосредственной смены выдаваемого сигнала. Эту проверку можно использовать, + чтобы явно узнать, что смена сигнала завершилась и можно уже загружать + следующий циклический сигнал. + + При вызове X502_OutCycleStop() ожидание завершения может использоваться + перед вызовом X502_StreamsStop(), чтобы убедится (если это необходимо), что + генерация была завершена именно на последней точке загруженного циклического + сигнала. + + @param[in] hnd Описатель модуля. + @param[out] done 0, если есть незавершенный запрос на установку или останов + циклического сигнала, 1 --- в противном случае (включая + случай, когда выдача циклического сигнала вообще не ведется) + @return Код ошибки. + ****************************************************************************/ +X502_EXPORT(int32_t) X502_OutCycleCheckSetupDone(t_x502_hnd hnd, uint32_t *done); + + + +/***************************************************************************//** + @brief Чтение флагов статуса вывода + + Функция читает значения флагов статуса синхронного вывода из регистра статуса. + В частности по флагу #X502_OUT_STATUS_FLAG_BUF_WAS_EMPTY можно проверить, + не было ли опустошения буфера с момента запуска синхронного вывода, + чтобы убедиться, что не было разрыва сигнала из-за неподкаченных вовремя + данных. + + @param[in] hnd Описатель модуля. + @param[out] status Флаги статуса --- набор битов из #t_x502_out_status_flags, + объединенных через логическое “ИЛИ”. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_OutGetStatusFlags(t_x502_hnd hnd, uint32_t *status); + + +/***************************************************************************//** + @brief Установка размера буфера для синхронного ввода или вывода + + Функция устанавливает размер буфера, который используется для временного + хранения данных на прием или на передачу. + Предназначена для случаев, когда пользователя по каким-либо причинам не + удовлетворяет рассчитываемое библиотекой значение по умолчанию. + + Если функцией X502_SetStreamBufSize был установлен не нулевой размер буфера, + то этот размер будет использоваться начиная с первого вызова X502_StreamsStart или + X502_StreamsEnable и пересчитываться далее не будет, чтобы библиотека сама + рассчитала размер буфера надо вызвать X502_SetStreamBufSize с нулевым размером + и он рассчитается при первом X502_StreamsStart или X502_StreamsEnable. + + @param[in] hnd Описатель модуля. + @param[in] ch Определяет, устанавливается размер буфера на ввод + или на вывод (значение из #t_x502_stream_ch). + @param[in] size Размер буфера в 32-битных отсчетах, + 0 - библиотека будет сама рассчитывать значение по умолчанию + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_SetStreamBufSize(t_x502_hnd hnd, uint32_t ch, uint32_t size); + +/***************************************************************************//** + @brief Установка шага при передаче потока на ввод или вывод + + Функция устанавливает шаг передачи данных (шаг генерации прерываний + для PCI-Express или размер запроса для USB) при передаче синхронного + потока данных на ввод или на вывод. + Данная функция предназначена для пользователей, которых не устроит + автоматически рассчитываемое библиотекой значение. + + @param[in] hnd Описатель модуля. + @param[in] dma_ch Определяет, шаг передачи устанавливается на ввод + или на вывод (значение из #t_x502_stream_ch). + @param[in] step Шаг прерывания в 32-битных отсчетах + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_SetStreamStep(t_x502_hnd hnd, uint32_t dma_ch, uint32_t step); + +/** @} */ + + + +/***************************************************************************//** + @addtogroup func_dsp Функции для работы с сигнальным процессором + @{ +*******************************************************************************/ +/***************************************************************************//** + @brief Загрузка прошивки сигнального процессора BlackFin + + Функция загружает прошивку сигнального процессора из указанного файла в + процессор и запускает ее, проверяет правильность загрузки путем получения + версии прошивки (через специальную команду). + Прошивка должна быть в бинарном формате LDR. + @param[in] hnd Описатель модуля. + @param[in] filename Имя файла с загружаемой прошивкой. + @return Код ошибки. + *****************************************************************************/ +X502_EXPORT(int32_t) X502_BfLoadFirmware(t_x502_hnd hnd, const char* filename); + + + +/***************************************************************************//** + @brief Проверка, загружена ли прошивка BlackFin + + Функция передает команды процессору BlackFin для получения версии прошивки и + ее состояния. Успешное выполнение команд свидетельствует о том, что в + BlackFin загружена действительная прошивка. + Кроме того прошивке передается информация о модуле (наличие опций, версия + ПЛИС и т.д.) для внутреннего использования. + В случае успеха модуль переводится в режим DSP. + + Данная функция может служить для проверки, была ли загружена прошивка раньше + (чтобы не загружать повторно) или для проверки была ли она загружена через + JTAG-интерфейс. + + @param[in] hnd Описатель модуля. + @param[out] version Если указатель не нулевой, то в данной переменной + возвращается версия прошивки BlackFin в случае + успешной проверки. + @return Код ошибки. + *****************************************************************************/ +X502_EXPORT(int32_t) X502_BfCheckFirmwareIsLoaded(t_x502_hnd hnd, uint32_t *version); + +/***************************************************************************//** + @brief Чтение блока данных из памяти сигнального процессора + + Функция считывает блок данных напрямую из памяти процессора. Может быть + прочитаны данные, как из внутренней памяти (L1), так и из внешней SDRAM. + Для выполнения этой функции в BlackFin должна быть загружена его прошивка. + + Функция предназначена в первую очередь для пользователей, пишущих свою + программу для сигнального процессора. + + @param[in] hnd Описатель модуля. + @param[in] addr Адрес памяти, начиная с которого будет считан блок + данных. + @param[out] regs Массив, в который будут сохранено прочитанное + содержимое памяти. + @param[in] size Количество считываемых 32-битных слов. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_BfMemRead(t_x502_hnd hnd, uint32_t addr, uint32_t* regs, + uint32_t size); + +/***************************************************************************//** + @brief Запись блока данных в память сигнального процессора + + Функция записывает блок данных напрямую в памяти процессора BlackFin. Блок + данных должен быть всегда кратен 8 32-битным словам (32 байтам). + Запись может осуществляться как во внутреннюю память (L1), так и во внешнюю SDRAM. + Для выполнения этой функции в BlackFin должна быть загружена его прошивка. + + Функция предназначена в первую очередь для пользователей, пишущих свою + программу для сигнального процессора. + + @note Следует быть осторожным, т.к. запись в область данных, используемую + программой может привести к ее неработоспособности. + + @param[in] hnd Описатель модуля. + @param[in] addr Адрес памяти, начиная с которого будет записан блок + данных. + @param[out] regs Массив с данными для записи в сигнальный процессор. + @param[in] size Количество записываемых данных в 32-битных словах + (должно быть кратно 8). + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_BfMemWrite(t_x502_hnd hnd, uint32_t addr, + const uint32_t* regs, uint32_t size); + + +/***************************************************************************//** + @brief Передача управляющей команды сигнальному процессору + + Функция предназначена для передачи пользовательских управляющих команд + процессору для пользователей, пишущих свою прошивку BlackFin. + + Управление работой сигнального процессора штатным образом осуществляется + через управляющие команды, которые записываются в специальную область + памяти сигнального процессора. Сигнальный процессор обрабатывает команду + и по завершению записывает в эту же область результат. + + Команды деляться на стандартные, которые используются библиотекой x502api и + реализованы в штатной прошивке сигнального процессора и пользовательские, + которые пользователь может определять по своему усмотрению. + Пользовательские команды начинаются с кода X502_BF_CMD_CODE_USER (0x8000). + + @param[in] hnd Описатель модуля. + @param[in] cmd_code Код команды - определяет, что за команда выполняется. + @param[in] par Параметр, передаваемый с командой (значение зависит + от кода команды). + @param[in] snd_data Опциональные данные, передаваемые вместе с командой. + Если данные не передаются, то должен передаваться + нулевой указатель и snd_size = 0. + @param[in] snd_size Количество 32-битных слов, передаваемых в snd_data + @param[out] rcv_data Массив, в который будут переданы данные, возвращенные + процессором по завершению команды. Если данные не + должны возвращаться, то должен передаваться нулевой + указатель, а rcv_size = 0. + @param[in] rcv_size Количество 32-битных слов, которое ожидается, что + вернет процессор по выполнению команды. Массив + rcv_data должен быть рассчитан на данное количество + слов. + @param[in] tout Таймаут в течении которого будет ожидаться, когда + процессор завершит выполнение команды. Функция + возвратит управление либо по завершению команды, + либо по таймауту. + @param[out] recvd_size Если не является нулевым указателем, то в эту + переменную будет сохранено количество 32-битных слов, + которое реально вернул процессор после выполнения + команды (процессор имеет право вернуть меньше данных, + чем запрашивалось в rcv_size). + @return Код ошибки. Если процессор выполнил команду с ненулевым + кодом завершения, то этот код и будет возвращен + функцией. +*******************************************************************************/ +X502_EXPORT(int32_t) X502_BfExecCmd(t_x502_hnd hnd, uint16_t cmd_code, uint32_t par, + const uint32_t* snd_data, uint32_t snd_size, + uint32_t* rcv_data, uint32_t rcv_size, + uint32_t tout, uint32_t* recvd_size); +/** @} */ + + + + + + +/***************************************************************************//** + @addtogroup func_flash Функции для работы с Flash-памятью модуля + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Чтение блока данных из Flash-памяти + + Функция считывает массив данных из Flash-памяти модуля в массив, переданный + пользователем. Для считывания не нужно специальное разрешение - оно доступно + всегда. + @param[in] hnd Описатель модуля. + @param[in] addr Адрес начала блока. + @param[out] data Массив, куда будут сохранены считанные данные + (должен быть не меньше size байт). + @param[in] size Количество байт для чтения. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_FlashRead(t_x502_hnd hnd, uint32_t addr, uint8_t* data, + uint32_t size); +/***************************************************************************//** + @brief Запись блока данных во Flash-память модуля + + Функция записывает переданный массив данных во Flash-память модуля. + Эта область должна быть предварительно стерта с помощью X502_FlashErase() и + до начала изменения должна быть вызвана функция X502_FlashWriteEnable(), + чтобы разрешить любое изменение содержимого Flash-памяти. + Пользователю для записи доступны только первые #X502_FLASH_USER_SIZE байт + Flash-памяти. + @param[in] hnd Описатель модуля. + @param[in] addr Адрес начала блока. + @param[in] data Массив с записываемыми данными (должен быть не меньше + size байт). + @param[in] size Количество байт для записи. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_FlashWrite(t_x502_hnd hnd, uint32_t addr, + const uint8_t* data, uint32_t size); +/***************************************************************************//** + @brief Стирание блока во Flash-памяти + + Функция стирает блок во Flash-памяти модуля (все ячейки будут читаться как + 0xFF). Адрес и размер должны быть кратны 4096 байт! + Перед вызовом этой функции должна быть разрешена запись + в пользовательскую область с помощью X502_FlashWriteEnable(). + @param[in] hnd Описатель модуля. + @param[in] addr Адрес начала блока (должен быть кратен 4K). + @param[in] size Количество байт для стирания (кратно 4K). + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_FlashErase(t_x502_hnd hnd, uint32_t addr, uint32_t size); +/***************************************************************************//** + @brief Разрешение записи в пользовательскую область Flash-памяти + + Функция разрешает запись в пользовательскую область Flash-памяти (первые + #X502_FLASH_USER_SIZE байт). Должна быть вызвана до того, как + можно будет использовать X502_FlashErase() и X502_FlashWrite() для изменения + содержимого пользовательской области памяти. После завершения изменений + следует вызвать X502_FlashWriteDisable(). + @param[in] hnd Описатель модуля. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_FlashWriteEnable(t_x502_hnd hnd); +/***************************************************************************//** + @brief Запрет записи в пользовательскую область Flash-памяти + + Функция запрещает запись в пользовательскую область Flash-памяти модуля + (первые #X502_FLASH_USER_SIZE байт). Должна быть вызвана после того, как + нужные данные в пользовательской области были изменены с помощью + X502_FlashErase() и X502_FlashWrite(), чтобы защитить пользовательскую + область от случайной изменения в дальнейшем. + @param[in] hnd Описатель модуля. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_FlashWriteDisable(t_x502_hnd hnd); + +/** @} */ + + + + + +/***************************************************************************//** + @addtogroup func_misc Дополнительные вспомогательные функции + @{ +*******************************************************************************/ +/**************************************************************************//** + @brief Получить версию библиотеки + + Функция возвращает версию библиотеки x502api. + Версия возвращается в виде 32-битного числа. + Строковое представление возвращенной версии --- четыре числа, + старшее соответствует старшему байту, младшее --- младшему. + + Старший байт --- мажорная версия, второй по старшинству байт --- минорная, + третий --- ревизия, четвертый --- номер сборки (не используется --- всегда 0) + + @return 32-битное число, представляющее собой версию библиотеки + *****************************************************************************/ +X502_EXPORT(uint32_t) X502_GetLibraryVersion(void); + +/***************************************************************************//** + @brief Получение строки об ошибке + + Функция возвращает строку, соответствующую переданному коду ошибки. + В настоящее время возвращается всегда русская версия строки (возможно в + будущем будет возможность сменить язык глобальной функцией). + + @note Следует учесть, что в ОС Windows строка возвращается в + стандартной для Windows кодировке CP1251, в то время как в Linux + используется кодировка UTF-8. + @param[in] err Код ошибки, для которого нужно вернуть строку. + @return Указатель на строку, соответствующую коду ошибки + ******************************************************************************/ +X502_EXPORT(const char*) X502_GetErrorString(int32_t err); + +/***************************************************************************//** + @brief Моргание светодиодом + + При вызове этой функции, если не запущен синхронный ввод/вывод, происходит + кратковременное затухание красного цвета светодиода на передней панели + L-502 или светодиода LED1 модуля E-502. + Может быть использована для визуальной идентификации модуля после его + открытия. + + При запущенном синхронном вводе/выводе данный светодиод всегда + горит зеленым цветом и данная функция не влияет на его состояние. + + @param[in] hnd Описатель модуля. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_LedBlink(t_x502_hnd hnd); + + +/***************************************************************************//** + @brief Установка подтягивающих резисторов на входных линиях + + Функция может использоваться для включения подтягивающих резисторов на + цифровых входах. Для разных модулей подтягивающие резисторы реализованы + на разных входах. Для модулей E-502 и L-502 можно включать их на линиях SYN1 и SYN2. + Для L-502 можно отдельно задавать включены или отключены подтяжки + на младшей или старшей половине цифровых линий. Для E-502 можно включить + подтягивающие к нулю резисторы на входах межмодульной синхронизации. + Для E-16 линий TRIG и INT могут быть как входом, так и выходом для сигналов START и CONV. + + На не указанных линиях подтягивающие резисторы будут отключены, + если они были включены до этого. + + При включении питания все подтягивающие резисторы отключены. + + @param[in] hnd Описатель модуля. + @param[in] pullups Флаги (из #t_x502_pullups), определяющие, на каких + линиях включены подтягивающие резисторы. + @return Код ошибки. + ******************************************************************************/ +X502_EXPORT(int32_t) X502_SetDigInPullup(t_x502_hnd hnd, uint32_t pullups); + + +/***************************************************************************//** + @brief Проверка поддержки модулем заданной возможности + + Функция используется для проверки, поддерживается ли определенная возможность + из #t_x502_features для данного модуля с текущими версиями прошивок. + + Если возможность поддерживается, то будет возвращен код #X502_ERR_OK. + + Данная функция доступна в библиотеке версии 1.1.6 или выше. + + @param[in] hnd Описатель модуля. + @param[in] feature Значение из #t_x502_features, определяющие, какую + возможность необходимо проверить. + @return Если возможность поддерживается, то возвращается + #X502_ERR_OK, иначе --- код ошибки + ******************************************************************************/ +X502_EXPORT(int32_t) X502_CheckFeature(t_x502_hnd hnd, uint32_t feature); + +/** @} */ +/** @} */ + +#ifdef __cplusplus +} +#endif + + +#endif // X502API_H diff --git a/x502/x502api_async.c b/x502/x502api_async.c new file mode 100644 index 0000000..5a3e39b --- /dev/null +++ b/x502/x502api_async.c @@ -0,0 +1,143 @@ +#include "x502api_private.h" +#include "ltimer.h" + +uint32_t prepare_dac_wrd(t_x502_hnd hnd, double val, uint32_t flags, const t_x502_cbr_coef* coef); + + +X502_EXPORT(int32_t) X502_AsyncOutDac(t_x502_hnd hnd, uint32_t ch, double data, uint32_t flags) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (ch!=X502_DAC_CH1) && (ch!=X502_DAC_CH2)) + err = X502_ERR_INVALID_DAC_CHANNEL; + if (!err && !(hnd->info.devflags & X502_DEVFLAGS_DAC_PRESENT)) + err = X502_ERR_DAC_NOT_PRESENT; + if (!err) { + uint32_t wr_val = prepare_dac_wrd(hnd, data, flags, &hnd->info.cbr.dac[ch]); + + if (ch==X502_DAC_CH1) { + wr_val |= X502_STREAM_OUT_WORD_TYPE_DAC1; + } else { + wr_val |= X502_STREAM_OUT_WORD_TYPE_DAC2; + } + + if (hnd->mode == X502_MODE_FPGA) { + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_ASYNC_OUT, wr_val); + } else if (hnd->mode == X502_MODE_DSP) { + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_ASYNC_OUT, ch==X502_DAC_CH1 ? + L502_BF_CMD_ASYNC_TYPE_DAC1 : L502_BF_CMD_ASYNC_TYPE_DAC2, + (uint32_t*)&wr_val, 1, NULL, 0, X502_BF_CMD_DEFAULT_TOUT, NULL); + } else { + err = X502_ERR_INVALID_MODE; + } + } + return err; +} + + +X502_EXPORT(int32_t) X502_AsyncOutDig(t_x502_hnd hnd, uint32_t val, uint32_t msk) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err) { + if(hnd->mode == X502_MODE_FPGA) { + err = osspec_mutex_lock(hnd->mutex_cfg, X502_MUTEX_CFG_LOCK_TOUT); + if (!err) { + int32_t release_err; + /* маскированные биты берем из последнего выведенного значения */ + if (msk) { + val = val & ~msk; + val |= hnd->last_dout & msk; + } + + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_ASYNC_OUT, + val | X502_STREAM_OUT_WORD_TYPE_DOUT); + /* сохраняем выведенное значения, для последующего использования + в маске */ + if (!err) + hnd->last_dout = val; + + release_err = osspec_mutex_release(hnd->mutex_cfg); + if (!err) + err = release_err; + } + } else if (hnd->mode == X502_MODE_DSP) { + uint32_t wrds[2] = {val, msk}; + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_ASYNC_OUT, L502_BF_CMD_ASYNC_TYPE_DOUT, + wrds, 2, NULL, 0, X502_BF_CMD_DEFAULT_TOUT, NULL); + } else { + err = X502_ERR_INVALID_MODE; + } + } + return err; +} + + +static int32_t f_read_digin(t_x502_hnd hnd, uint32_t* din) { + int32_t err = 0; + uint32_t val; + int rdy=0; + t_ltimer tmr; + + /* читаем состояние входов, чтобы сбросить флаг готовности */ + if (!x502_is_E16(hnd)) { + err = hnd->iface_hnd->fpga_reg_read(hnd, X502_REGS_IOARITH_DIN_ASYNC, &val); + } + + /* читаем синхронный ввод до готовности данных или пока не + выйдем по таймауту*/ + ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(500)); + while (!rdy && !ltimer_expired(&tmr) && (err == X502_ERR_OK)) { + err = hnd->iface_hnd->fpga_reg_read(hnd, X502_REGS_IOARITH_DIN_ASYNC, &val); + if (!err && (val & 0x80000000)) { + rdy = 1; + *din = (val & 0x3FFFF); + } + } + + if (!err && !rdy) + err = X502_ERR_DIG_IN_NOT_RDY; + return err; +} + + + +X502_EXPORT(int32_t) X502_AsyncInDig(t_x502_hnd hnd, uint32_t* din) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err & (din==NULL)) + err = X502_ERR_INVALID_POINTER; + if (!err) + err = osspec_mutex_lock(hnd->mutex_cfg, X502_MUTEX_CFG_LOCK_TOUT); + if (!err) { + if (x502_is_E16(hnd)) { + err = f_read_digin(hnd, din); + } else if (hnd->mode == X502_MODE_FPGA) { + if (hnd->flags & PRIV_FLAGS_STREAM_RUN) { + err = f_read_digin(hnd, din); + } else { + /* запрещаем прием данных */ + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOARITH_IN_STREAM_ENABLE, 0); + if (!err) { + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_PRELOAD_ADC, 1); + if (!err) { + /* запускаем чтение цифровых входов */ + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_GO_SYNC_IO, 1); + } + } + + if (!err) { + int32_t stop_err; + err = f_read_digin(hnd, din); + + /* останавливаем сбор данных */ + stop_err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_GO_SYNC_IO, 0); + if (!err) + err = stop_err; + } + } + } else if (hnd->mode == X502_MODE_DSP) { + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_ASYNC_DIG_IN, + 0, NULL, 0, din, 1, X502_BF_CMD_DEFAULT_TOUT, NULL); + } else { + err = X502_ERR_INVALID_MODE; + } + osspec_mutex_release(hnd->mutex_cfg); + } + return err; +} diff --git a/x502/x502api_bf.c b/x502/x502api_bf.c new file mode 100644 index 0000000..53dc3ef --- /dev/null +++ b/x502/x502api_bf.c @@ -0,0 +1,391 @@ +#include "x502api_private.h" +#include "ltimer.h" + +#include +#include +#include + +#define BF_LDR_HDR_SIZE (16) +#define BF_LDR_HDRSGN (0xAD) + +#define BF_LDR_HDRPOS_SGN (3) + +#define BF_LDR_FLAG_SAVE (0x0010) //не используется +#define BF_LDR_FLAG_AUX (0x0020) //не используется +#define BF_LDR_FLAG_FILL (0x0100) +#define BF_LDR_FLAG_QUICKBOOT (0x0200) //не используется +#define BF_LDR_FLAG_CALLBACK (0x0400) //не используется +#define BF_LDR_FLAG_INIT (0x0800) //не используется +#define BF_LDR_FLAG_IGNORE (0x1000) +#define BF_LDR_FLAG_INDIRECT (0x2000) //не используется +#define BF_LDR_FLAG_FIRST (0x4000) +#define BF_LDR_FLAG_FINAL (0x8000) + +#define BF_WAIT_LOAD_RDY_TOUT 500 +#define BF_MUTEX_LOCK_TOUT 1000 + +#define LDR_BUFF_SIZE 4096 + + + +#define BF_CHECK_ADDR(addr) (((addr) < 0xFFA0C000) && ((addr)>= 0xFFA0000)) || \ + (((addr) < 0xFF908000) && ((addr) >=0xFF900000)) || \ + (((addr) < 0xFF808000) && ((addr) >=0xFF800000)) || \ + (((addr) < 0x2000000)) ? 0 : X502_ERR_BF_INVALID_ADDR + +#define BF_CHECK_ADDR_SIZE(addr, size) BF_CHECK_ADDR(addr) ? X502_ERR_BF_INVALID_ADDR : \ + BF_CHECK_ADDR(addr+size*4-1) ? X502_ERR_BF_INVALID_ADDR : 0 + + +#define BF_CMD_FIRST_DATA_BLOCK_SIZE ((X502_BF_REQ_DATA_SIZE_MAX-offsetof(t_l502_bf_cmd, data))/4) + +typedef struct st_bf_ldr_pkt { + uint8_t res; + uint8_t dma_mode; + uint16_t flags; + uint32_t addr; + uint32_t size; + uint32_t arg; +} t_bf_ldr_pkt; + +/* Разбираем заголовок блока LDR-формата из буфера размером BF_LDR_HDR_SIZE + и сохраняем параметры в структуре pkt */ +int f_parse_ldr_hdr(const uint8_t* hdr, t_bf_ldr_pkt* pkt) { + int err = 0; + uint32_t* pdw_buff = (uint32_t*)hdr; + uint8_t xor_ch = 0; + int i; + for (i=0; i < BF_LDR_HDR_SIZE; i++) { + xor_ch ^= hdr[i]; + } + + if ((xor_ch!=0) || (hdr[BF_LDR_HDRPOS_SGN] != BF_LDR_HDRSGN)) { + err = X502_ERR_LDR_FILE_FORMAT; + } else { + pkt->res = 0; + pkt->dma_mode = pdw_buff[0]&0xF; + pkt->flags = pdw_buff[0]&0xFFF0; + pkt->addr = pdw_buff[1]; + pkt->size = pdw_buff[2]; + pkt->arg = pdw_buff[3]; + + if ((pkt->flags & BF_LDR_FLAG_INIT) && (pkt->flags & BF_LDR_FLAG_FILL)) + err = X502_ERR_LDR_FILE_FORMAT; + else if (pkt->flags & (BF_LDR_FLAG_CALLBACK | BF_LDR_FLAG_INDIRECT | BF_LDR_FLAG_INIT)) + err = X502_ERR_LDR_FILE_UNSUP_FEATURE; + else if ((pkt->flags & BF_LDR_FLAG_INIT) && (pkt->addr != 0xFFA00000)) + err = X502_ERR_LDR_FILE_UNSUP_STARTUP_ADDR; + } + return err; +} + +static int32_t f_bf_mem_wr(t_x502_hnd hnd, uint32_t addr, const uint32_t* regs, uint32_t size) { + int32_t err = 0; + + /* данные записываем блоками по L502_BF_REQ_DATA_SIZE */ + while (!err && size) { + int put_size = (size < hnd->iface_hnd->bf_mem_block_size) ? size : + hnd->iface_hnd->bf_mem_block_size; + err = hnd->iface_hnd->bf_mem_block_wr(hnd, addr, regs, put_size); + if (!err) { + size -= put_size; + regs += put_size; + addr += put_size*4; + } + } + return err; +} + +static int32_t f_bf_mem_rd(t_x502_hnd hnd, uint32_t addr, uint32_t* regs, uint32_t size) { + int err = 0; + while (!err && size) { + int get_size = (size < hnd->iface_hnd->bf_mem_block_size) ? size : + hnd->iface_hnd->bf_mem_block_size; + + err = hnd->iface_hnd->bf_mem_block_rd(hnd, addr, regs, get_size); + + if (!err) { + size -= get_size; + regs += get_size; + addr += get_size*4; + } + } + return err; +} + +int32_t bf_fpga_reg_wr(t_x502_hnd hnd, uint32_t addr, uint32_t val) { + int32_t err = hnd->mode != X502_MODE_DSP ? X502_ERR_INVALID_MODE : + (hnd->bf_features & L502_BF_FEATURE_FPGA_REG_ACCESS) ? + X502_ERR_OK : X502_ERR_NOT_IMPLEMENTED; + if (err == X502_ERR_OK) { + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_FPGA_REG_WR, addr, &val, 1, NULL, 0, + X502_BF_CMD_DEFAULT_TOUT, NULL); + } + return err; +} + +int32_t bf_fpga_reg_rd(t_x502_hnd hnd, uint32_t addr, uint32_t* val) { + int32_t err = hnd->mode != X502_MODE_DSP ? X502_ERR_INVALID_MODE : + (hnd->bf_features & L502_BF_FEATURE_FPGA_REG_ACCESS) ? + X502_ERR_OK : X502_ERR_NOT_IMPLEMENTED; + if (err == X502_ERR_OK) { + uint32_t recvd; + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_FPGA_REG_RD, addr, NULL, 0, val, 1, + X502_BF_CMD_DEFAULT_TOUT, &recvd); + if ((err == X502_ERR_OK) && (recvd < 1)) { + err = X502_ERR_BF_CMD_RETURN_INSUF_DATA; + } + } + return err; +} + + + + + +static int32_t f_check_bf_firm(t_x502_hnd hnd) { + int32_t err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_BF_CMD, X502_BF_CMD_HDMA_RST); + if (!err) { + uint32_t rcv_wrds[2], recvd; + + /* Проверяем версию прошивки BlackFin */ + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_GET_PARAM, L502_BF_PARAM_FIRM_VERSION, + NULL, 0, rcv_wrds, 2, X502_BF_CMD_DEFAULT_TOUT, &recvd); + if (!err) { + if (recvd >= 1) { + hnd->bf_ver = rcv_wrds[0]; + + } else { + err = X502_ERR_BF_CMD_RETURN_INSUF_DATA; + } + hnd->bf_features = recvd >= 2 ? rcv_wrds[1] : 0; + } + } + + /* Проверка состояния прошивки (запущен поток сбора или нет) */ + if (!err) { + uint32_t mode, streams; + + err = x502_bf_get_par(hnd, L502_BF_PARAM_STREAM_MODE, &mode, 1); + if (!err) + err = x502_bf_get_par(hnd, L502_BF_PARAM_ENABLED_STREAMS, &streams, 1); + if (!err) { + err = osspec_mutex_lock(hnd->mutex_cfg, BF_MUTEX_LOCK_TOUT); + if (!err) { + if (mode==L502_BF_MODE_IDLE) { + hnd->flags &= ~PRIV_FLAGS_STREAM_RUN; + } else { + hnd->flags |= PRIV_FLAGS_STREAM_RUN; + } + hnd->streams = streams; + osspec_mutex_release(hnd->mutex_cfg); + } + } + } + + + /* передаем информацию о модуле */ + if (!err && !(hnd->flags & PRIV_FLAGS_STREAM_RUN)) { + uint32_t put_wrds[3]; + uint32_t ch; + put_wrds[0] = hnd->info.devflags; + put_wrds[1] = hnd->info.fpga_ver | ((uint32_t)hnd->info.plda_ver<<16); + err = x502_bf_set_par(hnd, L502_BF_PARAM_MODULE_INFO, put_wrds, 2); + + for (ch=0; !err && (ch < X502_DAC_CH_CNT); ch++) { + float* pk = (float*)&put_wrds[1]; + float* po = (float*)&put_wrds[2]; + put_wrds[0] = ch; + *pk = (float)hnd->info.cbr.dac[ch].k; + *po = (float)hnd->info.cbr.dac[ch].offs; + err = x502_bf_set_par(hnd, L502_BF_PARAM_DAC_COEF, put_wrds, 3); + } + } + + if (!err && (hnd->mode!=X502_MODE_DSP)) + err = X502_SetMode(hnd, X502_MODE_DSP); + + return err; +} + + + + +X502_EXPORT(int32_t) X502_BfCheckFirmwareIsLoaded(t_x502_hnd hnd, uint32_t *version) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err) { + err = f_check_bf_firm(hnd); + if (!err && version) + *version = hnd->bf_ver; + } + return err; +} + +X502_EXPORT(int32_t) X502_BfLoadFirmware(t_x502_hnd hnd, const char* filename) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + + + if (!err && !(hnd->info.devflags & X502_DEVFLAGS_BF_PRESENT)) + err = X502_ERR_BF_NOT_PRESENT; + + + + if (!err) { + err = osspec_mutex_lock(hnd->mutex_bf, BF_MUTEX_LOCK_TOUT); + if (!err) { + err = hnd->iface_hnd->bf_firm_load(hnd, filename); + osspec_mutex_release(hnd->mutex_bf); + } + } + + if (!err) { + SLEEP_MS(100); + err = f_check_bf_firm(hnd); + } + + return err; +} + + + + +X502_EXPORT(int32_t) X502_BfMemWrite(t_x502_hnd hnd, uint32_t addr, + const uint32_t* regs, uint32_t size) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && !(hnd->info.devflags & X502_DEVFLAGS_BF_PRESENT)) + err = X502_ERR_BF_NOT_PRESENT;; + if (!err) + err = BF_CHECK_ADDR_SIZE(addr, size); + if (!err) + err = osspec_mutex_lock(hnd->mutex_bf, BF_MUTEX_LOCK_TOUT); + + if (!err) { + int32_t release_err; + err = f_bf_mem_wr(hnd, addr, regs, size); + release_err = osspec_mutex_release(hnd->mutex_bf); + if (!err) + err = release_err; + } + return err; +} + + +X502_EXPORT(int32_t) X502_BfMemRead(t_x502_hnd hnd, uint32_t addr, + uint32_t* regs, uint32_t size) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && !(hnd->info.devflags & X502_DEVFLAGS_BF_PRESENT)) + err = X502_ERR_BF_NOT_PRESENT; + if (!err) + err = BF_CHECK_ADDR_SIZE(addr,size); + if (!err) + err = osspec_mutex_lock(hnd->mutex_bf, BF_MUTEX_LOCK_TOUT); + if (!err) { + int32_t release_err; + err = f_bf_mem_rd(hnd, addr, regs, size); + release_err = osspec_mutex_release(hnd->mutex_bf); + if (!err) + err = release_err; + } + return err; +} + +static int f_bf_start_cmd(t_x502_hnd hnd, uint16_t cmd_code, uint32_t par, + const uint32_t* data, uint32_t size) { + int err = X502_CHECK_HND_OPENED(hnd); + if (!err && !(hnd->info.devflags & X502_DEVFLAGS_BF_PRESENT)) + err = X502_ERR_BF_NOT_PRESENT; + if (size > L502_BF_CMD_DATA_SIZE_MAX) + err = X502_ERR_BF_INVALID_CMD_DATA_SIZE; + if (!err) + err = osspec_mutex_lock(hnd->mutex_bf, BF_MUTEX_LOCK_TOUT); + + if (!err) { + t_l502_bf_cmd cmd; + cmd.code = cmd_code; + cmd.data_size = size; + cmd.param = par; + cmd.result = 0; + cmd.status = L502_BF_CMD_STATUS_REQ; + + + + if (size!=0) { + memcpy(cmd.data, data, size*sizeof(data[0])); + } + + /* если размер больше, чем можем записать за раз, то сперва записываем + * хвост, чтобы не получилось, что информация о команде будет записана + * раньше данных */ + if (size > BF_CMD_FIRST_DATA_BLOCK_SIZE) { + err = f_bf_mem_wr(hnd, X502_BF_MEMADDR_CMD+X502_BF_REQ_DATA_SIZE_MAX*4, + &cmd.data[BF_CMD_FIRST_DATA_BLOCK_SIZE], + size - BF_CMD_FIRST_DATA_BLOCK_SIZE); + size = BF_CMD_FIRST_DATA_BLOCK_SIZE; + } + + if (!err) { + err = f_bf_mem_wr(hnd, X502_BF_MEMADDR_CMD, (const uint32_t*)&cmd, + (offsetof(t_l502_bf_cmd, data))/4 + size); + } + + if (err) { + osspec_mutex_release(hnd->mutex_bf); + } + } + return err; +} + +static int f_bf_cmd_check(t_x502_hnd hnd, int32_t *res) { + t_l502_bf_cmd cmd; + int err = f_bf_mem_rd(hnd, X502_BF_MEMADDR_CMD, (uint32_t*)&cmd, + X502_BF_REQ_DATA_SIZE_MIN); + if (!err) { + if (cmd.status != L502_BF_CMD_STATUS_DONE) { + err = X502_ERR_BF_CMD_IN_PROGRESS; + } else if (res) { + *res = cmd.result; + } + } + + if (err != X502_ERR_BF_CMD_IN_PROGRESS) + osspec_mutex_release(hnd->mutex_bf); + + return err; +} + +X502_EXPORT(int32_t) X502_BfExecCmd(t_x502_hnd hnd, uint16_t cmd_code, uint32_t par, + const uint32_t* snd_data, uint32_t snd_size, + uint32_t* rcv_data, uint32_t rcv_size, uint32_t tout, + uint32_t* recvd_size) { + t_l502_bf_cmd cmd; + int done = 0; + t_ltimer tmr; + int err; + + ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout)); + err = f_bf_start_cmd(hnd, cmd_code, par, snd_data, snd_size); + while (!err && !done) { + err = f_bf_cmd_check(hnd, &cmd.result); + if (!err) { + done = 1; + if (rcv_size != 0) { + err = X502_BfMemRead(hnd, X502_BF_MEMADDR_CMD, (uint32_t*)&cmd, + (offsetof(t_l502_bf_cmd, data))/4 + rcv_size); + memcpy(rcv_data, cmd.data, rcv_size*sizeof(rcv_data[0])); + if (recvd_size!=NULL) + *recvd_size = cmd.data_size; + } + if (!err) + err = cmd.result; + } else if (err==X502_ERR_BF_CMD_IN_PROGRESS) { + if (ltimer_expired(&tmr)) { + err = X502_ERR_BF_CMD_TIMEOUT; + osspec_mutex_release(hnd->mutex_bf); + } else { + err = 0; + SLEEP_MS(20); + } + } + } + return err; +} diff --git a/x502/x502api_config.c b/x502/x502api_config.c new file mode 100644 index 0000000..066c72c --- /dev/null +++ b/x502/x502api_config.c @@ -0,0 +1,795 @@ +#include "x502api_private.h" +#include "x502_fpga_regs.h" + +#define SYNC_FREQ_CODE(mode) ((mode == X502_SYNC_DI_SYN2_RISE) ? X502_SYNC_DI_SYN1_FALL : \ + (mode == X502_SYNC_DI_SYN1_FALL) ? X502_SYNC_DI_SYN2_RISE : mode) + +#define CHECK_SYNC_MODE(mode) (((mode)) != X502_SYNC_INTERNAL) \ + && ((mode)!=X502_SYNC_EXTERNAL_MASTER) \ + && ((mode)!=X502_SYNC_DI_SYN1_RISE) \ + && ((mode)!=X502_SYNC_DI_SYN2_RISE) \ + && ((mode)!=X502_SYNC_DI_SYN1_FALL) \ + && ((mode)!=X502_SYNC_DI_SYN2_FALL) \ + ? X502_ERR_INVALID_SYNC_MODE : X502_ERR_OK + +/** Максимальное значения частоты АЦП для варианта E16*/ +#define E16_ADC_FREQ_MAX 1000000 +/** Максимальное значения частоты DIN для варианта E16*/ +#define E16_DIN_FREQ_MAX 1000000 +/** Максимальное значения частоты OUT для варианта E16*/ +#define E16_OUT_FREQ_MAX 1000000 + +static const uint16_t f_regadd_k[X502_ADC_RANGE_CNT] = {X502_REGS_IOARITH_K10, + X502_REGS_IOARITH_K5, + X502_REGS_IOARITH_K2, + X502_REGS_IOARITH_K1, + X502_REGS_IOARITH_K05, + X502_REGS_IOARITH_K02}; + +static const uint16_t f_regadd_offs[X502_ADC_RANGE_CNT] = {X502_REGS_IOARITH_B10, + X502_REGS_IOARITH_B5, + X502_REGS_IOARITH_B2, + X502_REGS_IOARITH_B1, + X502_REGS_IOARITH_B05, + X502_REGS_IOARITH_B02}; + + +static uint32_t f_get_out_freq_div_min(double ref_freq) { + return ref_freq > X502_OUT_FREQ_REF_LF_VAL ? X502_OUT_FREQ_DIV_MIN : X502_OUT_FREQ_DIV_MIN_REF_LF; +} + + +X502_EXPORT(int32_t) X502_SetLChannel(t_x502_hnd hnd, uint32_t lch, uint32_t phy_ch, + uint32_t mode, uint32_t range, uint32_t avg) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + + if (!err) { + /* Для измерения собственного нуля номер канала не имеет значения */ + if (mode == X502_LCH_MODE_ZERO) + phy_ch = 0; + + if ((mode != X502_LCH_MODE_COMM) && (mode != X502_LCH_MODE_DIFF) && + (mode != X502_LCH_MODE_ZERO)) { + err = X502_ERR_INVALID_LCH_MODE; + } else if ((phy_ch >= X502_ADC_COMM_CH_CNT) || + ((mode == X502_LCH_MODE_DIFF) && (phy_ch >= X502_ADC_DIFF_CH_CNT))) { + err = X502_ERR_INVALID_LCH_PHY_NUMBER; + } else if (range >= X502_ADC_RANGE_CNT) { + err = X502_ERR_INVALID_LCH_RANGE; + } else if (avg > X502_LCH_AVG_SIZE_MAX) { + err = X502_ERR_INVALID_LCH_AVG_SIZE; + } + + if (!err) { + hnd->set.lch[lch].ch = phy_ch; + hnd->set.lch[lch].range = range; + hnd->set.lch[lch].mode = mode; + hnd->set.lch[lch].avg = avg; + } + } + return err; +} + +X502_EXPORT(int32_t) X502_SetLChannelCount(t_x502_hnd hnd, uint32_t lch_cnt) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + + if (!err) { + if (lch_cnt > X502_LTABLE_MAX_CH_CNT) { + err = X502_ERR_INVALID_LTABLE_SIZE; + } else if (x502_is_E16(hnd) && lch_cnt > E16_LTABLE_MAX_CH_CNT) { + err = X502_ERR_INVALID_LTABLE_SIZE; + } else { + hnd->set.lch_cnt = lch_cnt; + } + } + return err; +} + +X502_EXPORT(int32_t) X502_GetLChannelCount(t_x502_hnd hnd, uint32_t* lch_cnt) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (lch_cnt==NULL)) + err = X502_ERR_INVALID_POINTER; + + if (!err) + *lch_cnt = hnd->set.lch_cnt; + + return err; +} + +X502_EXPORT(int32_t) X502_SetAdcFreqDivider(t_x502_hnd hnd, uint32_t adc_freq_div) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + + if (!err) { + if ((adc_freq_div==0) || + (x502_is_E16(hnd) && adc_freq_div > E16_ADC_FREQ_DIV_MAX) || + (!x502_is_E16(hnd) && adc_freq_div > X502_ADC_FREQ_DIV_MAX)) { + err = X502_ERR_INVALID_ADC_FREQ_DIV; + } else { + hnd->set.adc_freq_div = adc_freq_div; + } + } + return err; +} + +X502_EXPORT(int32_t) X502_SetAdcInterframeDelay(t_x502_hnd hnd, uint32_t delay) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + + if (!err && (delay > X502_ADC_INTERFRAME_DELAY_MAX)) + err = X502_ERR_INVALID_INTERFRAME_DELAY; + + if (!err) { + hnd->set.adc_frame_delay = delay; + } + return err; +} + +X502_EXPORT(int32_t) X502_SetDinFreqDivider(t_x502_hnd hnd, uint32_t din_freq_div) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + + if (!err) { + if ((din_freq_div==0) || + (x502_is_E16(hnd) && din_freq_div > E16_DIN_FREQ_DIV_MAX) || + (!x502_is_E16(hnd) && din_freq_div > X502_DIN_FREQ_DIV_MAX)) { + err = X502_ERR_INVALID_ADC_FREQ_DIV; + } else { + hnd->set.din_freq_div = din_freq_div; + } + } + return err; +} + +X502_EXPORT(int32_t) X502_SetOutFreqDivider(t_x502_hnd hnd, uint32_t out_freq_div) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + + if (!err) { + /* Проверяем, поддерживается ли возможность установить значение, отличное от стандартного */ + if (out_freq_div != X502_OUT_FREQ_DIV_DEFAULT) { + err = X502_CheckFeature(hnd, X502_FEATURE_OUT_FREQ_DIV); + } + } + + if (!err) { + double ref_freq; + X502_GetRefFreqValue(hnd, &ref_freq); + if ((out_freq_div < f_get_out_freq_div_min(ref_freq)) || + (x502_is_E16(hnd) && out_freq_div > E16_OUT_FREQ_DIV_MAX) || + (!x502_is_E16(hnd) && out_freq_div > X502_OUT_FREQ_DIV_MAX)) { + err = X502_ERR_INVALID_OUT_FREQ_DIV; + } else { + hnd->set.out_freq_div = out_freq_div; + } + } + return err; +} + + + + + + +X502_EXPORT(int32_t) X502_SetMode(t_x502_hnd hnd, uint32_t mode) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + + if (!err && (mode==X502_MODE_DSP) && + !(hnd->info.devflags & X502_DEVFLAGS_BF_PRESENT)) { + err = X502_ERR_BF_NOT_PRESENT; + } + + if (!err) { + uint32_t val; + err = hnd->iface_hnd->fpga_reg_read(hnd, X502_REGS_BF_CTL, &val); + if (!err) { + val &= ~(X502_REGBIT_BF_CTL_CLK_DIV_Msk | X502_REGBIT_BF_CTL_DBG_MODE_Msk | X502_REGBIT_BF_CTL_DSP_MODE_Msk); + if (mode==X502_MODE_DSP) { + /* в DSP режиме сброс должен быть снят с blackfin, иначе не будут работать + команды по HostDMA */ + val |= X502_REGBIT_BF_CTL_DSP_MODE_Msk | X502_REGBIT_BF_CTL_BF_RESET_Msk; + } else if (mode==X502_MODE_DEBUG) { + val |= X502_REGBIT_BF_CTL_DBG_MODE_Msk; + } else if (mode!=X502_MODE_FPGA) { + err = X502_ERR_INVALID_MODE; + } + } + + if (!err) + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_BF_CTL, val); + + /* при переходе в режим DSP сбрасываем автомат HDMA */ + if (!err && (mode==X502_MODE_DSP)) + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_BF_CMD, X502_BF_CMD_HDMA_RST); + + if (!err) + hnd->mode = mode; + } + return err; +} + +X502_EXPORT(int32_t) X502_GetMode(t_x502_hnd hnd, uint32_t* mode) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (mode==NULL)) + err = X502_ERR_INVALID_POINTER; + + if (!err) { + *mode = hnd->mode; + } + return err; +} + +#define IS_E16_FREQ(hnd, ref_freq) ((hnd && x502_is_E16(hnd)) || (uint32_t)ref_freq == E16_REF_FREQ_48000KHZ) + +X502_EXPORT(int32_t) X502_CalcAdcFreq2(t_x502_hnd hnd, double ref_freq, uint32_t lch_cnt, double *f_acq, + double *f_frame, uint32_t *adc_freq_div, uint32_t *adc_frame_delay) { + int32_t err = (f_acq==NULL) ? X502_ERR_INVALID_POINTER : X502_ERR_OK; + if (err == X502_ERR_OK) { + uint32_t cur_adc_freq_div, cur_frame_delay = 0; + uint32_t div_max = X502_ADC_FREQ_DIV_MAX; + + double set_freq = *f_acq; + if (set_freq <= 0) { + if (IS_E16_FREQ(hnd, ref_freq)) { + set_freq = E16_ADC_FREQ_DEFAULT; + } else { + set_freq = ref_freq; + } + } + if (IS_E16_FREQ(hnd, ref_freq)) { + div_max = E16_ADC_FREQ_DIV_MAX; + if (set_freq > E16_ADC_FREQ_MAX) { + set_freq = E16_ADC_FREQ_MAX; + } + } + + cur_adc_freq_div = (uint32_t)(ref_freq/set_freq+0.49); + if (cur_adc_freq_div == 0) + cur_adc_freq_div = 1; + if (cur_adc_freq_div > div_max) + cur_adc_freq_div = div_max; + set_freq = ref_freq/cur_adc_freq_div; + + *f_acq = set_freq; + + if (f_frame==NULL) { + cur_frame_delay = 0; + } else { + if (lch_cnt == 0) + lch_cnt = 1; + + if (*f_frame <= 0) { + cur_frame_delay = 0; + } else { + int32_t frame_div = (int32_t)((ref_freq/(*f_frame) + - lch_cnt*ref_freq/set_freq)+0.49); + + cur_frame_delay = frame_div <=0 ? 0 : + frame_div > X502_ADC_INTERFRAME_DELAY_MAX ? + X502_ADC_INTERFRAME_DELAY_MAX : frame_div; + } + *f_frame = 1./(lch_cnt/set_freq + + cur_frame_delay/ref_freq); + } + + if (adc_freq_div != NULL) + *adc_freq_div = cur_adc_freq_div; + if (adc_frame_delay != NULL) + *adc_frame_delay = cur_frame_delay; + } + return err; +} + +X502_EXPORT(int32_t) X502_CalcAdcFreq(double ref_freq, uint32_t lch_cnt, double *f_acq, + double *f_frame, uint32_t *adc_freq_div, uint32_t *adc_frame_delay) { + return X502_CalcAdcFreq2(NULL, ref_freq, lch_cnt, f_acq, f_frame, adc_freq_div, adc_frame_delay); +} + +X502_EXPORT(int32_t) X502_SetAdcFreq(t_x502_hnd hnd, double *f_acq, double *f_frame) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + + if (!err) { + double ref_freq; + X502_GetRefFreqValue(hnd, &ref_freq); + err = X502_CalcAdcFreq2(hnd, ref_freq, hnd->set.lch_cnt, f_acq, f_frame, + &hnd->set.adc_freq_div, &hnd->set.adc_frame_delay); + } + return err; +} + +X502_EXPORT(int32_t) X502_GetAdcFreq(t_x502_hnd hnd, double *f_acq, double *f_frame) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (f_acq == NULL) && (f_frame==NULL)) + err = X502_ERR_INVALID_POINTER; + + if (!err) { + double ref_freq, set_freq; + + X502_GetRefFreqValue(hnd, &ref_freq); + + set_freq = ref_freq/hnd->set.adc_freq_div; + if (f_acq!=NULL) { + *f_acq = set_freq; + } + + if (f_frame!=NULL) { + *f_frame = 1./(hnd->set.lch_cnt/set_freq + + hnd->set.adc_frame_delay/ref_freq); + } + } + return err; +} + + +X502_EXPORT(int32_t) X502_CalcDinFreq2(t_x502_hnd hnd, double ref_freq, double *f_din, uint32_t *result_freq_div) { + int32_t err = f_din==NULL ? X502_ERR_INVALID_POINTER : X502_ERR_OK; + if (err == X502_ERR_OK) { + uint32_t freq_div; + double set_freq = *f_din; + uint32_t div_max = X502_DIN_FREQ_DIV_MAX; + + if (set_freq<=0) { + if (IS_E16_FREQ(hnd, ref_freq)) { + set_freq = E16_DIN_FREQ_DEFAULT; + } else { + set_freq = ref_freq; + } + } + if (IS_E16_FREQ(hnd, ref_freq)) { + div_max = E16_DIN_FREQ_DIV_MAX; + if (set_freq > E16_DIN_FREQ_MAX) { + set_freq = E16_DIN_FREQ_MAX; + } + } + freq_div = (uint32_t)(ref_freq/set_freq+0.49); + if (freq_div == 0) + freq_div = 1; + if (freq_div > div_max) + freq_div = div_max; + set_freq = ref_freq/freq_div; + *f_din = set_freq; + + if (result_freq_div != NULL) + *result_freq_div = freq_div; + } + return err; +} + +X502_EXPORT(int32_t) X502_CalcDinFreq(double ref_freq, double *f_din, uint32_t *result_freq_div) { + return X502_CalcDinFreq2(NULL, ref_freq, f_din, result_freq_div); +} + +X502_EXPORT(int32_t) X502_SetDinFreq(t_x502_hnd hnd, double *f_din) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + if (!err) { + double ref_freq; + X502_GetRefFreqValue(hnd, &ref_freq); + err = X502_CalcDinFreq2(hnd, ref_freq, f_din, &hnd->set.din_freq_div); + } + return err; +} + + +X502_EXPORT(int32_t) X502_CalcOutFreq2(t_x502_hnd hnd, double ref_freq, double *f_dout, uint32_t *result_freq_div) { + int32_t err = f_dout==NULL ? X502_ERR_INVALID_POINTER : X502_ERR_OK; + if (err == X502_ERR_OK) { + const uint32_t out_div_min = f_get_out_freq_div_min(ref_freq); + uint32_t out_freq_div; + uint32_t div_max = X502_OUT_FREQ_DIV_MAX; + + + double set_freq = *f_dout; + if (set_freq <= 0) { + if (IS_E16_FREQ(hnd, ref_freq)) { + set_freq = E16_OUT_FREQ_DEFAULT; + } else { + set_freq = ref_freq; + } + } + if (IS_E16_FREQ(hnd, ref_freq)) { + div_max = E16_OUT_FREQ_DIV_MAX; + if (set_freq > E16_OUT_FREQ_MAX) { + set_freq = E16_OUT_FREQ_MAX; + } + } + + out_freq_div = (uint32_t)(ref_freq/set_freq+0.49); + if (out_freq_div < out_div_min) + out_freq_div = out_div_min; + if (out_freq_div > div_max) + out_freq_div = div_max; + + set_freq = ref_freq/out_freq_div; + *f_dout = set_freq; + + if (result_freq_div != NULL) + *result_freq_div = out_freq_div; + } + return err; +} + +X502_EXPORT(int32_t) X502_CalcOutFreq(double ref_freq, double *f_dout, uint32_t *result_freq_div) { + return X502_CalcOutFreq2(NULL, ref_freq, f_dout, result_freq_div); +} + +X502_EXPORT(int32_t) X502_SetOutFreq(t_x502_hnd hnd, double *f_dout) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + if (!err && (f_dout==NULL)) + err = X502_ERR_INVALID_POINTER; + + if (!err) { + double ref_freq; + + X502_GetRefFreqValue(hnd, &ref_freq); + /* Если не поддерживается возможность установки нестандартного делителя, то + всегда устанавливаем стандартный */ + if (X502_CheckFeature(hnd, X502_FEATURE_OUT_FREQ_DIV) != X502_ERR_OK) { + hnd->set.out_freq_div = X502_OUT_FREQ_DIV_DEFAULT; + *f_dout = ref_freq/hnd->set.out_freq_div; + } else { + err = X502_CalcOutFreq2(hnd, ref_freq, f_dout, &hnd->set.out_freq_div); + } + } + return err; +} + + + + +X502_EXPORT(int32_t) X502_SetRefFreq(t_x502_hnd hnd, uint32_t freq) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + + if (!err) { + if (x502_is_E16(hnd)) { + // для E16 ref freq всегда 48 МГц + hnd->set.ref_freq = E16_REF_FREQ_48000KHZ; + } else { + hnd->set.ref_freq = freq; + } + } + return err; +} + + + +X502_EXPORT(int32_t) X502_SetExtRefFreqValue(t_x502_hnd hnd, double freq) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + if (!err) + hnd->set.ext_ref_freq = freq; + return err; +} + +X502_EXPORT(int32_t) X502_SetAdcSyncStartValue(t_x502_hnd hnd, uint32_t phy_ch, uint32_t mode, uint32_t range, double value) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + if (!err) { + hnd->set.adc_sync_ch = phy_ch; + hnd->set.adc_sync_range = range; + hnd->set.adc_sync_mode = mode; + hnd->set.adc_sync_val = value; + } + return err; +} + +X502_EXPORT(int32_t) X502_GetRefFreqValue(t_x502_hnd hnd, double *freq) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + + if (!err) { + if ((hnd->set.sync_mode == X502_SYNC_INTERNAL) || + !(hnd->set.ext_ref_freq > 0)) { + *freq = hnd->set.ref_freq; + } else { + *freq = hnd->set.ext_ref_freq; + } + } + return err; +} + + + +X502_EXPORT(int32_t) X502_SetSyncMode(t_x502_hnd hnd, uint32_t sync_mode) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + + if (!err) { + err = CHECK_SYNC_MODE(sync_mode); + } + + if (!err) { + hnd->set.sync_mode = sync_mode; + } + return err; +} + +X502_EXPORT(int32_t) X502_SetSyncStartMode(t_x502_hnd hnd, uint32_t sync_start_mode) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + + if (!err) { + if (sync_start_mode==E16_SYNC_ADC_EDGE_RISE || + sync_start_mode==E16_SYNC_ADC_EDGE_FALL || + sync_start_mode==E16_SYNC_ADC_ABOVE_LEVEL || + sync_start_mode==E16_SYNC_ADC_BELOW_LEVEL) { + if (x502_is_E16(hnd) == false) { + // реализовано только в E16 + return X502_ERR_INVALID_SYNC_MODE; + } + } else { + err = CHECK_SYNC_MODE(sync_start_mode); + } + } + + if (!err) { + hnd->set.sync_start_mode = sync_start_mode; + } + + return err; +} + +uint32_t get_ltable_val(t_x502_hnd hnd, uint32_t phy_ch, uint32_t mode, uint32_t range, uint32_t avg) { + uint32_t wrd = ((phy_ch & 0xF) << 3) | (range & 0x7); + + if (mode == X502_LCH_MODE_ZERO) { + wrd |= (3 << 7); + } else if (mode == X502_LCH_MODE_COMM) { + wrd |= (phy_ch & 0x10 ? 2 : 1) << 7; + } + + if (avg) { + uint32_t avg_val = avg; + if (avg_val > hnd->set.adc_freq_div) + avg_val = hnd->set.adc_freq_div; + wrd |= ((avg_val-1) & 0x7F) << 9; + } + return wrd; +} + +X502_EXPORT(int32_t) X502_Configure(t_x502_hnd hnd, uint32_t flags) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + + if (!err) { + /* Проверяем правильность установленной опорной частоты. + Для внутренней может быть только одно из двух значений, + для внешней главное, чтобы не принимало требуемой */ + if ((hnd->set.sync_mode==X502_SYNC_INTERNAL) + && (hnd->set.ref_freq!=E16_REF_FREQ_48000KHZ) + && (hnd->set.ref_freq!=X502_REF_FREQ_2000KHZ) + &&(hnd->set.ref_freq!=X502_REF_FREQ_1500KHZ)) { + err = X502_ERR_INVALID_REF_FREQ; + } else if (hnd->set.ext_ref_freq > 2000000) { + err = X502_ERR_INVALID_REF_FREQ; + } + } + + if (!err) { + if (hnd->mode == X502_MODE_FPGA) { + uint32_t ch; + /* записываем логическую таблицу */ + for (ch=0; (ch < hnd->set.lch_cnt) && !err; ch++) { + uint32_t wrd = get_ltable_val(hnd, hnd->set.lch[ch].ch, hnd->set.lch[ch].mode, hnd->set.lch[ch].range, hnd->set.lch[ch].avg); + + err = hnd->iface_hnd->fpga_reg_write(hnd, (X502_REGS_IOHARD_LTABLE + + hnd->set.lch_cnt - 1 - ch) & 0xFFFF, wrd); + } + if (!err) { + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_LCH_CNT, + hnd->set.lch_cnt-1); + } + + if (!err) { + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_ADC_FREQ_DIV, + hnd->set.adc_freq_div-1); + } + + if (!err) { + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOARITH_ADC_FREQ_DIV, + hnd->set.adc_freq_div-1); + } + + + if (!err) { + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_ADC_FRAME_DELAY, + hnd->set.adc_frame_delay); + } + + if (!err) { + // для варианта E16 ref_freq будет всегда 48000000 + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_IO_MODE, + (SYNC_FREQ_CODE(hnd->set.sync_mode) & 0x7) + | ((hnd->set.sync_start_mode&0xF)<<3) + | ((hnd->set.ref_freq==X502_REF_FREQ_2000KHZ ? X502_MODE_REF_FREQ_2000 : + (hnd->set.ref_freq==X502_REF_FREQ_1500KHZ ? X502_MODE_REF_FREQ_1500: E16_MODE_REF_FREQ_48000)) << 7) + | (((hnd->set.out_freq_div-1) & (x502_is_E16(hnd) ? 0x7FFF : 0x3FF))<<9)); + } + + if (x502_is_E16(hnd)) { + // старт по аналоговому сигналу реализован только в E16 + if (!err) { + uint32_t wrd = get_ltable_val(hnd, hnd->set.adc_sync_ch, hnd->set.adc_sync_mode, hnd->set.adc_sync_range, 0); + + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOARITH_N_CHAN_SYN, wrd); + } + if (!err) { + uint32_t threshold; + threshold = hnd->set.adc_sync_val / hnd->set.f_scales[hnd->set.adc_sync_range] * E16_ADC_SCALE_CODE_MAX; + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOARITH_THRESHOLD, threshold); + } + } + + if (!err) { + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_DIGIN_FREQ_DIV, + hnd->set.din_freq_div-1); + } + } else if (hnd->mode == X502_MODE_DSP) { + uint32_t ch; + err = x502_bf_set_par(hnd, L502_BF_PARAM_LCH_CNT, &hnd->set.lch_cnt, 1); + for (ch=0; !err && (ch < hnd->set.lch_cnt); ch++) { + uint32_t ch_par[] = {ch, hnd->set.lch[ch].ch, + hnd->set.lch[ch].mode, + hnd->set.lch[ch].range, + hnd->set.lch[ch].avg > hnd->set.adc_freq_div ? + hnd->set.adc_freq_div : hnd->set.lch[ch].avg} ; + err = x502_bf_set_par(hnd, L502_BF_PARAM_LCH, ch_par, + sizeof(ch_par)/sizeof(ch_par[0])); + } + if (!err) { + err = x502_bf_set_par(hnd, L502_BF_PARAM_ADC_FREQ_DIV, + &hnd->set.adc_freq_div, 1); + } + if (!err) { + err = x502_bf_set_par(hnd, L502_BF_PARAM_ADC_FRAME_DELAY, + &hnd->set.adc_frame_delay, 1); + } + if (!err) { + err = x502_bf_set_par(hnd, L502_BF_PARAM_REF_FREQ_SRC, + &hnd->set.ref_freq, 1); + } + if (!err) { + uint32_t sync_code = SYNC_FREQ_CODE(hnd->set.sync_mode); + err = x502_bf_set_par(hnd, L502_BF_PARAM_SYNC_MODE, + &sync_code, 1); + } + if (!err) { + err = x502_bf_set_par(hnd, L502_BF_PARAM_SYNC_START_MODE, + &hnd->set.sync_start_mode, 1); + } + if (!err) { + err = x502_bf_set_par(hnd, L502_BF_PARAM_DAC_FREQ_DIV, + &hnd->set.out_freq_div, 1); + } + if (!err) + { + err = x502_bf_set_par(hnd, L502_BF_PARAM_DIN_FREQ_DIV, + &hnd->set.din_freq_div, 1); + } + + if (!err) { + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_CONFIGURE, 0, NULL, + 0, NULL, 0, X502_BF_CMD_DEFAULT_TOUT, NULL); + } + } else { + err = X502_ERR_INVALID_MODE; + } + } + return err; +} + + + + + + +X502_EXPORT(int32_t) X502_SetAdcCoef(t_x502_hnd hnd, uint32_t range, double k, double offs) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + + if (!err && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + + if (!err) { + if (x502_is_E16(hnd)) { + if (range >= E16_ADC_RANGE_CNT) { + err = X502_ERR_INVALID_LCH_RANGE; + } + } else { + if (range >= X502_ADC_RANGE_CNT) { + err = X502_ERR_INVALID_LCH_RANGE; + } + } + } + + if (!err) { + uint32_t kval = (uint32_t)(k*0x400000); + uint32_t offs_val = (uint32_t)(-offs); + + if (hnd->mode == X502_MODE_FPGA) { + err = hnd->iface_hnd->fpga_reg_write(hnd, f_regadd_k[range], kval); + if (!err) + err = hnd->iface_hnd->fpga_reg_write(hnd, f_regadd_offs[range], offs_val); + } else if (hnd->mode == X502_MODE_DSP) { + uint32_t wrds[3] = {range, kval, offs_val}; + err = x502_bf_set_par(hnd, L502_BF_PARAM_ADC_COEF, wrds, 3); + } else { + err = X502_ERR_INVALID_MODE; + } + } + + if (!err) { + hnd->info.cbr.adc[range].k = k; + hnd->info.cbr.adc[range].offs = offs; + } + + return err; +} + + +X502_EXPORT(int32_t) X502_GetAdcCoef(t_x502_hnd hnd, uint32_t range, double* k, double* offs) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err) { + if (range >= X502_ADC_RANGE_CNT) { + err = X502_ERR_INVALID_LCH_RANGE; + } + } + + if (!err) { + *k = hnd->info.cbr.adc[range].k; + *offs = hnd->info.cbr.adc[range].offs; + } + return err; +} + + + +X502_EXPORT(int32_t) X502_SetDacCoef(t_x502_hnd hnd, uint32_t ch, double k, double offs) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + + if (!err && (ch!=X502_DAC_CH1) && (ch!=X502_DAC_CH2)) { + err = X502_ERR_INVALID_DAC_CHANNEL; + } + + if (!err) { + hnd->info.cbr.dac[ch].offs = offs; + hnd->info.cbr.dac[ch].k = k; + } + return err; +} + +X502_EXPORT(int32_t) X502_GetDacCoef(t_x502_hnd hnd, uint32_t ch, double* k, double* offs) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (!err && (ch!=X502_DAC_CH1) && (ch!=X502_DAC_CH2)) { + err = X502_ERR_INVALID_DAC_CHANNEL; + } + + if (!err) { + if (offs!=NULL) + *offs = hnd->info.cbr.dac[ch].offs; + + if (k!=NULL) + *k = hnd->info.cbr.dac[ch].k; + } + return err; +} diff --git a/x502/x502api_eeprom.c b/x502/x502api_eeprom.c new file mode 100644 index 0000000..c6e792c --- /dev/null +++ b/x502/x502api_eeprom.c @@ -0,0 +1,213 @@ +#include "x502api_private.h" +#include "x502_eeprom.h" +#include "fast_crc.h" +#include +#include +#include + +#include "../devs/e502/e502_fpga_regs.h" + +#define X502_CHECK_ADDR(hnd, addr, size) ((addr+size)>hnd->info.flash_size ? \ + X502_ERR_FLASH_INVALID_ADDR : (size==0) ? X502_ERR_FLASH_INVALID_SIZE : X502_ERR_OK) + + +X502_EXPORT(int32_t) X502_FlashRead(t_x502_hnd hnd, uint32_t addr, uint8_t* data, + uint32_t size) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err==X502_ERR_OK) { + err = X502_CHECK_ADDR(hnd, addr, size); + } + if (err==X502_ERR_OK) { + + for ( ; (size!=0) && (err==X502_ERR_OK); ) { + uint32_t rd_size = size; + if (rd_size > hnd->iface_hnd->flash_rd_size) + rd_size = hnd->iface_hnd->flash_rd_size; + + err = hnd->iface_hnd->flash_rd(hnd, addr, data, rd_size); + if (err==X502_ERR_OK) { + data+=rd_size; + addr+=rd_size; + size-=rd_size; + } + } + } + return err; +} + +X502_EXPORT(int32_t) X502_FlashWrite(t_x502_hnd hnd, uint32_t addr, + const uint8_t* data, uint32_t size) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err==X502_ERR_OK) { + err = X502_CHECK_ADDR(hnd, addr, size); + } + if (err==X502_ERR_OK) { + for ( ; (size!=0) && (err==X502_ERR_OK); ) { + uint32_t wr_size = size; + if (wr_size > hnd->iface_hnd->flash_wr_size) + wr_size = hnd->iface_hnd->flash_wr_size; + + err = hnd->iface_hnd->flash_wr(hnd, addr, data, wr_size); + if (err==X502_ERR_OK) { + data+=wr_size; + addr+=wr_size; + size-=wr_size; + } + } + } + return err; +} + +X502_EXPORT(int32_t) X502_FlashErase(t_x502_hnd hnd, uint32_t addr, uint32_t size) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err==X502_ERR_OK) { + err = X502_CHECK_ADDR(hnd, addr, size); + } + if (err==X502_ERR_OK) { + err = hnd->iface_hnd->flash_erase(hnd, addr, size); + } + return err; +} + +X502_EXPORT(int32_t) X502_FlashWriteEnable(t_x502_hnd hnd) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err==X502_ERR_OK) { + err = hnd->iface_hnd->flash_set_prot(hnd, X502_EEPROM_PROT_WR_USER, NULL, 0); + } + return err; +} + +X502_EXPORT(int32_t) X502_FlashWriteDisable(t_x502_hnd hnd) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err==X502_ERR_OK) { + err = hnd->iface_hnd->flash_set_prot(hnd, X502_EEPROM_PROT_ALL, NULL, 0); + } + return err; +} + + +X502_EXPORT(int32_t) X502_FlashSetProtection(t_x502_hnd hnd, uint32_t prot, uint8_t *prot_data, uint32_t prot_data_size) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err==X502_ERR_OK) { + err = hnd->iface_hnd->flash_set_prot(hnd, prot, prot_data, prot_data_size); + } + return err; +} + +bool x502_is_E16(t_x502_hnd hnd) { + if (strcmp(hnd->info.name, E16_DEVICE_NAME) == 0) { + return true; + } else { + return false; + } +} + +/** Функция проверяет правильность информации о устройстве, записанной в EEPROM. + При наличии верной инофрмации, из EEPROM считывается название устройства и + серийный номер, а так же, при наличии, калибровочные коэффициенты */ +int x502_check_eeprom(t_x502_hnd hnd, uint32_t flags) { + int err; + uint32_t sign, size; + + if (x502_is_E16(hnd)) { + // в E16 добавился новый регистр и мы можем узнать размер flash-памяти + uint32_t flash_size; + err = hnd->iface_hnd->fpga_reg_read(hnd, E502_REGS_ARM_FLASHSIZE, &flash_size); + if (err != X502_ERR_OK) { + hnd->info.flash_size = 0; + return err; + } + hnd->info.flash_size = flash_size; + } else { + hnd->info.flash_size = X502_EEPROM_SIZE; + } + + hnd->info.devflags &= ~(X502_DEVFLAGS_FLASH_DATA_VALID | + ((flags & X502_RELOAD_FLAGS_NO_ADC) ? 0 : X502_DEVFLAGS_FLASH_ADC_CALIBR_VALID) | + ((flags & X502_RELOAD_FLAGS_NO_DAC) ? 0 : X502_DEVFLAGS_FLASH_DAC_CALIBR_VALID)); + + /* проверяем признак правильного описателя в EEPROM и его размер */ + err = X502_FlashRead(hnd, X502_EEPROM_ADDR_DESCR, (uint8_t*)&sign, (uint32_t)sizeof(sign)); + if (err == X502_ERR_OK) + err = X502_FlashRead(hnd, X502_EEPROM_ADDR_DESCR+sizeof(sign), (uint8_t*)&size, (uint32_t)sizeof(size)); + + if (err == X502_ERR_OK) { + if ((sign == X502_EEPROM_SIGN) && (size >= X502_DESCR_MIN_SIZE) && (size <= X502_DESCR_MAX_SIZE)) { + t_x502_descr* pdescr = (t_x502_descr*)malloc(size); + /* читаем весь описатель */ + if (pdescr!=NULL) { + err = X502_FlashRead(hnd, X502_EEPROM_ADDR_DESCR, (uint8_t*)pdescr, size); + } else { + err = X502_ERR_MEMORY_ALLOC; + } + + /* сверяем crc */ + if (err == X502_ERR_OK) { + uint32_t crc, crc2; + crc = CRC32_Block8(0, (uint8_t*)pdescr, (uint32_t)(size-sizeof(crc))); + memcpy(&crc2, &((uint8_t*)pdescr)[size-4], 4); + if (crc == crc2) { + + hnd->info.devflags |= X502_DEVFLAGS_FLASH_DATA_VALID; + memcpy(hnd->info.serial, pdescr->hdr.serial, sizeof(pdescr->hdr.serial)); + memcpy(hnd->info.name, pdescr->hdr.name, sizeof(pdescr->hdr.name)); + memcpy(hnd->info.factory_mac, pdescr->hdr.factory_mac, sizeof(pdescr->hdr.factory_mac)); + + if (!(flags & X502_RELOAD_FLAGS_NO_ADC)) { + if ((pdescr->cbr_adc.hdr.cbr_sign == X502_EEPROM_CBR_SIGN) && + (pdescr->cbr_adc.hdr.format == X502_EEPROM_CBR_FROMAT) && + (pdescr->cbr_adc.hdr.src == X502_EEPROM_CBR_SRC_ADC) && + (pdescr->cbr_adc.hdr.range_cnt == X502_ADC_RANGE_CNT || pdescr->cbr_adc.hdr.range_cnt == E16_ADC_RANGE_CNT) && + (pdescr->cbr_adc.hdr.channel_cnt == 1)) { + unsigned int i; + + hnd->info.devflags |= X502_DEVFLAGS_FLASH_ADC_CALIBR_VALID; + for (i=0; (i < pdescr->cbr_adc.hdr.range_cnt) && (err == X502_ERR_OK); i++) { + if (x502_is_E16(hnd)) { + if (i > E16_ADC_RANGE_CNT) { + break; + } + } else { + if (i > X502_ADC_RANGE_CNT) { + break; + } + } + err = X502_SetAdcCoef(hnd, i, pdescr->cbr_adc.coefs[i].k, + pdescr->cbr_adc.coefs[i].offs); + } + } + } + + if (!(flags & X502_RELOAD_FLAGS_NO_DAC)) { + if ((pdescr->cbr_dac.hdr.cbr_sign == X502_EEPROM_CBR_SIGN) && + (pdescr->cbr_dac.hdr.format == X502_EEPROM_CBR_FROMAT) && + (pdescr->cbr_dac.hdr.src == X502_EEPROM_CBR_SRC_DAC) && + (pdescr->cbr_dac.hdr.range_cnt == 1) && + (pdescr->cbr_dac.hdr.channel_cnt == X502_DAC_CH_CNT)) { + unsigned int i; + + hnd->info.devflags |= X502_DEVFLAGS_FLASH_DAC_CALIBR_VALID; + for (i=0; (i < pdescr->cbr_dac.hdr.channel_cnt) && + (i < X502_ADC_RANGE_CNT) && (err == X502_ERR_OK); i++) { + err = X502_SetDacCoef(hnd, i, pdescr->cbr_dac.coefs[i].k, + pdescr->cbr_dac.coefs[i].offs); + } + } + } + } + } + free(pdescr); + } + } + return err; +} + + +X502_EXPORT(int32_t) X502_ReloadDevInfo(t_x502_hnd hnd, uint32_t flags) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if ((err==X502_ERR_OK) && (hnd->iface_hnd->reload_dev_info!=NULL)) + err = hnd->iface_hnd->reload_dev_info(hnd); + if (err==X502_ERR_OK) + err = x502_check_eeprom(hnd, flags); + return err; +} diff --git a/x502/x502api_errs.c b/x502/x502api_errs.c new file mode 100644 index 0000000..76ace24 --- /dev/null +++ b/x502/x502api_errs.c @@ -0,0 +1,186 @@ +//--------------------------------------------------------------------------- +#include "x502api.h" +#include "l502_bf_cmd_defs.h" + +#include "../devs/e502/e502_cm4_defs.h" + +static const char* f_unknow_err = "Неизвестная ошибка"; + +typedef struct { + int32_t err; + const char* str; +}t_err_table; + +static const t_err_table f_err_tbl[] = { + { X502_ERR_OK, "Выполнено без ошибок" }, + { X502_ERR_INVALID_HANDLE, "Недействительный описатель модуля"}, + { X502_ERR_MEMORY_ALLOC, "Ошибка выделения памяти"}, + { X502_ERR_ALREADY_OPENED, "Попытка открыть уже открытое устройство"}, + { X502_ERR_DEVICE_NOT_FOUND, "Устройство с заданными параметрами не найдено в системе"}, + { X502_ERR_DEVICE_ACCESS_DENIED, "Доступ к устройству запрещен"}, + { X502_ERR_DEVICE_OPEN, "Ошибка открытия устройства"}, + { X502_ERR_INVALID_POINTER, "В функцию передан недействительный указатель"}, + { X502_ERR_STREAM_IS_RUNNING, "Функция не может быть выполнена при запущенном потоке сбора данных"}, + { X502_ERR_RECV, "Ошибка чтения данных синхронного ввода"}, + { X502_ERR_SEND, "Ошибка записи данных для синхронного вывода"}, + { X502_ERR_STREAM_OVERFLOW, "Произошло переполнение внутреннего буфера для потока синхронного ввода"}, + { X502_ERR_UNSUP_STREAM_MSG, "Неизвестное сообщение в потоке синхронного ввода"}, + { X502_ERR_MUTEX_CREATE, "Ошибка создания системного мьютекса"}, + { X502_ERR_MUTEX_INVALID_HANDLE, "Неверный описатель мьютекса"}, + { X502_ERR_MUTEX_LOCK_TOUT, "Истекло время ожидания освобождния мьютекса"}, + { X502_ERR_MUTEX_RELEASE, "Ошибка освобождения мьютекса"}, + { X502_ERR_INSUFFICIENT_SYSTEM_RESOURCES, "Недостаточно системных ресурсов"}, + { X502_ERR_NOT_IMPLEMENTED, "Данная возможность еще не реализована"}, + { X502_ERR_INSUFFICIENT_ARRAY_SIZE, "Недостаточный размер массива"}, + { X502_ERR_FPGA_REG_READ, "Ошибка чтения регистра FPGA"}, + { X502_ERR_FPGA_REG_WRITE, "Ошибка записи регистра FPGA"}, + { X502_ERR_STREAM_IS_NOT_RUNNING, "Синхронный сбор уже остановлен"}, + { X502_ERR_INTERFACE_RELEASE, "Ошибка освобождения интерфейса"}, + { X502_ERR_THREAD_START, "Ошибка запуска потока"}, + { X502_ERR_THREAD_STOP, "Ошибка останова потока"}, + { X502_ERR_DEVICE_DISCONNECTED, "Устройство было отключено"}, + { X502_ERR_IOCTL_INVALID_RESP_SIZE, "Неверный размер ответа на управляющий запрос"}, + { X502_ERR_INVALID_DEVICE, "Неверный тип устройства"}, + { X502_ERR_INVALID_DEVICE_RECORD, "Недействительная запись о устройстве"}, + { X502_ERR_INVALID_CONFIG_HANDLE, "Неверный описатель конфигурации модуля"}, + { X502_ERR_DEVICE_NOT_OPENED, "Связь с устройством закрыта или не была установлена"}, + { X502_ERR_INVALID_OP_FOR_IFACE, "Данная операция не доступна для текущего интерфейса связи с устройством"}, + { X502_ERR_FPGA_NOT_LOADED, "Не загружен ПЛИС модуля"}, + { X502_ERR_INVALID_USB_CONFIGURATION,"Неверная конфигурация USB-устройства"}, + { X502_ERR_INVALID_SVC_BROWSE_HANDLE, "Неверный описатель контекста поиска сервисов"}, + { X502_ERR_INVALID_SVC_RECORD_HANDLE,"Неверный описатель записи о сервисе"}, +#ifdef _WIN32 + { X502_ERR_DNSSD_NOT_RUNNING, "Не запущена программа обнаружения устройств в локальной сети (Bonjour Service)"}, +#else + { X502_ERR_DNSSD_NOT_RUNNING, "Не запущена программа обнаружения устройств в локальной сети (AVAHI)"}, +#endif + { X502_ERR_DNSSD_COMMUNICATION, "Ошибка при обращении к программе обнаружения устройств в локальной сети"}, + { X502_ERR_INVALID_SVC_BROWSE_HANDLE, "Неверный описатель контекста поиска устройств в сети"}, + { X502_ERR_INVALID_SVC_RECORD_HANDLE, "Неверный описатель записи о сервисе"}, + { X502_ERR_DNSSD_NOT_RUNNING, "Не запущена программа обнаружения устройств в локальной сети"}, + { X502_ERR_DNSSD_COMMUNICATION, "Ошибка при обращении к программе обнаружения устройств в локальной сети"}, + { X502_ERR_SVC_RESOLVE_TIMEOUT, "Превышен таймаут запроса параметров автообнаруженного сетевого устройства"}, + { X502_ERR_INSTANCE_NAME_ENCODING, "Ошибка в кодировке имени экземляра устройства"}, + { X502_ERR_INSTANCE_MISMATCH, "Экземпляры модулей не совпадают"}, + { X502_ERR_NOT_SUP_BY_FIRMWARE, "Возможность не поддерживается текущей версией прошивки устройства"}, + { X502_ERR_NOT_SUP_BY_DRIVER, "Возможность не поддерживается текущей версией драйвера устройства"}, + { X502_ERR_OUT_CYCLE_SETUP_TOUT, "Превышено время ожидания установления цикличиского сигнала на вывод"}, + + { X502_ERR_INVALID_LTABLE_SIZE, "Задан неверный размер логической таблицы"}, + { X502_ERR_INVALID_LCH_NUMBER, "Задан неверный номер логического канала"}, + { X502_ERR_INVALID_LCH_RANGE, "Неверно задано значение диапазона АЦП"}, + { X502_ERR_INVALID_LCH_MODE, "Неверно задан режим измерения для логического канала"}, + { X502_ERR_INVALID_LCH_PHY_NUMBER, "Неверно задан номер физического канала при настройке логического"}, + { X502_ERR_INVALID_LCH_AVG_SIZE, "Неверно задан размер усреднения для логического канала"}, + { X502_ERR_INVALID_ADC_FREQ_DIV, "Неверно задан делитель частоты сбора данных АЦП"}, + { X502_ERR_INVALID_DIN_FREQ_DIV, "Неверно задан делитель частоты синхронного ввода цифровых линий"}, + { X502_ERR_INVALID_MODE, "Неверно задан режим работы платы"}, + { X502_ERR_INVALID_DAC_CHANNEL, "Неверный номер канала ЦАП"}, + { X502_ERR_INVALID_REF_FREQ, "Неверный код выбора опорной частоты синхронизации"}, + { X502_ERR_INVALID_INTERFRAME_DELAY,"Неверно задано значение межкадровой задержки"}, + { X502_ERR_INVALID_SYNC_MODE, "Неверно задан режим синхронизации"}, + { X502_ERR_INVALID_STREAM_CH, "Неверно задан номер потока данных"}, + { X502_ERR_INVALID_OUT_FREQ_DIV, "Неверно задан делитель частоты синхронного вывода"}, + { X502_ERR_REF_FREQ_NOT_LOCKED, "Ошибка захвата опорной частоты синхронизации"}, + { X502_ERR_IOCTL_FAILD, "Управляющий запрос к драйверу завершен с ошибкой"}, + { X502_ERR_IOCTL_TIMEOUT, "Истек таймаут ожидания завершения выполнения управляющего запроса к драйверу"}, + { X502_ERR_GET_INFO, "Ошибка получения информации о устройстве от драйвера"}, + { X502_ERR_DIG_IN_NOT_RDY, "За время ожидания не было считано новое слово с цифровых линий"}, + { X502_ERR_RECV_INSUFFICIENT_WORDS, "Принято недостаточно слов от модуля"}, + { X502_ERR_DAC_NOT_PRESENT, "Попытка выполнить операцию, требующую наличие ЦАП, при его отсутствии"}, + { X502_ERR_SEND_INSUFFICIENT_WORDS, "Передано недостаточно слов в модуль"}, + { X502_ERR_NO_CMD_RESPONSE, "Не пришло ответа на переданную команду"}, + { X502_ERR_PROC_INVALID_CH_NUM, "Неверный номер канала в обрабатываемом потоке синхронного ввода"}, + { X502_ERR_PROC_INVALID_CH_RANGE, "Неверный код диапазона в обрабатываемом потоке синхронного ввода"}, + { X502_ERR_FLASH_INVALID_ADDR, "Задан неверный адрес во Flash-памяти"}, + { X502_ERR_FLASH_INVALID_SIZE, "Задан неверный размер блока данных при работе с Flash-памятью"}, + { X502_ERR_FLASH_WRITE_TOUT, "Истек таймаут ожидания завершения записи во Flash-память"}, + { X502_ERR_FLASH_ERASE_TOUT, "Истек таймаут ожидания завершения стирания блока Flash-памяти"}, + { X502_ERR_FLASH_SECTOR_BOUNDARY, "Заданная область для стирания Flash-памяти нарушает границу блока в 4 Кбайт"}, + + { X502_ERR_SOCKET_OPEN, "Не удалось открыть сокет для соединения"}, + { X502_ERR_CONNECTION_TOUT, "Превышено время подключения"}, + { X502_ERR_CONNECTION_CLOSED_BY_DEV,"Соединение закрыто устройством"}, + { X502_ERR_SOCKET_SET_BUF_SIZE, "Не удалось установить заданный размер буфера сокета"}, + { X502_ERR_NO_DATA_CONNECTION, "Соединение для передачи данных не установлено"}, + { X502_ERR_NO_STREAM_END_MSG, "Не удалось дождаться сообщения о завершении потока"}, + { X502_ERR_CONNECTION_RESET, "Соединение было сброшено другой стороной"}, + { X502_ERR_HOST_UNREACHABLE, "Не удалось найти хост с указанным адресом"}, + { X502_ERR_TCP_CONNECTION_ERROR, "Ошибка установления TCP-соединения"}, + + { X502_ERR_LDR_FILE_OPEN, "Не удалось открыть файл прошивки BlackFin"}, + { X502_ERR_LDR_FILE_READ, "Ошибка чтения из фала прошивки BlackFin"}, + { X502_ERR_LDR_FILE_FORMAT, "Неверный формат файла прошивки BlackFin"}, + { X502_ERR_LDR_FILE_UNSUP_FEATURE, "Используются возможность LDR-файла, недоступные при записи прошивки BlackFin по HDMA"}, + { X502_ERR_LDR_FILE_UNSUP_STARTUP_ADDR, "Неверный стартовый адрес программы в прошивке BlackFin"}, + { X502_ERR_BF_REQ_TIMEOUT, "Истек таймаут выполнения запроса на чтения/запись памяти BlackFin"}, + { X502_ERR_BF_CMD_IN_PROGRESS, "Команда для BlackFin все еще находится в процессе обработки"}, + { X502_ERR_BF_CMD_TIMEOUT, "Истекло время выполнения управляющей команды процессором BlackFin"}, + { X502_ERR_BF_CMD_RETURN_INSUF_DATA,"Возвращено недостаточно данных в ответ на команду к BlackFin"}, + { X502_ERR_BF_LOAD_RDY_TOUT, "Истек таймаут ожидания готовности процессора BlackFin к записи прошивки"}, + { X502_ERR_BF_NOT_PRESENT, "Процессор BlackFin не присутствует на плате"}, + { X502_ERR_BF_INVALID_ADDR, "Неверный адрес памяти BlackFin при записи или чтении по HDMA"}, + { X502_ERR_BF_INVALID_CMD_DATA_SIZE,"Неверный размер данных, передаваемых с управляющей командой в BlackFin"}, + + { L502_BF_ERR_UNSUP_CMD, "Ошибка BlackFin: неподдерживаемый код команды"}, + { L502_BF_ERR_CMD_OVERRUN, "Ошибка BlackFin: запрос на выполнение команды опережает завершение предыдущей"}, + { L502_BF_ERR_INVALID_CMD_PARAMS, "Ошибка BlackFin: неверное значение параметров команды"}, + { L502_BF_ERR_INSUF_CMD_DATA, "Ошибка BlackFin: недостаточное данных передано с командой"}, + { L502_BF_ERR_STREAM_RUNNING, "Ошибка BlackFin: команда не допустима при запущеном сборе"}, + { L502_BF_ERR_STREAM_STOPPED, "Ошибка BlackFin: команда допустима только при запущеном сборе"}, + { L502_BF_ERR_NO_TEST_IN_PROGR, "Ошибка BlackFin: не выполняется ни одного теста"}, + { L502_BF_ERR_TEST_VALUE, "Ошибка BlackFin: неверное значение при выполнении теста"}, + + { E502_CM4_ERR_FPGA_NSTATUS_TOUT, "Ошибка Cortex-M4: При загрузке ПЛИС не удалось дождаться сигнала перехода в режим загрузки"}, + { E502_CM4_ERR_FPGA_CONF_DONE_TOUT, "Ошибка Cortex-M4: При загрузке ПЛИС не удалось дождаться сигнала завершения загрузки"}, + { E502_CM4_ERR_FPGA_FW_NOT_PRESENT, "Ошибка Cortex-M4: Не обнаружена прошивка ПЛИС во flash-памяти модуля"}, + { E502_CM4_ERR_FPGA_REG_NACK, "Ошибка Cortex-M4: Обращение к регистру ПЛИС вернуло ответ NACK"}, + { E502_CM4_ERR_FPGA_REG_ERROR, "Ошибка Cortex-M4: Обращение к регистру ПЛИС вернуло ответ ERROR"}, + { E502_CM4_ERR_FPGA_REG_WT_TOUT, "Ошибка Cortex-M4: Не удалось дожлаться ответ на обращение к регистру ПЛИС"}, + { E502_CM4_ERR_TEST_INVALID_NUM, "Ошибка Cortex-M4: Неподдерживаемый номер теста"}, + { E502_CM4_ERR_TEST_VALUE_MISMATH, "Ошибка Cortex-M4: Несовпадение ожидаемых значений при проходе теста"}, + { E502_CM4_ERR_TEST_NOT_RUNNING, "Ошибка Cortex-M4: Тест не запущен"}, + { E502_CM4_ERR_TEST_ALREADY_RUNNING,"Ошибка Cortex-M4: Tест уже запщен"}, + { E502_CM4_ERR_BF_LDR_FILE_SIZE, "Ошибка Cortex-M4: Не удалось найти конец файла прошивки BlackFin"}, + { E502_CM4_ERR_LDR_FILE_FORMAT, "Ошибка Cortex-M4: Неверный формат файла прошивки BlackFin"}, + { E502_CM4_ERR_LDR_FILE_UNSUP_FEATURE, "Ошибка Cortex-M4: Используются возможность LDR-файла, недоступные при записи прошивки BlackFin по HDMA"}, + { E502_CM4_ERR_LDR_FILE_UNSUP_STARTUP_ADDR, "Ошибка Cortex-M4: Неверный стартовый адрес программы в прошивке BlackFin"}, + { E502_CM4_ERR_BF_REQ_TIMEOUT, "Ошибка Cortex-M4: Истек таймаут выполнения запроса на чтения/запись памяти BlackFin"}, + { E502_CM4_ERR_BF_CMD_IN_PROGRESS, "Ошибка Cortex-M4: Команда для BlackFin все еще находится в процессе обработки"}, + { E502_CM4_ERR_BF_CMD_TIMEOUT, "Ошибка Cortex-M4: Истекло время выполнения управляющей команды процессором BlackFin"}, + { E502_CM4_ERR_BF_CMD_RETURN_INSUF_DATA, "Ошибка Cortex-M4: Возвращено недостаточно данных в ответ на команду к BlackFin"}, + { E502_CM4_ERR_BF_LOAD_RDY_TOUT, "Ошибка Cortex-M4: Истек таймаут ожидания готовности процессора BlackFin к записи прошивки"}, + { E502_CM4_ERR_BF_NOT_PRESENT, "Ошибка Cortex-M4: Попытка выполнить операцию для которой нужен сигнальный процессор при отсутствии сигнального процессора в модуле"}, + { E502_CM4_ERR_BF_INVALID_ADDR, "Ошибка Cortex-M4: Неверный адрес памяти BlackFin при записи или чтении по HDMA"}, + { E502_CM4_ERR_BF_INVALID_CMD_DATA_SIZE, "Ошибка Cortex-M4: Неверный размер данных, передаваемых с управляющей командой в BlackFin"}, + { E502_CM4_ERR_UNKNOWN_CMD, "Ошибка Cortex-M4: Неподдерживаемый код команды"}, + { E502_CM4_ERR_INVALID_CMD_PARAMS, "Ошибка Cortex-M4: Неверные параметры переданной команды"}, + { E502_CM4_ERR_FIRM_BUF_OVERFLOW, "Ошибка Cortex-M4: Переполнение буфера для приема прошивки"}, + { E502_CM4_ERR_CMD_SIGNATURE, "Ошибка Cortex-M4: Неверный признак начала команды"}, + { E502_CM4_ERR_INVALID_CMD_DATA_SIZE, "Ошибка Cortex-M4: Неверное количество данных в команде"}, + { E502_CM4_ERR_FLASH_PROT_CODE, "Ошибка Cortex-M4: Неверный код настройки защиты Flash-памяти"}, + { E502_CM4_ERR_FLASH_OP, "Ошибка Cortex-M4: Ошибка выполнения операции с Flash-памятью"}, + { E502_CM4_ERR_FLASH_DATA_COMPARE, "Ошибка Cortex-M4: Ошибка сравнения записанных данных во Flash-память"}, + { E502_CM4_ERR_INVALID_PASSWORD, "Ошибка Cortex-M4: Неверный пароль для изменения сетевых настроек"}, + { E502_CM4_ERR_FPGA_NOT_LOADED, "Ошибка Cortex-M4: ПЛИС не был загружен"}, + { E502_CM4_ERR_FLASH_SET_PROT_BITS, "Ошибка Cortex-M4: Не удалось изменить занчения битов защиты Flash-памяти"}, + { E502_CM4_ERR_FPGA_FW_INVALID_TEMP_RANGE, "Ошибка Cortex-M4: Загруженная прошивка ПЛИС предназначена для другого темп. исполнения"}, + { E502_CM4_ERR_M0_STREAM_START_REQ, "Ошибка Cortex-M4: Нет ответа на запрос запуска потока от ядра Cortex-M0"}, + { E502_CM4_ERR_M0_STREAM_STOP_REQ, "Ошибка Cortex-M4: Нет ответа на запрос останова потока от ядра Cortex-M0"}, + { E502_CM4_ERR_OUT_STREAM_RUNNING, "Ошибка Cortex-M4: Уже запущен вывод в потоковом режиме"}, + { E502_CM4_ERR_OUT_NO_CYCLE_BUF, "Ошибка Cortex-M4: Нет свободного буфера для циклического режима. Не произошла смена страниц"}, + { E502_CM4_ERR_OUT_CYCLE_BUF_SIZE, "Ошибка Cortex-M4: Задан слишком большой размер циклического буфера"}, + { E502_CM4_ERR_OUT_CYCLE_NOT_LOADED, "Ошибка Cortex-M4: Не был полностью загружен циклический буфер перед сменой"} +}; + + +X502_EXPORT(const char*) X502_GetErrorString(int32_t err) { + size_t i; + const char *str = f_unknow_err; + + for (i=0; (i < sizeof(f_err_tbl)/sizeof(f_err_tbl[0])) && (str==f_unknow_err); i++) { + if (f_err_tbl[i].err == err) + str = f_err_tbl[i].str; + } + return str; +} diff --git a/x502/x502api_private.h b/x502/x502api_private.h new file mode 100644 index 0000000..d397d72 --- /dev/null +++ b/x502/x502api_private.h @@ -0,0 +1,310 @@ +#ifndef X502API_PRIVATE_H +#define X502API_PRIVATE_H + + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#include "x502api.h" +#include "osspec.h" +#include "l502_bf_cmd_defs.h" +#include "x502_fpga_regs.h" +#include +#include +#include + +struct st_x502_devrec_inptr { + const void *iface; + void *iface_data; +}; + +#define E16_DEVICE_NAME "E16" + +#define X502_SIGN 0xA55A0502 + + +#define X502_CHECK_HND(hnd) ((hnd!=NULL) ? (hnd)->sign == X502_SIGN ? X502_ERR_OK \ + : X502_ERR_INVALID_HANDLE : X502_ERR_INVALID_HANDLE) + +#define X502_CHECK_HND_OPENED(hnd) ((hnd!=NULL) ? (hnd)->sign == X502_SIGN ? \ + (((hnd)->flags & PRIV_FLAGS_OPENED) ? X502_ERR_OK : X502_ERR_DEVICE_NOT_OPENED) \ + : X502_ERR_INVALID_HANDLE : X502_ERR_INVALID_HANDLE) + + + + +/* на сколько секунд данных будет рассчитан внутренний буфер */ +#define X502_DMA_IN_BUF_FOR_SEC 4 +/* максимальное кол-во прерываний в секунду */ +#define X502_DMA_IN_MAX_IRQ_PER_SEC 20 + +#define X502_DMA_OUT_BUF_SIZE (3*3*1024*1024) +#define X502_DMA_OUT_IRQ_STEP (3*1024*1024/32) + + + +#define X502_MUTEX_CFG_LOCK_TOUT 1000 + +#define X502_OUT_CYCLE_WAIT_TOUT 20000 + + +#define X502_STREAM_CH_CNT 2 + + +#ifdef _WIN32 + #define SLEEP_MS(ms) Sleep(ms) +#else + #define SLEEP_MS(ms) usleep(ms*1000) +#endif + + + + +/** параметры канала потока данных */ +typedef struct { + uint32_t buf_size; /** размер временного буфера */ + uint32_t step; /** через сколько переданных отсчетов будет генерироваться прерывание */ +} t_x502_stream_ch_params; + + + +typedef int32_t (*t_x502_iface_free_devinfo_ptr)(t_x502_devrec_inptr *devinfo_ptr); + +typedef int32_t (*t_x502_iface_open)(t_x502_hnd hnd, const t_x502_devrec *devinfo); +typedef int32_t (*t_x502_iface_close)(t_x502_hnd hnd); +typedef int32_t (*t_x502_iface_fpga_reg_read)(t_x502_hnd hnd, uint32_t addr, uint32_t *val); +typedef int32_t (*t_x502_iface_fpga_reg_write)(t_x502_hnd hnd, uint32_t addr, uint32_t val); + +typedef int32_t (*t_x502_iface_stream_cfg)(t_x502_hnd hnd, uint32_t ch, t_x502_stream_ch_params *pars); +typedef int32_t (*t_x502_iface_stream_start)(t_x502_hnd hnd, uint32_t ch, uint32_t flags); +typedef int32_t (*t_x502_iface_stream_stop)(t_x502_hnd hnd, uint32_t ch, uint32_t flags); +typedef int32_t (*t_x502_iface_stream_free)(t_x502_hnd hnd, uint32_t ch, uint32_t flags); +typedef int32_t (*t_x502_iface_stream_running)(t_x502_hnd hnd, uint32_t ch, int32_t* running); +typedef int32_t (*t_x502_iface_stream_read)(t_x502_hnd hnd, uint32_t *buf, uint32_t size, uint32_t tout); +typedef int32_t (*t_x502_iface_stream_write)(t_x502_hnd hnd, const uint32_t *buf, uint32_t size, uint32_t tout); +typedef int32_t (*t_x502_iface_stream_get_rdy_cnt)(t_x502_hnd hnd, uint32_t ch, uint32_t *rdy_cnt); + +typedef int32_t (*t_x502_iface_bf_mem_block_rd)(t_x502_hnd hnd, uint32_t addr, uint32_t *block, uint32_t size); +typedef int32_t (*t_x502_iface_bf_mem_block_wr)(t_x502_hnd hnd, uint32_t addr, const uint32_t *block, uint32_t size); +typedef int32_t (*t_x502_iface_bf_firm_load)(t_x502_hnd hnd, const char* filename); + +typedef int32_t (*t_x502_iface_flash_rd)(t_x502_hnd hnd, uint32_t addr, uint8_t* data, uint32_t size); +typedef int32_t (*t_x502_iface_flash_wr)(t_x502_hnd hnd, uint32_t addr, const uint8_t* data, uint32_t size); +typedef int32_t (*t_x502_iface_flash_erase)(t_x502_hnd hnd, uint32_t addr, uint32_t size); +typedef int32_t (*t_x502_iface_flash_set_prot)(t_x502_hnd hnd, uint32_t flags, + const uint8_t* prot_data, uint32_t size); + + +typedef int32_t (*t_x502_iface_reload_devinfo)(t_x502_hnd hnd); + +typedef int32_t (*t_x502_iface_cycle_load_start)(t_x502_hnd hnd, uint32_t size); +typedef int32_t (*t_x502_iface_cycle_setup)(t_x502_hnd hnd, uint32_t flags); +typedef int32_t (*t_x502_iface_cycle_stop)(t_x502_hnd hnd, uint32_t flags); +typedef int32_t (*t_x502_iface_cycle_check_setup)(t_x502_hnd hnd, uint32_t *done); + + +typedef int32_t (*t_x502_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); + +typedef int32_t (*t_x502_iface_check_feature)(t_x502_hnd hnd, uint32_t feature); + +typedef int32_t (*t_x502_iface_fpga_mode_init)(t_x502_hnd hnd); + +typedef enum { + X502_STREAM_FLAG_SINGLE = 0x01, + X502_STREAM_FLAG_NO_REQUEST = 0x02 +} t_x502_streams_flags; + + +typedef struct { + uint16_t id_reg_addr; + uint16_t in_stream_buf_min; + uint16_t ioctl_max_data_size; + uint16_t bf_mem_block_size; + uint16_t flash_rd_size; /**< Максимальный размер чтения из flash-памяти за один запрос */ + uint16_t flash_wr_size; /**< Максимальный размер записи во flash-память за один запрос */ + t_x502_iface_free_devinfo_ptr free_devinfo_ptr; + t_x502_iface_open open; + t_x502_iface_close close; + t_x502_iface_fpga_reg_read fpga_reg_read; + t_x502_iface_fpga_reg_write fpga_reg_write; + t_x502_iface_stream_cfg stream_cfg; + t_x502_iface_stream_start stream_start; + t_x502_iface_stream_stop stream_stop; + t_x502_iface_stream_free stream_free; + t_x502_iface_stream_running stream_running; + t_x502_iface_stream_read stream_read; + t_x502_iface_stream_write stream_write; + t_x502_iface_stream_get_rdy_cnt stream_get_rdy_cnt; + t_x502_iface_bf_mem_block_rd bf_mem_block_rd; + t_x502_iface_bf_mem_block_wr bf_mem_block_wr; + t_x502_iface_bf_firm_load bf_firm_load; + t_x502_iface_flash_rd flash_rd; + t_x502_iface_flash_wr flash_wr; + t_x502_iface_flash_erase flash_erase; + t_x502_iface_flash_set_prot flash_set_prot; + t_x502_iface_reload_devinfo reload_dev_info; + t_x502_iface_cycle_load_start cycle_load_start; + t_x502_iface_cycle_setup cycle_setup; + t_x502_iface_cycle_stop cycle_stop; + t_x502_iface_cycle_check_setup cycle_check_setup; + t_x502_iface_fpga_mode_init fpga_mode_init; + t_x502_iface_gen_ioctl gen_ioctl; + t_x502_iface_check_feature check_feature; +} t_x502_dev_iface; + + + + + +typedef enum { + STREAM_IN_WRD_ADC = 0, + STREAM_IN_WRD_DIN = 1, + STREAM_IN_WRD_MSG = 2, + STREAM_IN_WRD_USR = 3, + STREAM_IN_WRD_TSP = 4, +} t_stream_in_wrd_type; + + + +typedef enum { + PRIV_FLAGS_OPENED = 0x0001, + PRIV_FLAGS_PRELOAD_DONE = 0x0002, + PRIV_FLAGS_CYCLE_MODE = 0x0004, + PRIV_FLAGS_STREAM_RUN = 0x0080 +} t_x502_state_flags; + +typedef enum { + X502_RELOAD_FLAGS_NO_DAC = 0x0001, + X502_RELOAD_FLAGS_NO_ADC = 0x0002 +} t_x502_reload_flags; + +/** структура, описывающая параметры логического канала */ +typedef struct { + uint32_t ch; /** физический номер канала */ + uint32_t mode; /** режим работы канала из #t_l502_ch_mode */ + uint32_t range; /** диапазон измерения */ + uint32_t avg; /** коэффициент усреднения */ +} t_x502_lch; + +typedef struct { + t_x502_lch lch[X502_LTABLE_MAX_CH_CNT]; + uint32_t lch_cnt; + uint32_t adc_freq_div; + uint32_t adc_frame_delay; + uint32_t din_freq_div; + uint32_t sync_mode; + uint32_t sync_start_mode; + uint32_t ref_freq; + double ext_ref_freq; + uint32_t out_freq_div; + uint32_t adc_sync_ch; + uint32_t adc_sync_range; + uint32_t adc_sync_mode; + double adc_sync_val; + double dac_range; + uint32_t dac_code_max; + uint32_t adc_code_max; + const double *f_scales; +} t_x502_settings; + + + +typedef struct st_x502 { + uint32_t sign; /* признак описателя L502/E502 */ + t_x502_iface iface; + const t_x502_dev_iface *iface_hnd; + void *iface_data; + t_x502_state_flags flags; /* флаги состояния платы */ + t_x502_streams streams; /* какие синхронные потоки разрешены */ + t_x502_mode mode; /* режим работы (через ПЛИС или DSP) */ + t_x502_info info; + t_x502_settings set; /* настройки платы */ + + t_x502_stream_ch_params stream_pars[X502_STREAM_CH_CNT]; + + uint32_t last_dout; /* последнее выданное значение на DIGOUT */ + uint32_t proc_adc_ch; /* ожидаемый логический канал для следующей ProcessData() */ + + + t_mutex mutex_cfg; /* мьютекс для доступа к полям параметров и состояния модуля */ + t_mutex mutex_bf; /* мьютекс для доступа к памяти сигнального процессора */ + + uint32_t bf_ver; /* версия прошивки BlackFin, если есть */ + uint32_t bf_features; /* дополниельные возможности, поддерживаемые прошивкой */ +} t_x502; + + +typedef int32_t (APIENTRY *t_x502_get_devinfo_list_cb)(t_x502_devrec* list, uint32_t size, + uint32_t flags, uint32_t* devcnt); + + + +bool x502_is_E16(t_x502_hnd hnd); + +int x502_check_eeprom(t_x502_hnd hnd, uint32_t flags); + +X502_EXPORT(int32_t) X502_DevRecordInit(t_x502_devrec *info); +X502_EXPORT(int32_t) X502_Open(t_x502_hnd hnd, const char* serial, + const char *devname, t_x502_get_devinfo_list_cb get_list); +X502_EXPORT(int32_t) X502_GetSerialList(char serials[][X502_SERIAL_SIZE], uint32_t size, + uint32_t flags, uint32_t *devcnt, const char *devname, + t_x502_get_devinfo_list_cb get_list); + +X502_EXPORT(int32_t) X502_FpgaRegWrite(t_x502_hnd hnd, uint32_t reg, uint32_t val); +X502_EXPORT(int32_t) X502_FpgaRegRead(t_x502_hnd hnd, uint32_t reg, uint32_t *val); +X502_EXPORT(int32_t) X502_ReloadDevInfo(t_x502_hnd hnd, uint32_t flags); + +int32_t bf_fpga_reg_rd(t_x502_hnd hnd, uint32_t addr, uint32_t* val); +int32_t bf_fpga_reg_wr(t_x502_hnd hnd, uint32_t addr, uint32_t val); + +#define x502_bf_set_par(hnd, par, data, size) X502_BfExecCmd(hnd, L502_BF_CMD_CODE_SET_PARAM, \ + par, data, size, NULL, 0, X502_BF_CMD_DEFAULT_TOUT, NULL) + +#define x502_bf_get_par(hnd, par, data, size) X502_BfExecCmd(hnd, L502_BF_CMD_CODE_GET_PARAM, \ + par, NULL, 0, data, size, X502_BF_CMD_DEFAULT_TOUT, NULL) + + + + +#define FILL_HARD_ID_FLAGS(devflags, hard_id) do { \ + if (hard_id & 0x01) { \ + devflags |= X502_DEVFLAGS_DAC_PRESENT; \ + } else { \ + devflags &= ~X502_DEVFLAGS_DAC_PRESENT; \ + } \ + if (hard_id & 0x02) { \ + devflags |= X502_DEVFLAGS_GAL_PRESENT; \ + } else { \ + devflags &= ~X502_DEVFLAGS_GAL_PRESENT; \ + } \ + if (hard_id & 0x04) {\ + devflags |= X502_DEVFLAGS_BF_PRESENT; \ + } else { \ + devflags &= ~X502_DEVFLAGS_BF_PRESENT; \ + } \ +} while(0) + + +#define STREAM_OUT_IRQ_STEP(hnd) (hnd->stream_pars[X502_STREAM_CH_OUT].step ? \ + hnd->stream_pars[X502_STREAM_CH_OUT].step : \ + X502_DMA_OUT_IRQ_STEP/hnd->set.out_freq_div) + +#define STREAM_OUT_CFG(hnd, err) do { \ + t_x502_stream_ch_params params; \ + memset(¶ms, 0, sizeof(params)); \ + params.buf_size = hnd->stream_pars[X502_STREAM_CH_OUT].buf_size ? \ + hnd->stream_pars[X502_STREAM_CH_OUT].buf_size : \ + X502_DMA_OUT_BUF_SIZE; \ + params.step = STREAM_OUT_IRQ_STEP(hnd); \ + err = hnd->iface_hnd->stream_cfg(hnd, X502_STREAM_CH_OUT, ¶ms); \ +} while(0) + + +#endif // X502API_PRIVATE_H diff --git a/x502/x502api_streams.c b/x502/x502api_streams.c new file mode 100644 index 0000000..fda2071 --- /dev/null +++ b/x502/x502api_streams.c @@ -0,0 +1,717 @@ +#include "x502api_private.h" +#include "x502_fpga_regs.h" +#include "l502_bf_cmd_defs.h" +#include "ltimer.h" + +#include +#include + + +/* разрешение/запрещение потоков ввода вывода в соответствии с полем hnd->streams */ +static int32_t f_set_streams(t_x502_hnd hnd, uint32_t streams) { + int32_t err = X502_ERR_OK; + if (hnd->mode == X502_MODE_FPGA) { + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOARITH_IN_STREAM_ENABLE, + (streams & X502_STREAM_ADC ? 0x01 : 0) | + (streams & X502_STREAM_DIN ? 0x02 : 0)); + } + + if (err == X502_ERR_OK) { + hnd->streams = streams; + } + return err; +} + + + +/* Функция автоматического рассчета параметов DMA для входного потока + на основе частоты сбора данных */ +static int32_t f_stream_in_cfg(t_x502 *hnd) { + int32_t err = X502_ERR_OK; + + + t_x502_stream_ch_params params; + double din_freq = 0; + uint32_t total_size = 0; + double ref_freq; + + memset(¶ms, 0, sizeof(params)); + + X502_GetRefFreqValue(hnd, &ref_freq); + + /* рассчитываем частоту сбора для потоков АЦП и DIN */ + if (hnd->streams & X502_STREAM_ADC) { + double f_frame; + X502_GetAdcFreq(hnd, NULL, &f_frame); + din_freq = f_frame*hnd->set.lch_cnt; + } + + if (hnd->streams & X502_STREAM_DIN) { + din_freq+=ref_freq/hnd->set.din_freq_div; + } + + /* размер полного буфера определяем таким, чтобы его хватило на + L502_DMA_IN_BUF_FOR_SEC секунд постоянного сбора, но не меньше минимального + размера */ + total_size = (uint32_t)(X502_DMA_IN_BUF_FOR_SEC*din_freq); + if (total_size < hnd->iface_hnd->in_stream_buf_min) + total_size = hnd->iface_hnd->in_stream_buf_min; + + + + + + + /* рассчитываем IRQ_STEP, чтобы он был L502_DMA_IN_MAX_IRQ_PER_SEC + в секунду */ + + params.step = hnd->stream_pars[X502_STREAM_CH_IN].step ? + hnd->stream_pars[X502_STREAM_CH_IN].step : + (din_freq>X502_DMA_IN_MAX_IRQ_PER_SEC) ? + (uint32_t)(din_freq/X502_DMA_IN_MAX_IRQ_PER_SEC) : 1; + + /* для эффиктивности делаем размер буфера кратным irq_step */ + total_size = ((total_size+params.step-1)/params.step)*params.step; + + params.buf_size = hnd->stream_pars[X502_STREAM_CH_IN].buf_size ? + hnd->stream_pars[X502_STREAM_CH_IN].buf_size : total_size; + + err = hnd->iface_hnd->stream_cfg(hnd, X502_STREAM_CH_IN, ¶ms); + + if ((err == X502_ERR_OK) && (hnd->mode == X502_MODE_DSP)) { + /* для BlackFin нужно так же установить еще шаг прерываний для + приема данных от SPORT'а */ + uint32_t size; + err = x502_bf_get_par(hnd, L502_BF_PARAM_IN_BUF_SIZE, &size, 1); + if (err == X502_ERR_OK) { + uint32_t instep = params.step; + if (instep>size/4) { + instep=size/4; + } + if (instep > 0x8000) + instep = 0x8000; + err = x502_bf_set_par(hnd, L502_BF_PARAM_IN_STEP_SIZE, &instep, 1); + } + } + return err; +} + + + + + +static int32_t f_out_stream_preload(t_x502 *hnd) { + int32_t err; + STREAM_OUT_CFG(hnd, err); + if (err == X502_ERR_OK) + err = hnd->iface_hnd->stream_start(hnd, X502_STREAM_CH_OUT, 0); + + if ((err == X502_ERR_OK) && (hnd->mode == X502_MODE_DSP)) { + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_PRELOAD, 0, NULL, 0, + NULL, 0, X502_BF_REQ_TOUT, NULL); + } + + if (err == X502_ERR_OK) { + hnd->flags |= PRIV_FLAGS_PRELOAD_DONE; + } + return err; +} + + + + + +X502_EXPORT(int32_t) X502_StreamsEnable(t_x502_hnd hnd, uint32_t streams) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) + err = osspec_mutex_lock(hnd->mutex_cfg, X502_MUTEX_CFG_LOCK_TOUT); + if (err == X502_ERR_OK) { + if (hnd->mode == X502_MODE_DSP) { + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_STREAM_EN, streams, + NULL, 0, NULL, 0, X502_BF_REQ_TOUT, NULL); + if (err == X502_ERR_OK) + hnd->streams |= streams; + } else { + if (hnd->flags & PRIV_FLAGS_STREAM_RUN) { + uint32_t old_streams = hnd->streams; + hnd->streams |= streams; + + /* если не было разрешено потока на ввод до этого, + а при вызове стал разрешен => инициализируем его */ + if ((err == X502_ERR_OK) && !(old_streams & X502_STREAM_ALL_IN) && + (streams & X502_STREAM_ALL_IN)) { + err = f_stream_in_cfg(hnd); + if (err == X502_ERR_OK) + err = hnd->iface_hnd->stream_start(hnd, X502_STREAM_CH_IN, 0); + } else if ((err == X502_ERR_OK) && ((old_streams & X502_STREAM_ALL_IN) != + (streams & X502_STREAM_ALL_IN))) { + err =f_stream_in_cfg(hnd); + } + + + /* Запись в регистры плиса имеет смысл делать только перед запуском + или во время запуска синхронного сбора, т.к. пока сбор не запущен + - не вляют. но в старых версиях ПЛИС могли приводит к сдвигу по + времени первого отсчета АЦП. + Разрешение потока в ПЛИС следует делать уже после stream_start, + чтобы ПК был готов принимать данные */ + err = f_set_streams(hnd, hnd->streams | streams); + } else { + hnd->streams |= streams; + } + } + osspec_mutex_release(hnd->mutex_cfg); + } + return err; +} + +X502_EXPORT(int32_t) X502_StreamsDisable(t_x502_hnd hnd, uint32_t streams) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) + err = osspec_mutex_lock(hnd->mutex_cfg, X502_MUTEX_CFG_LOCK_TOUT); + + if (err == X502_ERR_OK) { + if (hnd->mode == X502_MODE_DSP) { + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_STREAM_DIS, streams, + NULL, 0, NULL, 0, X502_BF_REQ_TOUT, NULL); + if (err == X502_ERR_OK) + hnd->streams &= ~streams; + + } else { + + + if (hnd->flags & PRIV_FLAGS_STREAM_RUN) { + uint32_t old_streams = hnd->streams; + err = f_set_streams(hnd, hnd->streams & ~streams); + /* если все потоки на ввод были запрещены, то + останавливаем их */ + if ((err == X502_ERR_OK) && (old_streams & X502_STREAM_ALL_IN) && + !(hnd->streams & X502_STREAM_ALL_IN)) { + err = hnd->iface_hnd->stream_stop(hnd, X502_STREAM_CH_IN, 0); + } + + if ((err == X502_ERR_OK) && (old_streams & X502_STREAM_ALL_OUT) && + !(hnd->streams & X502_STREAM_ALL_OUT)) { + err = hnd->iface_hnd->stream_stop(hnd, X502_STREAM_CH_OUT, 0); + if (err == X502_ERR_OK) { + hnd->flags &= ~PRIV_FLAGS_PRELOAD_DONE; + } + } + } else { + hnd->streams &= ~streams; + } + } + + osspec_mutex_release(hnd->mutex_cfg); + } + return err; +} + +X502_EXPORT(int32_t) X502_GetEnabledStreams(t_x502_hnd hnd, uint32_t* streams) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if ((err == X502_ERR_OK) && (streams == NULL)) + err = X502_ERR_INVALID_POINTER; + if (err == X502_ERR_OK) + *streams = hnd->streams; + return err; +} + + + +X502_EXPORT(int32_t) X502_StreamsStart(t_x502_hnd hnd) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if ((err == X502_ERR_OK) && (hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_RUNNING; + + if ((err == X502_ERR_OK) && (hnd->mode==X502_MODE_FPGA)) { + uint32_t reg; + err = hnd->iface_hnd->fpga_reg_read(hnd, X502_REGS_IOHARD_IO_MODE, ®); + if ((err == X502_ERR_OK) && !(reg & X502_REGBIT_ADC_SLV_CLK_LOCK_Msk)) + err = X502_ERR_REF_FREQ_NOT_LOCKED; + } + + if (err == X502_ERR_OK) + err = osspec_mutex_lock(hnd->mutex_cfg, X502_MUTEX_CFG_LOCK_TOUT); + + if (err == X502_ERR_OK) { + int in_started = 0; + f_set_streams(hnd, hnd->streams); + + /** @todo корректные действия, если сбор в Bf не остановлен */ + + /* выполняем предзагрузку первого слова выходного потока и + коммутатора АЦП (при наличии DSP это делает DSP) */ + if ((err == X502_ERR_OK) && (hnd->mode==X502_MODE_FPGA)) { + /* предзагрузку значения на вывод делаем только если реально данные уже были + * предзагружены в буфер платы */ + if ((hnd->streams & X502_STREAM_ALL_OUT) && + (hnd->flags & (PRIV_FLAGS_PRELOAD_DONE | PRIV_FLAGS_CYCLE_MODE))) { + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_OUTSWAP_BFCTL, 1); + } + + + /* Предзагрузку АЦП должны делать всегда, так как по этой функции + выполняется часть инициализации параметров синхронного сбора! + + Так как конвейер автомата управления входной коммутации АЦП состоит + из 2-х стадий, для корректного синхронного старта необходимо в + ыполнить два раза предзагрузку. В противном случае, + время момента первого отсчета может не совпадать с моментом + запуска синхронизации + */ + if (err == X502_ERR_OK) + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_PRELOAD_ADC, 1); + if (err == X502_ERR_OK) + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_PRELOAD_ADC, 1); + SLEEP_MS(20); + } + + + /* запуск потока на ввод выполняем при запуске синхронного ввода-вывода */ + if ((err == X502_ERR_OK) && (hnd->streams & X502_STREAM_ALL_IN)) { + err = f_stream_in_cfg(hnd); + if (err == X502_ERR_OK) + err = hnd->iface_hnd->stream_start(hnd, X502_STREAM_CH_IN, 0); + if (err == X502_ERR_OK) + in_started = 1; + } + + if (err == X502_ERR_OK) { + if (hnd->mode == X502_MODE_FPGA) { + /* взводим сигнал GO, указывающий что запущен синхронных ввод-вывод */ + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_GO_SYNC_IO, 1); + } else if (hnd->mode == X502_MODE_DSP) { + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_STREAM_START, 0, + NULL, 0, NULL, 0, X502_BF_CMD_DEFAULT_TOUT, NULL); + } else { + err = X502_ERR_INVALID_MODE; + } + } + + if (err && in_started) { + hnd->iface_hnd->stream_free(hnd, X502_STREAM_CH_IN, 0); + } + + + if (err == X502_ERR_OK) { + hnd->flags |= PRIV_FLAGS_STREAM_RUN; + hnd->proc_adc_ch = 0; + } + + osspec_mutex_release(hnd->mutex_cfg); + } + return err; +} + + +X502_EXPORT(int32_t) X502_StreamsStop(t_x502_hnd hnd) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) + err = osspec_mutex_lock(hnd->mutex_cfg, X502_MUTEX_CFG_LOCK_TOUT); + if (err == X502_ERR_OK) { + int32_t stop_err1, stop_err2; + + if (hnd->mode==X502_MODE_FPGA) { + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_GO_SYNC_IO, 0); + } else if (hnd->mode == X502_MODE_DSP) { + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_STREAM_STOP, 0, + NULL, 0, NULL, 0, X502_BF_CMD_DEFAULT_TOUT, NULL); + } + + stop_err1 = hnd->iface_hnd->stream_free(hnd, X502_STREAM_CH_IN, 0); + stop_err2 = hnd->iface_hnd->stream_free(hnd, X502_STREAM_CH_OUT, 0); + if (err == X502_ERR_OK) + err = stop_err1; + if (err == X502_ERR_OK) + err = stop_err2; + + hnd->flags &= ~(PRIV_FLAGS_STREAM_RUN | PRIV_FLAGS_PRELOAD_DONE | PRIV_FLAGS_CYCLE_MODE); + + osspec_mutex_release(hnd->mutex_cfg); + } + return err; +} + +X502_EXPORT(int32_t) X502_IsRunning(t_x502_hnd hnd) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + uint32_t bf_mode=0; + if ((err == X502_ERR_OK) && (hnd->mode==X502_MODE_DSP)) { + err = x502_bf_get_par(hnd, L502_BF_PARAM_STREAM_MODE, &bf_mode, 1); + } + + if (err == X502_ERR_OK) + err = osspec_mutex_lock(hnd->mutex_cfg, X502_MUTEX_CFG_LOCK_TOUT); + if (err == X502_ERR_OK) { + if (hnd->mode==X502_MODE_DSP) { + if (bf_mode==L502_BF_MODE_IDLE) { + hnd->flags &= ~PRIV_FLAGS_STREAM_RUN; + } else { + hnd->flags |= PRIV_FLAGS_STREAM_RUN; + } + } + + if (!(hnd->flags & PRIV_FLAGS_STREAM_RUN)) + err = X502_ERR_STREAM_IS_NOT_RUNNING; + + osspec_mutex_release(hnd->mutex_cfg); + } + return err; +} + +X502_EXPORT(int32_t) X502_Recv(t_x502_hnd hnd, uint32_t* buf, uint32_t size, uint32_t tout) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if ((err == X502_ERR_OK) && (buf==NULL)) + err = X502_ERR_INVALID_POINTER; + if (err == X502_ERR_OK) { + err = hnd->iface_hnd->stream_read(hnd, buf, size, tout); + } + return err; +} + +X502_EXPORT(int32_t) X502_Send(t_x502_hnd hnd, const uint32_t* buf, uint32_t size, uint32_t tout) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if ((err == X502_ERR_OK) && (buf==NULL)) + err = X502_ERR_INVALID_POINTER; + + /* если разрешен синхронный вывод, но не было + * вызова X502_PreloadStart() или не был установлен синхронных режим, то + * делаем запуск потока вывода при первой записи */ + if ((err == X502_ERR_OK) && (hnd->streams & X502_STREAM_ALL_OUT)) { + if (err == X502_ERR_OK) + err = osspec_mutex_lock(hnd->mutex_cfg, X502_MUTEX_CFG_LOCK_TOUT); + if (err == X502_ERR_OK) { + if (!(hnd->flags & (PRIV_FLAGS_PRELOAD_DONE | PRIV_FLAGS_CYCLE_MODE))) { + err = f_out_stream_preload(hnd); + } + osspec_mutex_release(hnd->mutex_cfg); + } + } + + if (err == X502_ERR_OK) { + err = hnd->iface_hnd->stream_write(hnd, buf, size, tout); + } + return err; +} + +X502_EXPORT(int32_t)X502_PreloadStart(t_x502_hnd hnd) { + int err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) + err = osspec_mutex_lock(hnd->mutex_cfg, X502_MUTEX_CFG_LOCK_TOUT); + if (err == X502_ERR_OK) { + err = f_out_stream_preload(hnd); + osspec_mutex_release(hnd->mutex_cfg); + } + return err; +} + +X502_EXPORT(int32_t) X502_GetRecvReadyCount(t_x502_hnd hnd, uint32_t *rdy_cnt) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if ((err == X502_ERR_OK) && (rdy_cnt==NULL)) + err = X502_ERR_INVALID_POINTER; + if (err == X502_ERR_OK) + err = hnd->iface_hnd->stream_get_rdy_cnt(hnd, X502_STREAM_CH_IN, rdy_cnt); + return err; +} + +X502_EXPORT(int32_t) X502_GetSendReadyCount(t_x502_hnd hnd, uint32_t *rdy_cnt) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if ((err == X502_ERR_OK) && (rdy_cnt==NULL)) + err = X502_ERR_INVALID_POINTER; + if (err == X502_ERR_OK) + err = hnd->iface_hnd->stream_get_rdy_cnt(hnd, X502_STREAM_CH_OUT, rdy_cnt); + return err; +} + + +static int32_t f_check_stream_ch_par_en(t_x502_hnd hnd, uint32_t stream_ch) { + int32_t err = 0; + if (stream_ch == X502_STREAM_CH_IN) { + if ((hnd->flags & PRIV_FLAGS_STREAM_RUN) + && (hnd->streams & X502_STREAM_ALL_IN)) { + err = X502_ERR_STREAM_IS_RUNNING; + } + } else if (stream_ch == X502_STREAM_CH_OUT) { + if ((hnd->flags & (PRIV_FLAGS_PRELOAD_DONE | PRIV_FLAGS_STREAM_RUN)) + && (hnd->streams & X502_STREAM_ALL_OUT)) { + err = X502_ERR_STREAM_IS_RUNNING; + } + } else { + err = X502_ERR_INVALID_STREAM_CH; + } + return err; +} + +X502_EXPORT(int32_t) X502_SetStreamBufSize(t_x502_hnd hnd, uint32_t ch, uint32_t size) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) + err = f_check_stream_ch_par_en(hnd, ch); + if (err == X502_ERR_OK) + hnd->stream_pars[ch].buf_size = size; + return err; +} + +X502_EXPORT(int32_t) X502_SetStreamStep(t_x502_hnd hnd, uint32_t ch, uint32_t step) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) + err = f_check_stream_ch_par_en(hnd, ch); + if (err == X502_ERR_OK) + hnd->stream_pars[ch].step = step; + return err; +} + + +X502_EXPORT(int32_t) X502_OutCycleLoadStart(t_x502_hnd hnd, uint32_t size) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) { + err = osspec_mutex_lock(hnd->mutex_cfg, X502_MUTEX_CFG_LOCK_TOUT); + if (err == X502_ERR_OK) { + /** @todo проверить правильность момента вызова */ + err = hnd->iface_hnd->cycle_load_start(hnd, size); + + /* если еще не была выполнена предзагрузка сигнала, то посылаем DSP + команду, чтобы успел подгрузить данные до старта выдачи */ + if ((err == X502_ERR_OK) && !(hnd->flags & (PRIV_FLAGS_CYCLE_MODE | PRIV_FLAGS_PRELOAD_DONE))) { + if (hnd->mode == X502_MODE_DSP) { + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_PRELOAD, 0, NULL, 0, + NULL, 0, X502_BF_REQ_TOUT, NULL); + } + } + + if (err == X502_ERR_OK) + hnd->flags |= PRIV_FLAGS_CYCLE_MODE; + + osspec_mutex_release(hnd->mutex_cfg); + } + } + return err; +} + + +static int32_t f_cycle_setup_wait(t_x502_hnd hnd, uint32_t tout) { + int32_t err = X502_ERR_OK; + t_ltimer tmr; + uint32_t done = 0; + int first = 1; + ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout)); + + do { + if (!first) { + SLEEP_MS(5); + } else { + first = 0; + } + + err = X502_OutCycleCheckSetupDone(hnd, &done); + } while (!done && !ltimer_expired(&tmr) && (err == X502_ERR_OK)); + + if ((err == X502_ERR_NOT_SUP_BY_DRIVER) || (err == X502_ERR_NOT_SUP_BY_FIRMWARE)) { + done = 1; + err = X502_ERR_OK; + } + + if ((err == X502_ERR_OK) && !done) { + err = X502_ERR_OUT_CYCLE_SETUP_TOUT; + } + + return err; + +} + + + +X502_EXPORT(int32_t) X502_OutCycleSetup(t_x502_hnd hnd, uint32_t flags) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) + err = hnd->iface_hnd->cycle_setup(hnd, flags); + if ((err == X502_ERR_OK) && (flags & X502_OUT_CYCLE_FLAGS_WAIT_DONE)) + err = f_cycle_setup_wait(hnd, X502_OUT_CYCLE_WAIT_TOUT); + return err; +} + + + +X502_EXPORT(int32_t) X502_OutCycleStop(t_x502_hnd hnd, uint32_t flags) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) + err = hnd->iface_hnd->cycle_stop(hnd, flags); + if (err == X502_ERR_OK) { + err = osspec_mutex_lock(hnd->mutex_cfg, X502_MUTEX_CFG_LOCK_TOUT); + if (err == X502_ERR_OK) { + hnd->flags &= ~PRIV_FLAGS_CYCLE_MODE; + osspec_mutex_release(hnd->mutex_cfg); + } + } + + if ((err == X502_ERR_OK) && (flags & X502_OUT_CYCLE_FLAGS_WAIT_DONE)) + err = f_cycle_setup_wait(hnd, X502_OUT_CYCLE_WAIT_TOUT); + + return err; +} + +X502_EXPORT(int32_t) X502_OutCycleCheckSetupDone(t_x502_hnd hnd, uint32_t *done) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) { + err = hnd->iface_hnd->cycle_check_setup(hnd, done); + } + return err; +} + +X502_EXPORT(int32_t) X502_OutGetStatusFlags(t_x502_hnd hnd, uint32_t *status) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) { + err = hnd->iface_hnd->check_feature(hnd, X502_FEATURE_OUT_STATUS_FLAGS); + } + if (err == X502_ERR_OK) { + if (hnd->mode == X502_MODE_DSP) { + if (hnd->bf_features & L502_BF_FEATURE_OUT_STATUS_FLAGS) { + uint32_t recvd; + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_GET_OUT_STATUS, + 0, NULL, 0, status, 1, X502_BF_CMD_DEFAULT_TOUT, + &recvd); + if ((err == X502_ERR_OK) && (recvd < 1)) { + err = X502_ERR_BF_CMD_RETURN_INSUF_DATA; + } + } else { + err = X502_ERR_NOT_IMPLEMENTED; + } + + } else { + err = X502_FpgaRegRead(hnd, X502_REGS_IOHARD_OUTSWAP_ERROR, status); + } + } + return err; +} + + +X502_EXPORT(int32_t) X502_AsyncGetAdcFrame(t_x502_hnd hnd, uint32_t flags, + uint32_t tout, double* data) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if ((err == X502_ERR_OK) && (data==NULL)) + err = X502_ERR_INVALID_POINTER; + if (err == X502_ERR_OK) + err = osspec_mutex_lock(hnd->mutex_cfg, X502_MUTEX_CFG_LOCK_TOUT); + if (err == X502_ERR_OK) { + if (hnd->mode == X502_MODE_FPGA) { + /* если запущен хоть один поток на ввод синхронно, то ввод кадра + * не возможне */ + if ((hnd->flags & PRIV_FLAGS_STREAM_RUN) && (hnd->streams & X502_STREAM_ALL_IN)) { + err = X502_ERR_STREAM_IS_RUNNING; + } + + if (err == X502_ERR_OK) { + int need_stop = 0; + int old_streams = hnd->streams; + uint32_t *wrds = NULL; + int32_t rcv_size = hnd->set.lch_cnt; + + /* разрешаем поток для АЦП */ + err = f_set_streams(hnd, (hnd->streams & ~X502_STREAM_ALL_IN) + | X502_STREAM_ADC); + + hnd->proc_adc_ch = 0; + + if (err == X502_ERR_OK) { + /* инициализируем буфер для приема - достаточно всего на один кадр */ + t_x502_stream_ch_params par = hnd->stream_pars[X502_STREAM_CH_IN]; + hnd->stream_pars[X502_STREAM_CH_IN].buf_size = hnd->iface_hnd->in_stream_buf_min; + hnd->stream_pars[X502_STREAM_CH_IN].step = hnd->set.lch_cnt; + err = f_stream_in_cfg(hnd); + + /* восстанавливаем параметры в структуре, что были заданы + до этой функции */ + hnd->stream_pars[X502_STREAM_CH_IN] = par; + } + + if (err == X502_ERR_OK) { + /* выделяем массив для необработанных отсчетов */ + wrds = malloc(sizeof(wrds[0])*rcv_size); + if (wrds==NULL) + err = X502_ERR_MEMORY_ALLOC; + } + + /* предзагрузка логической таблицы для АЦП */ + if (err == X502_ERR_OK) + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_PRELOAD_ADC, 1); + + /* запуск канала DMA на прием данных */ + if (err == X502_ERR_OK) { + err = hnd->iface_hnd->stream_start(hnd, X502_STREAM_CH_IN, + X502_STREAM_FLAG_SINGLE); + } + + /* если общий синхронный ввод не был запущен - разрешаем его */ + if (err == X502_ERR_OK) { + if (!(hnd->flags & PRIV_FLAGS_STREAM_RUN)) { + err = hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_GO_SYNC_IO, 1); + if (err == X502_ERR_OK) + need_stop = 1; + } + } + + if (err == X502_ERR_OK) { + /* принимаем отсчеты от одного кадра */ + int32_t rcv = X502_Recv(hnd, wrds, rcv_size, tout); + if (rcv < 0) { + err = rcv; + } else if (rcv!=rcv_size) { + err = X502_ERR_RECV_INSUFFICIENT_WORDS; + } else { + err = X502_ProcessAdcData(hnd, wrds, data, (uint32_t*)&rcv_size, flags); + } + } + + /* если в этой функции запустили синхронный сбор, то останвливаем его */ + if (need_stop) + hnd->iface_hnd->fpga_reg_write(hnd, X502_REGS_IOHARD_GO_SYNC_IO, 0); + + hnd->proc_adc_ch = 0; + hnd->iface_hnd->stream_free(hnd, X502_STREAM_CH_IN, 0); + /* восстанавливаем те потоки, которые были разрешены */ + f_set_streams(hnd, old_streams); + free(wrds); + } + } else if (hnd->mode == X502_MODE_DSP) { + err = X502_BfExecCmd(hnd, L502_BF_CMD_CODE_ADC_GET_FRAME, + 0, NULL, 0, (uint32_t*)data, 2*hnd->set.lch_cnt, + X502_BF_CMD_DEFAULT_TOUT, NULL); + } else { + err = X502_ERR_INVALID_MODE; + } + osspec_mutex_release(hnd->mutex_cfg); + } + + return err; +} + + +X502_EXPORT(int32_t) X502_ManualStreamStart(t_x502_hnd hnd, uint32_t stream_ch, uint32_t flags) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) + err = X502_StreamsEnable(hnd, stream_ch == X502_STREAM_CH_IN ? X502_STREAM_ALL_IN : X502_STREAM_ALL_OUT ); + if (err == X502_ERR_OK) { + if (stream_ch == X502_STREAM_CH_IN) { + err = f_stream_in_cfg(hnd); + } else { + STREAM_OUT_CFG(hnd, err); + } + } + if (err == X502_ERR_OK) + err = hnd->iface_hnd->stream_start(hnd, stream_ch, flags); + if ((err == X502_ERR_OK) && (stream_ch == X502_STREAM_CH_OUT)) { + err = osspec_mutex_lock(hnd->mutex_cfg, X502_MUTEX_CFG_LOCK_TOUT); + if (err==X502_ERR_OK) { + hnd->flags |= PRIV_FLAGS_PRELOAD_DONE; + osspec_mutex_release(hnd->mutex_cfg); + } + } + return err; +} + +X502_EXPORT(int32_t) X502_ManualStreamStop(t_x502_hnd hnd, uint32_t stream_ch) { + int32_t err = X502_CHECK_HND_OPENED(hnd); + if (err == X502_ERR_OK) + err = X502_StreamsDisable(hnd, stream_ch == X502_STREAM_CH_IN ? X502_STREAM_ALL_IN : X502_STREAM_ALL_OUT ); + if (err == X502_ERR_OK) + err = hnd->iface_hnd->stream_free(hnd, stream_ch, 0); + return err; +} diff --git a/x502/x502tstp.c b/x502/x502tstp.c new file mode 100644 index 0000000..b6dac74 --- /dev/null +++ b/x502/x502tstp.c @@ -0,0 +1,137 @@ +#include + +#include "x502tstp.h" + +typedef enum { + STREAM_IN_WRD_ADC = 0, + STREAM_IN_WRD_DIN = 1, + STREAM_IN_WRD_MSG = 2, + STREAM_IN_WRD_USR = 3, + STREAM_IN_WRD_TSP = 4, +} t_stream_in_wrd_type; + +#define STREAM_IN_WRD_TYPE(wrd) wrd & 0x80000000 ? STREAM_IN_WRD_ADC : \ + (wrd & 0xFF000000) == 0x0 ? STREAM_IN_WRD_DIN : \ + (wrd & 0xF0000000) == 0x40000000 ? STREAM_IN_WRD_TSP : \ + ((wrd & 0xFF000000)>>24) == 0x01 ? STREAM_IN_WRD_MSG : STREAM_IN_WRD_USR + +static uint64_t get_secssec(uint32_t wrd1, uint32_t wrd2, uint32_t wrd3) { + uint64_t wrd1_sec = TSP_WRD1_GET_SSEC(wrd1); + uint64_t wrd2_sec = TSP_WRD2_GET_SECSSEC(wrd2); + uint64_t wrd3_sec = TSP_WRD3_GET_SEC(wrd3); + uint64_t ret; + + ret = wrd1_sec | wrd2_sec | wrd3_sec; + return ret; + //return TSP_WRD3_GET_SEC(wrd3) | TSP_WRD2_GET_SECSSEC(wrd2) | TSP_WRD1_GET_SSEC(wrd1); +} + +#if 0 +static int32_t get_ssec(uint32_t wrd1, uint32_t wrd2, uint32_t wrd3) { + int32_t ret; + ret = (TSP_WRD2_GET_SSEC(wrd2) << TSP_WRD1_SSEC_LEN) | TSP_WRD1_GET_SSEC(wrd1); + if(ret < 0) { + fprintf(stderr, "unexpected get_ssec ret=%d\n", ret); + while(1); + } + return ret; +} + +static uint32_t get_sec(uint32_t wrd2) { + return ((wrd2 & TSP_WRD2_SEC_MASK) >> TSP_WRD2_SSEC_LEN); +} + +#endif + +X502_EXPORT(bool) X502_tstp_get_lock(t_x502_tstp_state *tstp_state) { + if (TSP_WRD1_IS_LOCK(tstp_state->wrd[1])) { + return true; + } else { + return false; + } +} + +X502_EXPORT(void) X502_tstp_process_wrd(t_x502_tstp_state *tstp_state, uint32_t wrd) { + t_stream_in_wrd_type type = STREAM_IN_WRD_TYPE(wrd); + if (type == STREAM_IN_WRD_TSP) { + if (TSP_WRD_NUM(wrd) == 3) { + uint64_t cur_tspt_time; + + tstp_state->wrd[3] = wrd; + + cur_tspt_time = get_secssec(tstp_state->wrd[1], tstp_state->wrd[2], tstp_state->wrd[3]); + + if (TSP_WRD1_IS_FMARK(tstp_state->wrd[1])) { + tstp_state->tstp_start_time = cur_tspt_time; +#ifdef DEBUG + if (tstp_state->tstp_mark_rcvd) { + fprintf(stderr, "fmark && tstp_mark_rcv unexpected!\n"); + } +#endif + tstp_state->tstp_mark_rcvd = true; + } + tstp_state->adcwrds_after_tstp = 0; + tstp_state->dinwrds_after_tstp = 0; + tstp_state->wrds_after_tstp = 0; + + if (cur_tspt_time < tstp_state->last_tstp_time) { +#ifdef DEBUG + fprintf(stderr, "cur = %" PRIu64 " last = %" PRIu64 "\n", cur_tspt_time, tstp_state->last_tstp_time); + fprintf(stderr, "cur_tstp < last_tstp unexpected!\n"); +#endif + return; + } + + //printf("tstp time = %d.%09d\n", TSTP_SECSSEC_TO_SEC(cur_tspt_time), TSTP_SSEC_TO_NSEC(cur_tspt_time)); + + tstp_state->last_tstp_time = cur_tspt_time; + } else { + if (TSP_WRD_NUM(wrd) == 0) { + tstp_state->wrd[0] = wrd; + } else + if (TSP_WRD_NUM(wrd) == 1) { + tstp_state->wrd[1] = wrd; + } else + if (TSP_WRD_NUM(wrd) == 2) { + tstp_state->wrd[2] = wrd; + } + tstp_state->wrds_after_tstp++; + } + } else { + if (type == STREAM_IN_WRD_DIN) { + tstp_state->dinwrds_after_tstp++; + } else + if (type == STREAM_IN_WRD_ADC) { + tstp_state->adcwrds_after_tstp++; + } + tstp_state->wrds_after_tstp++; + } + tstp_state->processed_wrds++; + tstp_state->cur_wrd = wrd; +} + +X502_EXPORT(void) X502_tstp_init(t_x502_tstp_state *tstp_state, uint32_t adc_freq, uint32_t din_freq) { + memset(tstp_state, 0, sizeof(*tstp_state)); + + tstp_state->adc_freq = adc_freq; + tstp_state->din_freq = din_freq; +} + +X502_EXPORT(void) X502_tstp_get_curwrd_time(t_x502_tstp_state *tstp_state, t_x502_tstptime *ret) { + t_x502_tstptime wrd_time; + t_stream_in_wrd_type type = STREAM_IN_WRD_TYPE(tstp_state->cur_wrd); + + if (type == STREAM_IN_WRD_DIN) { + wrd_time = tstp_state->last_tstp_time + ((double)(tstp_state->dinwrds_after_tstp - 1) / tstp_state->din_freq) * (1U << 31); + } else + if (type == STREAM_IN_WRD_ADC) { + wrd_time = tstp_state->last_tstp_time + ((double)(tstp_state->adcwrds_after_tstp - 1)/ tstp_state->adc_freq) * (1U << 31); + } else { + // чтобы посчитать время слова отличного от АЦП и DIN нужно знать частоту их появления + wrd_time = tstp_state->last_tstp_time + ((double)(tstp_state->adcwrds_after_tstp - 1)/ tstp_state->adc_freq) * (1U << 31); + wrd_time += tstp_state->last_tstp_time + ((double)(tstp_state->dinwrds_after_tstp - 1) / tstp_state->din_freq) * (1U << 31); + } + if (ret) { + *ret = wrd_time; + } +} diff --git a/x502/x502tstp.h b/x502/x502tstp.h new file mode 100644 index 0000000..5e77ef4 --- /dev/null +++ b/x502/x502tstp.h @@ -0,0 +1,140 @@ +#ifndef E502TSTP_H +#define E502TSTP_H + +#include "x502api.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @addtogroup tstpfunc_list Функции для работы с метками времени + @{ **/ + +/***************************************************************************//** + @addtogroup tstptype_list Константы и перечисления + @{ + *****************************************************************************/ + +#define TSP_NSEC_PER_SEC (1000000000) + +#define TSP_WRD1_SSEC_LEN (24) +#define TSP_WRD2_SSEC_LEN (7) +#define TSP_WRD2_SEC_LEN (19) +#define TSP_WRD3_SEC_LEN (13) +#define TSP_SSEC_WIDTH (31) + + +#define TSP_WRD_NUM(wrd) (((wrd) >> 26) & 3) +#define TSP_WRD0_ADC_CLK_NUM_MASK (0x3ffffff) +#define TSP_WRD1_LOCK_MASK (1 << 25) +#define TSP_WRD1_FMARK_MASK (1 << 24) +#define TSP_WRD1_SSEC_MASK ((1 << TSP_WRD1_SSEC_LEN) - 1) +#define TSP_WRD2_SEC_MASK (0x3ffff80) +#define TSP_WRD2_SSEC_MASK ((1 << TSP_WRD2_SSEC_LEN) - 1) +#define TSP_WRD2_SECSSEC_MASK ((1 << (TSP_WRD2_SEC_LEN + TSP_WRD2_SSEC_LEN)) - 1) +#define TSP_WRD3_SEC_MASK ((1 << TSP_WRD3_SEC_LEN) - 1) + +#define TSP_WRD1_IS_FMARK(wrd) (!!((wrd) & TSP_WRD1_FMARK_MASK)) +#define TSP_WRD1_IS_LOCK(wrd) (!!((wrd) & TSP_WRD1_LOCK_MASK)) +#define TSP_WRD1_GET_SSEC(wrd) ((wrd) & TSP_WRD1_SSEC_MASK) +#define TSP_WRD2_GET_SECSSEC(wrd) ((uint64_t)((wrd) & TSP_WRD2_SECSSEC_MASK) << TSP_WRD1_SSEC_LEN) +#define TSP_WRD2_GET_SSEC(wrd) ((wrd) & TSP_WRD2_SSEC_MASK) +#define TSP_WRD3_GET_SEC(wrd) ((uint64_t)((wrd) & TSP_WRD3_SEC_MASK) << (TSP_WRD1_SSEC_LEN + TSP_WRD2_SSEC_LEN + TSP_WRD2_SEC_LEN)) +#define SSEC_MAX (0x7fffffff) +#define TSTP_SSEC_TO_NSEC(time) (time > 0 ? ((uint32_t)((((double)((time) & SSEC_MAX)) / (1U << TSP_SSEC_WIDTH)) * TSP_NSEC_PER_SEC)) : ((uint32_t)((((double)((time*(-1)) & SSEC_MAX)) / (1U << TSP_SSEC_WIDTH)) * TSP_NSEC_PER_SEC))) +#define TSTP_SECSSEC_TO_SEC(time) (time > 0 ? (uint32_t)((time) >> TSP_SSEC_WIDTH) : (uint32_t)((time * -1) >> TSP_SSEC_WIDTH)) +#define TSTP_SECSSEC_TO_SEC_DOUBLE(time) (((double)(time)) / (1U << TSP_SSEC_WIDTH)) +#define TSTP_SEC_TO_SSEC(time) (((uint64_t)(time)) << TSP_SSEC_WIDTH) + +/** Тип данных для хранения времени + * Время хранится в секундах прошедшее с начала этой эпохи (00:00:00 UTC, 1 Января 1970 года) + * Дробный формат хранения 32.31: 32 целых бит, 31 дробных бит, + * старшие 32 бита - секунды, младшие 31 бит сабсекунды = 1 / (1<<31) секунд + + сабсекунды, + */ +typedef uint64_t t_x502_tstptime; + +/** Структура для хранения контекста при обработке потока слов "на ввод" с включенными метками времени */ +typedef struct { + /** значение слов последней метки времени */ + uint32_t wrd[4]; + /** частота АЦП */ + uint32_t adc_freq; + /** частота DIN */ + uint32_t din_freq; + /** время первой метки времени */ + t_x502_tstptime tstp_start_time; + /** признак, что первая метка времени получена */ + bool tstp_mark_rcvd; + /** значение текущего обрабатываемого слова */ + uint32_t cur_wrd; + /** кол-во обработанных слов из потока */ + uint32_t processed_wrds; + /** кол-во слов АЦП после последней метки времени */ + uint32_t adcwrds_after_tstp; + /** кол-во слов DIN после последней метки времени */ + uint32_t dinwrds_after_tstp; + /** общее кол-во слов после последней метки времени */ + uint32_t wrds_after_tstp; + /** время последней метки времени */ + t_x502_tstptime last_tstp_time; +} t_x502_tstp_state; + +/** @} */ + +/***************************************************************************//** + @addtogroup func_tstp Функции для работы с метками времени + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Инициализация tstp_state, в нем хранится текущий контекст для операций с метками времени из потока "на ввод" + + Данная функция инициализирует структуру в которой хранится контекст для работы с метками времени + Необходимо указывать на частоту АЦП и DIN т.к. время для слов между двумя метками + будет считаться в зависимости от частоты. + + @param[in] tstp_state Указатель на существующую струтуру t_x502_tstp_state + @param[in] adc_freq Частота АЦП + @param[in] din_freq Частота DIN +*******************************************************************************/ +X502_EXPORT(void) X502_tstp_init(t_x502_tstp_state *tstp_state, uint32_t adc_freq, uint32_t din_freq); + +/** @brief Обработать очередное слово wrd из потока "на ввод" +* +* Функция должна быть вызвана только один раз и последовательно для каждого слова полученного из X502_Recv +* +* @param[in] tstp_state Указатель на струтуру t_x502_tstp_state предварительно инициализированную через tstp_init() +* @param[in] wrd Слово из потока "на ввод" +*/ +X502_EXPORT(void) X502_tstp_process_wrd(t_x502_tstp_state *tstp_state, uint32_t wrd); + +/** @brief Узнать время текущего обработанного слова +* +* Узнать время текущего слова которое до этого было обработано функцией tstp_process_wrd() +* Формат времени: 32бита - секунды, 31 бит сабсекунды = 1 / (1<<31) секунд +* +* @param[in] tstp_state Указатель на струтуру t_x502_tstp_state предварительно инициализированную через tstp_init() +* @param[in] ret Указатель на t_x502_tstptime по которому будет сохранено расчитанное значение времени для текущего слова +*/ +X502_EXPORT(void) X502_tstp_get_curwrd_time(t_x502_tstp_state *tstp_state, t_x502_tstptime *ret); + +/** @brief Возвращает признак того что часы синхронизированы +* +* Возвращает признак "захват PTP" для текущего обработанного слова +* +* @param[in] tstp_state Указатель на струтуру t_x502_tstp_state предварительно инициализированную через tstp_init() +* +* @return true: присутствует признак "захват PTP" для текущего обработанного слова, false: признак "захват PTP" отсутствует +*/ +X502_EXPORT(bool) X502_tstp_get_lock(t_x502_tstp_state *tstp_state); + +/** @} */ +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif //E502TSTP_H diff --git a/x502api-1.1.34/CMakeLists.txt b/x502api-1.1.34/CMakeLists.txt new file mode 100644 index 0000000..39ab49a --- /dev/null +++ b/x502api-1.1.34/CMakeLists.txt @@ -0,0 +1,219 @@ +cmake_minimum_required(VERSION 2.8.12) + +project(x502api C) +set(PROJECT_VARNAME_PREFIX X502API) + + +set(X502API_VER_MAJOR 1) +set(X502API_VER_MINOR 1) +set(X502API_VER_PATCH 34) +set(X502API_VERSION ${X502API_VER_MAJOR}.${X502API_VER_MINOR}.${X502API_VER_PATCH}) + +add_definitions(-DX502API_VER_MAJOR=${X502API_VER_MAJOR} -DX502API_VER_MINOR=${X502API_VER_MINOR} -DX502API_VER_PATCH=${X502API_VER_PATCH}) + +option(X502API_ENABLE_DEV_L502 "Enable L-502 device support" ON) +option(X502API_ENABLE_DEV_E502 "Enable E-502 device support" ON) + + +set(X502API_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/include) +set(X502API_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib) +set(LTIMER_DIR ${X502API_LIB_DIR}/ltimer) +set(LCSPEC_DIR ${X502API_LIB_DIR}/lcspec) +set(OSSPEC_DIR ${X502API_LIB_DIR}/osspec) +set(X502_LIBS_CMAKE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/x502libs.cmake) + +if(NOT X502_INSTALL_LIB) + set(X502_INSTALL_LIB lib) +endif(NOT X502_INSTALL_LIB) +if (NOT X502_INSTALL_INCLUDE) + set(X502_INSTALL_INCLUDE include) +endif(NOT X502_INSTALL_INCLUDE) +set(X502_INSTALL_PASCAL pas) + + +file(MAKE_DIRECTORY ${X502API_INCLUDE_DIR}) +set(X502API_PREPARE_HEADERS ON) + +#для GCC устанавливаем повышенный уроень предупреждения компилятора +if(CMAKE_COMPILER_IS_GNUCC) + + include(CheckCCompilerFlag) + set(CHECK_FLAGS + -Wall -Wformat-security -Wundef -Wshadow -Wpointer-arith -Wcast-align + -Wwrite-strings -Wsign-compare -Waggregate-return -Winline -Wno-aggregate-return + -Werror=implicit-int -Werror=implicit-function-declaration -Werror=strict-prototypes -Werror=return-type + -Wextra -Winit-self -Wstrict-aliasing -Wfloat-equal + -Wunsafe-loop-optimizations -Wlogical-op + -Wno-unused-parameter -Wno-unused-variable) + foreach(CUR_FLAG ${CHECK_FLAGS}) + # для корректного названия переменной флага заменяем недопустимые + # символы - и = в названии флага на _ + string(REGEX REPLACE "-|=" "_" FLAG_VARNAME FLAG_${CUR_FLAG}_ENABLE) + + CHECK_C_COMPILER_FLAG(${CUR_FLAG} ${FLAG_VARNAME}) + if(${FLAG_VARNAME}) + set(COMPILE_FLAGS "${COMPILE_FLAGS} ${CUR_FLAG}") + endif() + endforeach(CUR_FLAG) + + + + CHECK_C_COMPILER_FLAG(-Wl,--no-undefined FLAG_NO_UNDEFINED) + if(FLAG_NO_UNDEFINED) + set(LINK_FLAGS "${LINK_FLAGS} -Wl,--no-undefined") + else(FLAG_NO_UNDEFINED) + message(STATUS "LINKER FLAG -Wl,--no-undefined WAS NOT FOUND!") + endif(FLAG_NO_UNDEFINED) + + CHECK_C_COMPILER_FLAG(-fvisibility=hidden FLAG_FVISIBILITY) + if (FLAG_FVISIBILITY) + set(COMPILE_FLAGS "${COMPILE_FLAGS} -fvisibility=hidden") + endif(FLAG_FVISIBILITY) +endif(CMAKE_COMPILER_IS_GNUCC) + +#при сборке MSVC компилируем runtime-библиотеку статически, чтобы не зависеть от версии +if(MSVC) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + foreach(flag_var + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + endif(${flag_var} MATCHES "/MD") + endforeach(flag_var) + + #убираем флаг /INCREMENTAL, так как в MSVC с ним могут быть + #внутренние ошибки линковщика + foreach(flag_var + CMAKE_SHARED_LINKER_FLAGS + CMAKE_SHARED_LINKER_FLAGS_DEBUG + CMAKE_SHARED_LINKER_FLAGS_RELEASE + CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL + CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO + CMAKE_SHARED_EXE_FLAGS + CMAKE_SHARED_EXE_FLAGS_DEBUG + CMAKE_SHARED_EXE_FLAGS_RELEASE + CMAKE_SHARED_EXE_FLAGS_MINSIZEREL + CMAKE_SHARED_EXE_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES "/INCREMENTAL:YES") + string(REGEX REPLACE "/INCREMENTAL:YES" "/INCREMENTAL:NO" ${flag_var} "${${flag_var}}") + endif(${flag_var} MATCHES "/INCREMENTAL:YES") + endforeach(flag_var) +endif(MSVC) + + +include(${LTIMER_DIR}/ltimer.cmake) +set(OSSPEC_USE_MUTEX ON) +set(OSSPEC_USE_THREADS OFF) +set(OSSPEC_USE_EVENTS OFF) +include(${OSSPEC_DIR}/osspec.cmake) +set(X502API_COMPILE_DEFINITIONS ${LTIMER_DEFINITIONS} ${OSSPEC_DEFINITIONS}) + +set(SOURCES + src/x502api.c + src/x502api_config.c + src/x502api_errs.c + src/x502api_streams.c + src/x502api_async.c + src/x502api_bf.c + src/x502api_eeprom.c + src/x502tstp.c + lib/crc/fast_crc.c + ${OSSPEC_SOURCES} + ${LTIMER_SOURCES}) + + +set(SETUP_HEADERS + src/lcard_pstdint.h + src/x502api.h + src/x502tstp.h + ) + +set(HEADERS + src/l502_bf_cmd_defs.h + src/lboot_req.h + src/x502_fpga_regs.h + src/x502api_private.h + src/x502_eeprom.h + src/osspec_cfg.h + src/fast_crc_cfg.h + ${OSSPEC_HEADERS} + ${LTIMER_HEADERS}) + +set(LIBS + ${LTIMER_LIBS} + ${OSSPEC_LIBS}) + + +include_directories(src lib/crc ${X502API_LIB_DIR}/osspec) + + +include(${X502_LIBS_CMAKE_FILE}) + + +add_subdirectory(devs) + +if(BUILD_EXAMPLES OR BUILD_ALL) + add_subdirectory(examples/c) +endif(BUILD_EXAMPLES OR BUILD_ALL) + +if(BUILD_TESTS) + add_subdirectory(tests) +endif(BUILD_TESTS) + +if(LPCIE_DOCGEN_DIR) + include(${LPCIE_DOCGEN_DIR}/LDoxyToPdf.cmake) + set(PDF_IMG_FILES doc/images/adc_frame.jpeg) + set(DOC_DOXYGEN_PRJ_FILE doc/Doxyfile.in) + set(DOXYGEN_INPUT_FILES devs/l502/l502api.h devs/e502/e502api.h src/x502api.h src/x502tstp.h doc/mainpage.md doc/about.md doc/setup.md doc/gen_descr.md doc/tstp_descr.md) + ADD_DOXY_TO_PDF_TARGET(x502api) +endif(LPCIE_DOCGEN_DIR) + +#цели для сборки пакетов +if(UNIX AND CMAKE_EXT_INCLUDE_DIR) + set(PACKAGE_NAME ${PROJECT_NAME}) + set(PACKAGE_VERSION ${X502API_VERSION}) + set(PACKAGE_ARCH_INSTALL_FILE ${PACKAGE_NAME}.install) + include(${CMAKE_EXT_INCLUDE_DIR}/packages/packages.cmake) +endif(UNIX AND CMAKE_EXT_INCLUDE_DIR) + + + +if(WIN32 AND LCARD_SDK_MAKE_INSTALLER) + set(X502API_CONVERTED_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/conv_src) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/x64) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/x86) + file(MAKE_DIRECTORY ${X502API_CONVERTED_SOURCE_DIR}) + + string(REGEX REPLACE "/" "\\\\" ICONV_EXEC ${ICONV_EXEC}) + string(REGEX REPLACE "/" "\\\\" X502API_CONVERTED_SOURCE_DIR ${X502API_CONVERTED_SOURCE_DIR}) + set(X502API_ORIG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + string(REGEX REPLACE "/" "\\\\" X502API_ORIG_SOURCE_DIR ${X502API_ORIG_SOURCE_DIR}) + + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/win_install/conv_src_cp1251.bat.in + ${CMAKE_CURRENT_BINARY_DIR}/conv_src_cp1251.bat @ONLY) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/win_install/make64.bat.in + ${CMAKE_CURRENT_BINARY_DIR}/x64/make.bat @ONLY) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/win_install/make32.bat.in + ${CMAKE_CURRENT_BINARY_DIR}/x86/make.bat @ONLY) + add_custom_target(convert_src + COMMAND cmd /c conv_src_cp1251.bat + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + add_custom_target(x502api_64 + COMMAND cmd /c make.bat + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/x64 + DEPENDS convert_src) + + #x86 версию собираем 2008 студией (Express) для совместимости с Win2000 + #set(VS2008_SETENV "${VS2008_DIR}/vcvarsall.bat") + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/x86) + add_custom_target(x502api_32 + COMMAND cmd /c make.bat + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/x86 + DEPENDS convert_src + ) +endif() + + + diff --git a/x502api-1.1.34/arch/PKGBUILD.in b/x502api-1.1.34/arch/PKGBUILD.in new file mode 100644 index 0000000..13312b0 --- /dev/null +++ b/x502api-1.1.34/arch/PKGBUILD.in @@ -0,0 +1,33 @@ +pkgname=@PACKAGE_NAME@ +pkgver=@PACKAGE_VERSION@ +pkgrel=1 +pkgdesc="library for control L-Card L502/E502 data acqusition modules" +arch=('i686' 'x86_64') +url="http://lcard.ru" +license=('LGPL') +groups=() +depends=('libusb' 'udev' 'avahi') +makedepends=('cmake' 'libusb') +optdepends=() +provides=() +conflicts=() +replaces=() +backup=() +options=() +install=@PACKAGE_ARCH_INSTALL_FILE@ +source=(@PACKAGE_SRC_TAR_NAME@) +replaces=('l502api') + + +build() { + cd "$srcdir/$pkgname-$pkgver" + cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr ./ + make + } + +package() { + cd "$srcdir/$pkgname-$pkgver" + make DESTDIR="$pkgdir/" install + } + + diff --git a/x502api-1.1.34/arch/x502api.install.in b/x502api-1.1.34/arch/x502api.install.in new file mode 100644 index 0000000..030b7e6 --- /dev/null +++ b/x502api-1.1.34/arch/x502api.install.in @@ -0,0 +1,17 @@ +# This is a default template for a post-install scriptlet. +# Uncomment only required functions and remove any functions +# you don't need (and this header). + +## arg 1: the new package version +post_install() { + # обновляем правила udev, чтобы разрешить доступ к крейтам по USB + if [ -x /sbin/udevadm ]; then + /sbin/udevadm control --reload-rules + /sbin/udevadm trigger --subsystem-match=usb --attr-match=idVendor=2a52 --attr-match=idProduct=e502 + /sbin/udevadm trigger --subsystem-match=usb --attr-match=idVendor=2a52 --attr-match=idProduct=0e16 + fi + exit 0 +} + + +# vim:set ts=2 sw=2 et: diff --git a/x502api-1.1.34/debian/changelog b/x502api-1.1.34/debian/changelog new file mode 100644 index 0000000..4e671b1 --- /dev/null +++ b/x502api-1.1.34/debian/changelog @@ -0,0 +1,5 @@ +x502api (1.1.34) stable; urgency=low + + * Initial Release. + + -- Borisov Alexey Sat, 31 Mar 2012 12:25:54 +0400 diff --git a/x502api-1.1.34/debian/changelog.in b/x502api-1.1.34/debian/changelog.in new file mode 100644 index 0000000..d90749f --- /dev/null +++ b/x502api-1.1.34/debian/changelog.in @@ -0,0 +1,5 @@ +@PACKAGE_NAME@ (@PACKAGE_VERSION@) stable; urgency=low + + * Initial Release. + + -- Borisov Alexey Sat, 31 Mar 2012 12:25:54 +0400 diff --git a/x502api-1.1.34/debian/compat b/x502api-1.1.34/debian/compat new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/x502api-1.1.34/debian/compat @@ -0,0 +1 @@ +8 diff --git a/x502api-1.1.34/debian/control b/x502api-1.1.34/debian/control new file mode 100644 index 0000000..7048123 --- /dev/null +++ b/x502api-1.1.34/debian/control @@ -0,0 +1,54 @@ +Source: x502api +Priority: optional +Maintainer: Borisov Alexey +Build-Depends: debhelper (>= 8.0.0), cmake, libusb-1.0-0-dev, libavahi-common-dev, libavahi-client-dev +Standards-Version: 3.9.3 +Section: libs +Homepage: "http://www.lcard.ru" +Replaces: l502api + +Package: libx502api1-dev +Section: libdevel +Architecture: any +Depends: libx502api1 (= ${binary:Version}), + libe502api1 (= ${binary:Version}), + libl502api1 (= ${binary:Version}), + ${misc:Depends} +Replaces: libl502api1-dev +Description:Library for control L-Card L502/E502 devices + Library that allow you control L-Card L502 and E502 data acquisition devices. + . + This package contains the development files. + . + You need these files if you writing your own software. Non-developers likely + have little use for this package. + +Package: libx502api1 +Section: libs +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description:Library with general function for L-Card L502/E502 devices + Library that allow you control L-Card L502 and E502 data acquisition devices. + . + This package contains the shared object files. + +Package: libe502api1 +Section: libs +Architecture: any +Suggests: avahi-daemon +Depends: ${shlibs:Depends}, ${misc:Depends}, udev +Description:Library for control L-Card E502 device + Library that allow you control L-Card E502 data acquisition devices. + . + This package contains the shared object files. + +Package: libl502api1 +Section: libs +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description:Library for control L-Card L502 device + Library that allow you control L-Card L502 data acquisition devices. + . + This package contains the shared object files. + + diff --git a/x502api-1.1.34/debian/control.in b/x502api-1.1.34/debian/control.in new file mode 100644 index 0000000..ea424c0 --- /dev/null +++ b/x502api-1.1.34/debian/control.in @@ -0,0 +1,54 @@ +Source: @PACKAGE_NAME@ +Priority: optional +Maintainer: Borisov Alexey +Build-Depends: debhelper (>= 8.0.0), cmake, libusb-1.0-0-dev, libavahi-common-dev, libavahi-client-dev +Standards-Version: 3.9.3 +Section: libs +Homepage: "http://www.lcard.ru" +Replaces: l502api + +Package: libx502api1-dev +Section: libdevel +Architecture: any +Depends: libx502api1 (= ${binary:Version}), + libe502api1 (= ${binary:Version}), + libl502api1 (= ${binary:Version}), + ${misc:Depends} +Replaces: libl502api1-dev +Description:Library for control L-Card L502/E502 devices + Library that allow you control L-Card L502 and E502 data acquisition devices. + . + This package contains the development files. + . + You need these files if you writing your own software. Non-developers likely + have little use for this package. + +Package: libx502api1 +Section: libs +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description:Library with general function for L-Card L502/E502 devices + Library that allow you control L-Card L502 and E502 data acquisition devices. + . + This package contains the shared object files. + +Package: libe502api1 +Section: libs +Architecture: any +Suggests: avahi-daemon +Depends: ${shlibs:Depends}, ${misc:Depends}, udev +Description:Library for control L-Card E502 device + Library that allow you control L-Card E502 data acquisition devices. + . + This package contains the shared object files. + +Package: libl502api1 +Section: libs +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description:Library for control L-Card L502 device + Library that allow you control L-Card L502 data acquisition devices. + . + This package contains the shared object files. + + diff --git a/x502api-1.1.34/debian/copyright b/x502api-1.1.34/debian/copyright new file mode 100644 index 0000000..a7b4738 --- /dev/null +++ b/x502api-1.1.34/debian/copyright @@ -0,0 +1,25 @@ +Format: http://dep.debian.net/deps/dep5 +Upstream-Name: x502api +Source: + +Files: * +Copyright: 2025 L-Card +License: LGPL-3.0+ + +License: LGPL-3.0+ + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + . + This package 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 General Public License + along with this program. If not, see . + . + On Debian systems, the complete text of the GNU Lesser General + Public License can be found in "/usr/share/common-licenses/LGPL-3". + diff --git a/x502api-1.1.34/debian/copyright.in b/x502api-1.1.34/debian/copyright.in new file mode 100644 index 0000000..c28fe07 --- /dev/null +++ b/x502api-1.1.34/debian/copyright.in @@ -0,0 +1,25 @@ +Format: http://dep.debian.net/deps/dep5 +Upstream-Name: @PACKAGE_NAME@ +Source: + +Files: * +Copyright: @PACKAGE_BUILD_YEAR@ L-Card +License: LGPL-3.0+ + +License: LGPL-3.0+ + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + . + This package 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 General Public License + along with this program. If not, see . + . + On Debian systems, the complete text of the GNU Lesser General + Public License can be found in "/usr/share/common-licenses/LGPL-3". + diff --git a/x502api-1.1.34/debian/libe502api1.dirs b/x502api-1.1.34/debian/libe502api1.dirs new file mode 100644 index 0000000..4be4d60 --- /dev/null +++ b/x502api-1.1.34/debian/libe502api1.dirs @@ -0,0 +1,2 @@ +usr/lib +lib/udev/rules.d diff --git a/x502api-1.1.34/debian/libe502api1.install b/x502api-1.1.34/debian/libe502api1.install new file mode 100644 index 0000000..813adae --- /dev/null +++ b/x502api-1.1.34/debian/libe502api1.install @@ -0,0 +1,3 @@ +usr/lib/libe502api.so.* +lib/udev/rules.d/e502.rules +lib/udev/rules.d/e16.rules diff --git a/x502api-1.1.34/debian/libe502api1.postinst b/x502api-1.1.34/debian/libe502api1.postinst new file mode 100644 index 0000000..faaadd2 --- /dev/null +++ b/x502api-1.1.34/debian/libe502api1.postinst @@ -0,0 +1,35 @@ +#!/bin/sh +# postinst script for ldaemon + +#set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + configure) + # обновляем правила udev, чтобы разрешить доступ к крейтам по USB + if [ -x /sbin/udevadm ]; then + /sbin/udevadm control --reload-rules + /sbin/udevadm trigger --subsystem-match=usb --attr-match=idVendor=2a52 --attr-match=idProduct=e502 + /sbin/udevadm trigger --subsystem-match=usb --attr-match=idVendor=2a52 --attr-match=idProduct=0e16 + fi + ;; + abort-upgrade|abort-remove|abort-deconfigure) + ;; + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# + diff --git a/x502api-1.1.34/debian/libl502api1.dirs b/x502api-1.1.34/debian/libl502api1.dirs new file mode 100644 index 0000000..6845771 --- /dev/null +++ b/x502api-1.1.34/debian/libl502api1.dirs @@ -0,0 +1 @@ +usr/lib diff --git a/x502api-1.1.34/debian/libl502api1.install b/x502api-1.1.34/debian/libl502api1.install new file mode 100644 index 0000000..de25749 --- /dev/null +++ b/x502api-1.1.34/debian/libl502api1.install @@ -0,0 +1 @@ +usr/lib/libl502api.so.* diff --git a/x502api-1.1.34/debian/libx502api1-dev.dirs b/x502api-1.1.34/debian/libx502api1-dev.dirs new file mode 100644 index 0000000..4418816 --- /dev/null +++ b/x502api-1.1.34/debian/libx502api1-dev.dirs @@ -0,0 +1,2 @@ +usr/lib +usr/include diff --git a/x502api-1.1.34/debian/libx502api1-dev.install b/x502api-1.1.34/debian/libx502api1-dev.install new file mode 100644 index 0000000..5f8312f --- /dev/null +++ b/x502api-1.1.34/debian/libx502api1-dev.install @@ -0,0 +1,2 @@ +usr/include/* +usr/lib/lib*.so \ No newline at end of file diff --git a/x502api-1.1.34/debian/libx502api1.dirs b/x502api-1.1.34/debian/libx502api1.dirs new file mode 100644 index 0000000..6845771 --- /dev/null +++ b/x502api-1.1.34/debian/libx502api1.dirs @@ -0,0 +1 @@ +usr/lib diff --git a/x502api-1.1.34/debian/libx502api1.install b/x502api-1.1.34/debian/libx502api1.install new file mode 100644 index 0000000..7ad9a1e --- /dev/null +++ b/x502api-1.1.34/debian/libx502api1.install @@ -0,0 +1 @@ +usr/lib/libx502api.so.* diff --git a/x502api-1.1.34/debian/rules b/x502api-1.1.34/debian/rules new file mode 100755 index 0000000..43dea36 --- /dev/null +++ b/x502api-1.1.34/debian/rules @@ -0,0 +1,16 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ + +override_dh_auto_configure: + dh_auto_configure -- -DUDEV_RULES_DIR=/lib/udev/rules.d -DCMAKE_BUILD_TYPE=Release diff --git a/x502api-1.1.34/debian/source/format b/x502api-1.1.34/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/x502api-1.1.34/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/x502api-1.1.34/devs/CMakeLists.txt b/x502api-1.1.34/devs/CMakeLists.txt new file mode 100644 index 0000000..3f7ce9a --- /dev/null +++ b/x502api-1.1.34/devs/CMakeLists.txt @@ -0,0 +1,7 @@ +if(X502API_ENABLE_DEV_E502) + add_subdirectory(e502) +endif(X502API_ENABLE_DEV_E502) + +if(X502API_ENABLE_DEV_L502) + add_subdirectory(l502) +endif(X502API_ENABLE_DEV_L502) diff --git a/x502api-1.1.34/devs/e502/.e502api_dnssd.c.kate-swp b/x502api-1.1.34/devs/e502/.e502api_dnssd.c.kate-swp new file mode 100644 index 0000000..4a3d6e0 Binary files /dev/null and b/x502api-1.1.34/devs/e502/.e502api_dnssd.c.kate-swp differ diff --git a/x502api-1.1.34/devs/e502/CMakeLists.txt b/x502api-1.1.34/devs/e502/CMakeLists.txt new file mode 100644 index 0000000..48bc9d4 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/cmake/modules/FindAvahi.cmake b/x502api-1.1.34/devs/e502/cmake/modules/FindAvahi.cmake new file mode 100644 index 0000000..58caf29 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/cmake/modules/FindDNSSD.cmake b/x502api-1.1.34/devs/e502/cmake/modules/FindDNSSD.cmake new file mode 100644 index 0000000..410e6d5 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/cmake/modules/FindFFTW3.cmake b/x502api-1.1.34/devs/e502/cmake/modules/FindFFTW3.cmake new file mode 100644 index 0000000..81990ff --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/cmake/modules/FindGpgError.cmake b/x502api-1.1.34/devs/e502/cmake/modules/FindGpgError.cmake new file mode 100755 index 0000000..2069cb3 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/cmake/modules/FindLTRAPI.cmake b/x502api-1.1.34/devs/e502/cmake/modules/FindLTRAPI.cmake new file mode 100644 index 0000000..86b5cb6 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/cmake/modules/FindLibCBOR.cmake b/x502api-1.1.34/devs/e502/cmake/modules/FindLibCBOR.cmake new file mode 100644 index 0000000..4bcc33a --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/cmake/modules/FindLibUSB.cmake b/x502api-1.1.34/devs/e502/cmake/modules/FindLibUSB.cmake new file mode 100644 index 0000000..66ebc23 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/cmake/modules/FindLibdaemon.cmake b/x502api-1.1.34/devs/e502/cmake/modules/FindLibdaemon.cmake new file mode 100644 index 0000000..e212118 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/cmake/modules/FindLibgcrypt.cmake b/x502api-1.1.34/devs/e502/cmake/modules/FindLibgcrypt.cmake new file mode 100755 index 0000000..2423147 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/cmake/modules/FindMKL.cmake b/x502api-1.1.34/devs/e502/cmake/modules/FindMKL.cmake new file mode 100644 index 0000000..29525a9 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/cmake/modules/FindMODBUS.cmake b/x502api-1.1.34/devs/e502/cmake/modules/FindMODBUS.cmake new file mode 100644 index 0000000..108f2ea --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/cmake/modules/FindX502API.cmake b/x502api-1.1.34/devs/e502/cmake/modules/FindX502API.cmake new file mode 100644 index 0000000..f31372a --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/cmake/modules/FindXLSXWriter.cmake b/x502api-1.1.34/devs/e502/cmake/modules/FindXLSXWriter.cmake new file mode 100644 index 0000000..8040d17 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/cmake/modules/FindZMQ.cmake b/x502api-1.1.34/devs/e502/cmake/modules/FindZMQ.cmake new file mode 100644 index 0000000..1131909 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/e16.rules b/x502api-1.1.34/devs/e502/e16.rules new file mode 100644 index 0000000..e842599 --- /dev/null +++ b/x502api-1.1.34/devs/e502/e16.rules @@ -0,0 +1 @@ +SUBSYSTEM=="usb", ATTRS{idVendor}=="2a52", ATTRS{idProduct}=="0e16", MODE="0666" diff --git a/x502api-1.1.34/devs/e502/e502.rules b/x502api-1.1.34/devs/e502/e502.rules new file mode 100644 index 0000000..9552016 --- /dev/null +++ b/x502api-1.1.34/devs/e502/e502.rules @@ -0,0 +1 @@ +SUBSYSTEM=="usb", ATTRS{idVendor}=="2a52", ATTRS{idProduct}=="e502", MODE="0666" diff --git a/x502api-1.1.34/devs/e502/e502_cm4_defs.h b/x502api-1.1.34/devs/e502/e502_cm4_defs.h new file mode 100644 index 0000000..f4bdd20 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/e502_eth_config.h b/x502api-1.1.34/devs/e502/e502_eth_config.h new file mode 100644 index 0000000..20659fe --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/e502_fpga_regs.h b/x502api-1.1.34/devs/e502/e502_fpga_regs.h new file mode 100644 index 0000000..a5abc0e --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/e502_tcp_protocol.h b/x502api-1.1.34/devs/e502/e502_tcp_protocol.h new file mode 100644 index 0000000..8cc1433 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/e502api.c b/x502api-1.1.34/devs/e502/e502api.c new file mode 100644 index 0000000..6a837ae --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/e502api.def b/x502api-1.1.34/devs/e502/e502api.def new file mode 100644 index 0000000..ae8c27f --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/e502api.h b/x502api-1.1.34/devs/e502/e502api.h new file mode 100644 index 0000000..6c1bdcd --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/e502api.rc.in b/x502api-1.1.34/devs/e502/e502api.rc.in new file mode 100644 index 0000000..b246665 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/e502api_dnssd.c b/x502api-1.1.34/devs/e502/e502api_dnssd.c new file mode 100644 index 0000000..6c9cdcf --- /dev/null +++ b/x502api-1.1.34/devs/e502/e502api_dnssd.c @@ -0,0 +1,1061 @@ +#include + +#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/x502api-1.1.34/devs/e502/e502api_eth_config.c b/x502api-1.1.34/devs/e502/e502api_eth_config.c new file mode 100644 index 0000000..a300acb --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/e502api_private.h b/x502api-1.1.34/devs/e502/e502api_private.h new file mode 100644 index 0000000..64261c0 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/e502api_tcp.c b/x502api-1.1.34/devs/e502/e502api_tcp.c new file mode 100644 index 0000000..480cc15 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/e502api_tcp_private.h b/x502api-1.1.34/devs/e502/e502api_tcp_private.h new file mode 100644 index 0000000..152532e --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/e502api_usb.c b/x502api-1.1.34/devs/e502/e502api_usb.c new file mode 100644 index 0000000..6f3d720 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/Makefile.am b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/Makefile.am new file mode 100644 index 0000000..80e3705 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/Makefile.in b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/Makefile.in new file mode 100644 index 0000000..0fcff77 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/core.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/core.c new file mode 100644 index 0000000..f3d7ece --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/descriptor.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/descriptor.c new file mode 100644 index 0000000..93d34ce --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/hotplug.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/hotplug.c new file mode 100644 index 0000000..eba8f98 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/hotplug.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/hotplug.h new file mode 100644 index 0000000..321a0a8 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/io.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/io.c new file mode 100644 index 0000000..b9ca767 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/libusb-1.0.def b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/libusb-1.0.def new file mode 100644 index 0000000..d45cfc5 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/libusb-1.0.rc b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/libusb-1.0.rc new file mode 100644 index 0000000..3dce6d5 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/libusb.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/libusb.h new file mode 100644 index 0000000..53083ec --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/libusbi.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/libusbi.h new file mode 100644 index 0000000..d59ec30 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/msvc/config.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/msvc/config.h new file mode 100644 index 0000000..4b418db --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/msvc/errno/errno.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/msvc/errno/errno.h new file mode 100644 index 0000000..07d15e3 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/msvc/inttypes/inttypes.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/msvc/inttypes/inttypes.h new file mode 100644 index 0000000..289bb50 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/msvc/missing.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/msvc/missing.h new file mode 100644 index 0000000..183b9d3 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/msvc/stdint/stdint.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/msvc/stdint/stdint.h new file mode 100644 index 0000000..00988d9 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/darwin_usb.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/darwin_usb.c new file mode 100644 index 0000000..19174b1 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/darwin_usb.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/darwin_usb.h new file mode 100644 index 0000000..8838881 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/linux_netlink.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/linux_netlink.c new file mode 100644 index 0000000..306f0b4 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/linux_udev.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/linux_udev.c new file mode 100644 index 0000000..0394048 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/linux_usbfs.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/linux_usbfs.c new file mode 100644 index 0000000..db21710 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/linux_usbfs.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/linux_usbfs.h new file mode 100644 index 0000000..f2404ab --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/netbsd_usb.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/netbsd_usb.c new file mode 100644 index 0000000..ed4f50e --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/openbsd_usb.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/openbsd_usb.c new file mode 100644 index 0000000..ff470be --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/poll_posix.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/poll_posix.c new file mode 100644 index 0000000..eeaf5dc --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/poll_posix.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/poll_posix.h new file mode 100644 index 0000000..5b4b2c9 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/poll_windows.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/poll_windows.c new file mode 100644 index 0000000..03160bd --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/poll_windows.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/poll_windows.h new file mode 100644 index 0000000..9163fec --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/threads_posix.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/threads_posix.c new file mode 100644 index 0000000..95c1027 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/threads_posix.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/threads_posix.h new file mode 100644 index 0000000..d7a5d21 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/threads_windows.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/threads_windows.c new file mode 100644 index 0000000..5537b1f --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/threads_windows.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/threads_windows.h new file mode 100644 index 0000000..2b82925 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/wince_usb.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/wince_usb.c new file mode 100644 index 0000000..4d9b3cc --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/wince_usb.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/wince_usb.h new file mode 100644 index 0000000..bc61b5c --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/windows_common.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/windows_common.h new file mode 100644 index 0000000..fe95d79 --- /dev/null +++ b/x502api-1.1.34/devs/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/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/windows_usb.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/windows_usb.c new file mode 100644 index 0000000..9880f96 --- /dev/null +++ b/x502api-1.1.34/devs/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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libusb-1.0/libusbi.h" +#include "poll_windows.h" +#include "windows_usb.h" + +// The 2 macros below are used in conjunction with safe loops. +#define LOOP_CHECK(fcall) { r=fcall; if (r != LIBUSB_SUCCESS) continue; } +#define LOOP_BREAK(err) { r=err; continue; } + +// Helper prototypes +static int windows_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian); +static int windows_clock_gettime(int clk_id, struct timespec *tp); +unsigned __stdcall windows_clock_gettime_threaded(void* param); +// Common calls +static int common_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface); + +// WinUSB-like API prototypes +static int winusbx_init(int sub_api, struct libusb_context *ctx); +static int winusbx_exit(int sub_api); +static int winusbx_open(int sub_api, struct libusb_device_handle *dev_handle); +static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle); +static int winusbx_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting); +static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int winusbx_abort_transfers(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_abort_control(int sub_api, struct usbi_transfer *itransfer); +static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_handle); +static int winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size); +// HID API prototypes +static int hid_init(int sub_api, struct libusb_context *ctx); +static int hid_exit(int sub_api); +static int hid_open(int sub_api, struct libusb_device_handle *dev_handle); +static void hid_close(int sub_api, struct libusb_device_handle *dev_handle); +static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int hid_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting); +static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer); +static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer); +static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int hid_abort_transfers(int sub_api, struct usbi_transfer *itransfer); +static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle); +static int hid_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size); +// Composite API prototypes +static int composite_init(int sub_api, struct libusb_context *ctx); +static int composite_exit(int sub_api); +static int composite_open(int sub_api, struct libusb_device_handle *dev_handle); +static void composite_close(int sub_api, struct libusb_device_handle *dev_handle); +static int composite_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int composite_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting); +static int composite_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface); +static int composite_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer); +static int composite_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer); +static int composite_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer); +static int composite_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); +static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfer); +static int composite_abort_control(int sub_api, struct usbi_transfer *itransfer); +static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle); +static int composite_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size); + + +// Global variables +uint64_t hires_frequency, hires_ticks_to_ps; +const uint64_t epoch_time = UINT64_C(116444736000000000); // 1970.01.01 00:00:000 in MS Filetime +int windows_version = WINDOWS_UNDEFINED; +static char windows_version_str[128] = "Windows Undefined"; +// Concurrency +static int concurrent_usage = -1; +usbi_mutex_t autoclaim_lock; +// 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; +// API globals +#define CHECK_WINUSBX_AVAILABLE(sub_api) do { if (sub_api == SUB_API_NOTSET) sub_api = priv->sub_api; \ + if (!WinUSBX[sub_api].initialized) return LIBUSB_ERROR_ACCESS; } while(0) +static struct winusb_interface WinUSBX[SUB_API_MAX]; +const char* sub_api_name[SUB_API_MAX] = WINUSBX_DRV_NAMES; +bool api_hid_available = false; +#define CHECK_HID_AVAILABLE do { if (!api_hid_available) return LIBUSB_ERROR_ACCESS; } while (0) + +static inline BOOLEAN guid_eq(const GUID *guid1, const GUID *guid2) { + if ((guid1 != NULL) && (guid2 != NULL)) { + return (memcmp(guid1, guid2, sizeof(GUID)) == 0); + } + return false; +} + +#if defined(ENABLE_LOGGING) +static char* guid_to_string(const GUID* guid) +{ + static char guid_string[MAX_GUID_STRING_LENGTH]; + + if (guid == NULL) return NULL; + sprintf(guid_string, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + (unsigned int)guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); + return guid_string; +} +#endif + +/* + * 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 char err_string[ERR_BUFFER_SIZE]; + + DWORD size; + ssize_t i; + uint32_t error_code, format_error; + + error_code = retval?retval:GetLastError(); + + safe_sprintf(err_string, ERR_BUFFER_SIZE, "[%u] ", error_code); + + // Translate codes returned by SetupAPI. The ones we are dealing with are either + // in 0x0000xxxx or 0xE000xxxx and can be distinguished from standard error codes. + // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff545011.aspx + switch (error_code & 0xE0000000) { + case 0: + error_code = HRESULT_FROM_WIN32(error_code); // Still leaves ERROR_SUCCESS unmodified + break; + case 0xE0000000: + error_code = 0x80000000 | (FACILITY_SETUPAPI << 16) | (error_code & 0x0000FFFF); + break; + default: + break; + } + + size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &err_string[safe_strlen(err_string)], + ERR_BUFFER_SIZE - (DWORD)safe_strlen(err_string), NULL); + if (size == 0) { + format_error = GetLastError(); + if (format_error) + safe_sprintf(err_string, ERR_BUFFER_SIZE, + "Windows error code %u (FormatMessage error code %u)", error_code, format_error); + else + safe_sprintf(err_string, ERR_BUFFER_SIZE, "Unknown error code %u", error_code); + } else { + // Remove CR/LF terminators + for (i=safe_strlen(err_string)-1; (i>=0) && ((err_string[i]==0x0A) || (err_string[i]==0x0D)); i--) { + err_string[i] = 0; + } + } + return err_string; +} +#endif + +/* + * Sanitize Microsoft's paths: convert to uppercase, add prefix and fix backslashes. + * Return an allocated sanitized string or NULL on error. + */ +static char* sanitize_path(const char* path) +{ + const char root_prefix[] = "\\\\.\\"; + size_t j, size, root_size; + char* ret_path = NULL; + size_t add_root = 0; + + if (path == NULL) + return NULL; + + size = safe_strlen(path)+1; + root_size = sizeof(root_prefix)-1; + + // Microsoft indiscriminately uses '\\?\', '\\.\', '##?#" or "##.#" for root prefixes. + if (!((size > 3) && (((path[0] == '\\') && (path[1] == '\\') && (path[3] == '\\')) || + ((path[0] == '#') && (path[1] == '#') && (path[3] == '#'))))) { + add_root = root_size; + size += add_root; + } + + if ((ret_path = (char*) calloc(size, 1)) == NULL) + return NULL; + + safe_strcpy(&ret_path[add_root], size-add_root, path); + + // Ensure consistency with root prefix + for (j=0; jcbSize = sizeof(SP_DEVINFO_DATA); + if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + usbi_err(ctx, "Could not obtain device info data for index %u: %s", + _index, windows_error_str(0)); + } + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return false; + } + return true; +} + +/* + * enumerate interfaces for a specific GUID + * + * Parameters: + * dev_info: a pointer to a dev_info list + * dev_info_data: a pointer to an SP_DEVINFO_DATA to be filled (or NULL if not needed) + * guid: the GUID for which to retrieve interface details + * index: zero based index of the interface in the device info list + * + * Note: it is the responsibility of the caller to free the DEVICE_INTERFACE_DETAIL_DATA + * structure returned and call this function repeatedly using the same guid (with an + * incremented index starting at zero) until all interfaces have been returned. + */ +static SP_DEVICE_INTERFACE_DETAIL_DATA_A *get_interface_details(struct libusb_context *ctx, + HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const GUID* guid, unsigned _index) +{ + SP_DEVICE_INTERFACE_DATA dev_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL; + DWORD size; + + if (_index <= 0) { + *dev_info = pSetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); + } + + if (dev_info_data != NULL) { + dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA); + if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + usbi_err(ctx, "Could not obtain device info data for index %u: %s", + _index, windows_error_str(0)); + } + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; + } + } + + dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + if (!pSetupDiEnumDeviceInterfaces(*dev_info, NULL, guid, _index, &dev_interface_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + usbi_err(ctx, "Could not obtain interface data for index %u: %s", + _index, windows_error_str(0)); + } + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; + } + + // Read interface data (dummy + actual) to access the device path + if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) { + // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + usbi_err(ctx, "could not access interface data (dummy) for index %u: %s", + _index, windows_error_str(0)); + goto err_exit; + } + } else { + usbi_err(ctx, "program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong."); + goto err_exit; + } + + if ((dev_interface_details = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) calloc(size, 1)) == NULL) { + usbi_err(ctx, "could not allocate interface data for index %u.", _index); + goto err_exit; + } + + dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, + dev_interface_details, size, &size, NULL)) { + usbi_err(ctx, "could not access interface data (actual) for index %u: %s", + _index, windows_error_str(0)); + } + + return dev_interface_details; + +err_exit: + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; +} + +/* For libusb0 filter */ +static SP_DEVICE_INTERFACE_DETAIL_DATA_A *get_interface_details_filter(struct libusb_context *ctx, + HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const GUID* guid, unsigned _index, char* filter_path){ + SP_DEVICE_INTERFACE_DATA dev_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL; + DWORD size; + if (_index <= 0) { + *dev_info = pSetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); + } + if (dev_info_data != NULL) { + dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA); + if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + usbi_err(ctx, "Could not obtain device info data for index %u: %s", + _index, windows_error_str(0)); + } + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; + } + } + dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + if (!pSetupDiEnumDeviceInterfaces(*dev_info, NULL, guid, _index, &dev_interface_data)) { + if (GetLastError() != ERROR_NO_MORE_ITEMS) { + usbi_err(ctx, "Could not obtain interface data for index %u: %s", + _index, windows_error_str(0)); + } + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL; + } + // Read interface data (dummy + actual) to access the device path + if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) { + // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + usbi_err(ctx, "could not access interface data (dummy) for index %u: %s", + _index, windows_error_str(0)); + goto err_exit; + } + } else { + usbi_err(ctx, "program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong."); + goto err_exit; + } + if ((dev_interface_details = malloc(size)) == NULL) { + usbi_err(ctx, "could not allocate interface data for index %u.", _index); + goto err_exit; + } + dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + if (!pSetupDiGetDeviceInterfaceDetailA(*dev_info, &dev_interface_data, + dev_interface_details, size, &size, NULL)) { + usbi_err(ctx, "could not access interface data (actual) for index %u: %s", + _index, windows_error_str(0)); + } + // [trobinso] lookup the libusb0 symbolic index. + if (dev_interface_details) { + HKEY hkey_device_interface=pSetupDiOpenDeviceInterfaceRegKey(*dev_info,&dev_interface_data,0,KEY_READ); + if (hkey_device_interface != INVALID_HANDLE_VALUE) { + DWORD libusb0_symboliclink_index=0; + DWORD value_length=sizeof(DWORD); + DWORD value_type=0; + LONG status; + status = pRegQueryValueExW(hkey_device_interface, L"LUsb0", NULL, &value_type, + (LPBYTE) &libusb0_symboliclink_index, &value_length); + if (status == ERROR_SUCCESS) { + if (libusb0_symboliclink_index < 256) { + // libusb0.sys is connected to this device instance. + // If the the device interface guid is {F9F3FF14-AE21-48A0-8A25-8011A7A931D9} then it's a filter. + safe_sprintf(filter_path, sizeof("\\\\.\\libusb0-0000"), "\\\\.\\libusb0-%04d", libusb0_symboliclink_index); + usbi_dbg("assigned libusb0 symbolic link %s", filter_path); + } else { + // libusb0.sys was connected to this device instance at one time; but not anymore. + } + } + pRegCloseKey(hkey_device_interface); + } + } + return dev_interface_details; +err_exit: + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return NULL;} + +/* Hash table functions - modified From glibc 2.3.2: + [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986 + [Knuth] The Art of Computer Programming, part 3 (6.4) */ +typedef struct htab_entry { + unsigned long used; + char* str; +} htab_entry; +htab_entry* htab_table = NULL; +usbi_mutex_t htab_write_mutex = NULL; +unsigned long htab_size, htab_filled; + +/* For the used double hash method the table size has to be a prime. To + correct the user given table size we need a prime test. This trivial + algorithm is adequate because the code is called only during init and + the number is likely to be small */ +static int isprime(unsigned long number) +{ + // no even number will be passed + unsigned int divider = 3; + + while((divider * divider < number) && (number % divider != 0)) + divider += 2; + + return (number % divider != 0); +} + +/* Before using the hash table we must allocate memory for it. + We allocate one element more as the found prime number says. + This is done for more effective indexing as explained in the + comment for the hash function. */ +static int htab_create(struct libusb_context *ctx, unsigned long nel) +{ + if (htab_table != NULL) { + usbi_err(ctx, "hash table already allocated"); + } + + // Create a mutex + usbi_mutex_init(&htab_write_mutex, NULL); + + // Change nel to the first prime number not smaller as nel. + nel |= 1; + while(!isprime(nel)) + nel += 2; + + htab_size = nel; + usbi_dbg("using %d entries hash table", nel); + htab_filled = 0; + + // allocate memory and zero out. + htab_table = (htab_entry*) calloc(htab_size + 1, sizeof(htab_entry)); + if (htab_table == NULL) { + usbi_err(ctx, "could not allocate space for hash table"); + return 0; + } + + return 1; +} + +/* After using the hash table it has to be destroyed. */ +static void htab_destroy(void) +{ + size_t i; + if (htab_table == NULL) { + return; + } + + for (i=0; i New entry + + // If the table is full return an error + if (htab_filled >= htab_size) { + usbi_err(NULL, "hash table is full (%d entries)", htab_size); + return 0; + } + + // Concurrent threads might be storing the same entry at the same time + // (eg. "simultaneous" enums from different threads) => use a mutex + usbi_mutex_lock(&htab_write_mutex); + // Just free any previously allocated string (which should be the same as + // new one). The possibility of concurrent threads storing a collision + // string (same hash, different string) at the same time is extremely low + safe_free(htab_table[idx].str); + htab_table[idx].used = hval; + htab_table[idx].str = (char*) malloc(safe_strlen(str)+1); + if (htab_table[idx].str == NULL) { + usbi_err(NULL, "could not duplicate string for hash table"); + usbi_mutex_unlock(&htab_write_mutex); + return 0; + } + memcpy(htab_table[idx].str, str, safe_strlen(str)+1); + ++htab_filled; + usbi_mutex_unlock(&htab_write_mutex); + + return idx; +} + +/* + * Returns the session ID of a device's nth level ancestor + * If there's no device at the nth level, return 0 + */ +static unsigned long get_ancestor_session_id(DWORD devinst, unsigned level) +{ + DWORD parent_devinst; + unsigned long session_id = 0; + char* sanitized_path = NULL; + char path[MAX_PATH_LENGTH]; + unsigned i; + + if (level < 1) return 0; + for (i = 0; idev); + struct libusb_config_descriptor *conf_desc; + const struct libusb_interface_descriptor *if_desc; + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + + r = libusb_get_config_descriptor(dev_handle->dev, 0, &conf_desc); + if (r != LIBUSB_SUCCESS) { + usbi_warn(ctx, "could not read config descriptor: error %d", r); + return r; + } + + if_desc = &conf_desc->interface[iface].altsetting[altsetting]; + safe_free(priv->usb_interface[iface].endpoint); + + if (if_desc->bNumEndpoints == 0) { + usbi_dbg("no endpoints found for interface %d", iface); + return LIBUSB_SUCCESS; + } + + priv->usb_interface[iface].endpoint = (uint8_t*) malloc(if_desc->bNumEndpoints); + if (priv->usb_interface[iface].endpoint == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + + priv->usb_interface[iface].nb_endpoints = if_desc->bNumEndpoints; + for (i=0; ibNumEndpoints; i++) { + priv->usb_interface[iface].endpoint[i] = if_desc->endpoint[i].bEndpointAddress; + usbi_dbg("(re)assigned endpoint %02X to interface %d", priv->usb_interface[iface].endpoint[i], iface); + } + libusb_free_config_descriptor(conf_desc); + + // Extra init may be required to configure endpoints + return priv->apib->configure_endpoints(SUB_API_NOTSET, dev_handle, iface); +} + +// Lookup for a match in the list of API driver names +// return -1 if not found, driver match number otherwise +static int get_sub_api(char* driver, int api){ + int i; + const char sep_str[2] = {LIST_SEPARATOR, 0}; + char *tok, *tmp_str; + size_t len = safe_strlen(driver); + + if (len == 0) return SUB_API_NOTSET; + tmp_str = (char*) calloc(len+1, 1); + if (tmp_str == NULL) return SUB_API_NOTSET; + memcpy(tmp_str, driver, len+1); + tok = strtok(tmp_str, sep_str); + while (tok != NULL) { + for (i=0; idev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv( + transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int current_interface = *interface_number; + int r = LIBUSB_SUCCESS; + + switch(api_type) { + case USB_API_WINUSBX: + case USB_API_HID: + break; + default: + return LIBUSB_ERROR_INVALID_PARAM; + } + + usbi_mutex_lock(&autoclaim_lock); + if (current_interface < 0) // No serviceable interface was found + { + for (current_interface=0; current_interfaceusb_interface[current_interface].apib->id == api_type) + && (libusb_claim_interface(transfer->dev_handle, current_interface) == LIBUSB_SUCCESS) ) { + usbi_dbg("auto-claimed interface %d for control request", current_interface); + if (handle_priv->autoclaim_count[current_interface] != 0) { + usbi_warn(ctx, "program assertion failed - autoclaim_count was nonzero"); + } + handle_priv->autoclaim_count[current_interface]++; + break; + } + } + if (current_interface == USB_MAXINTERFACES) { + usbi_err(ctx, "could not auto-claim any interface"); + r = LIBUSB_ERROR_NOT_FOUND; + } + } else { + // If we have a valid interface that was autoclaimed, we must increment + // its autoclaim count so that we can prevent an early release. + if (handle_priv->autoclaim_count[current_interface] != 0) { + handle_priv->autoclaim_count[current_interface]++; + } + } + usbi_mutex_unlock(&autoclaim_lock); + + *interface_number = current_interface; + return r; + +} + +static void auto_release(struct usbi_transfer *itransfer) +{ + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + libusb_device_handle *dev_handle = transfer->dev_handle; + struct windows_device_handle_priv* handle_priv = _device_handle_priv(dev_handle); + int r; + + usbi_mutex_lock(&autoclaim_lock); + if (handle_priv->autoclaim_count[transfer_priv->interface_number] > 0) { + handle_priv->autoclaim_count[transfer_priv->interface_number]--; + if (handle_priv->autoclaim_count[transfer_priv->interface_number] == 0) { + r = libusb_release_interface(dev_handle, transfer_priv->interface_number); + if (r == LIBUSB_SUCCESS) { + usbi_dbg("auto-released interface %d", transfer_priv->interface_number); + } else { + usbi_dbg("failed to auto-release interface %d (%s)", + transfer_priv->interface_number, libusb_error_name((enum libusb_error)r)); + } + } + } + usbi_mutex_unlock(&autoclaim_lock); +} + +/* Windows version dtection */ +static BOOL is_x64(void) +{ + BOOL ret = FALSE; + // Detect if we're running a 32 or 64 bit system + if (sizeof(uintptr_t) < 8) { + DLL_LOAD_PREFIXED(Kernel32.dll, p, IsWow64Process, FALSE); + if (pIsWow64Process != NULL) { + (*pIsWow64Process)(GetCurrentProcess(), &ret); + } + } else { + ret = TRUE; + } + return ret; +} + +static void get_windows_version(void) +{ + OSVERSIONINFOEXA vi, vi2; + const char* w = 0; + const char* w64 = "32 bit"; + char* vptr; + size_t vlen; + unsigned major, minor; + ULONGLONG major_equal, minor_equal; + BOOL ws; + + memset(&vi, 0, sizeof(vi)); + vi.dwOSVersionInfoSize = sizeof(vi); + if (!GetVersionExA((OSVERSIONINFOA *)&vi)) { + memset(&vi, 0, sizeof(vi)); + vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + if (!GetVersionExA((OSVERSIONINFOA *)&vi)) + return; + } + + if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) { + + if (vi.dwMajorVersion > 6 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion >= 2)) { + // Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version + // See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx + + major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL); + for (major = vi.dwMajorVersion; major <= 9; major++) { + memset(&vi2, 0, sizeof(vi2)); + vi2.dwOSVersionInfoSize = sizeof(vi2); vi2.dwMajorVersion = major; + if (!VerifyVersionInfoA(&vi2, VER_MAJORVERSION, major_equal)) + continue; + if (vi.dwMajorVersion < major) { + vi.dwMajorVersion = major; vi.dwMinorVersion = 0; + } + + minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL); + for (minor = vi.dwMinorVersion; minor <= 9; minor++) { + memset(&vi2, 0, sizeof(vi2)); vi2.dwOSVersionInfoSize = sizeof(vi2); + vi2.dwMinorVersion = minor; + if (!VerifyVersionInfoA(&vi2, VER_MINORVERSION, minor_equal)) + continue; + vi.dwMinorVersion = minor; + break; + } + + break; + } + } + + if (vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) { + ws = (vi.wProductType <= VER_NT_WORKSTATION); + windows_version = vi.dwMajorVersion << 4 | vi.dwMinorVersion; + switch (windows_version) { + case 0x50: w = "2000"; + break; + case 0x51: w = "XP"; + break; + case 0x52: w = ("2003"); + break; + case 0x60: w = (ws?"Vista":"2008"); + break; + case 0x61: w = (ws?"7":"2008_R2"); + break; + case 0x62: w = (ws?"8":"2012"); + break; + case 0x63: w = (ws?"8.1":"2012_R2"); + break; + case 0x64: w = (ws?"8.2":"2012_R3"); + break; + default: + if (windows_version < 0x50) + windows_version = WINDOWS_UNSUPPORTED; + else + w = "9 or later"; + break; + } + } + } + + if (is_x64()) + w64 = "64-bit"; + + vptr = &windows_version_str[sizeof("Windows ") - 1]; + vlen = sizeof(windows_version_str) - sizeof("Windows ") - 1; + if (!w) + safe_sprintf(vptr, vlen, "%s %u.%u %s", (vi.dwPlatformId==VER_PLATFORM_WIN32_NT?"NT":"??"), + (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64); + else if (vi.wServicePackMinor) + safe_sprintf(vptr, vlen, "%s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, w64); + else if (vi.wServicePackMajor) + safe_sprintf(vptr, vlen, "%s SP%u %s", w, vi.wServicePackMajor, w64); + else + safe_sprintf(vptr, vlen, "%s %s", w, w64); +} + +/* + * init: libusb backend init function + * + * This function enumerates the HCDs (Host Controller Drivers) and populates our private HCD list + * In our implementation, we equate Windows' "HCD" to libusb's "bus". Note that bus is zero indexed. + * HCDs are not expected to change after init (might not hold true for hot pluggable USB PCI card?) + */ +static int windows_init(struct libusb_context *ctx) +{ + int i, r = LIBUSB_ERROR_OTHER; + HANDLE semaphore; + char sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID) + + sprintf(sem_name, "libusb_init%08X", (unsigned int)GetCurrentProcessId()&0xFFFFFFFF); + semaphore = CreateSemaphoreA(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? + get_windows_version(); + usbi_dbg(windows_version_str); + if (windows_version == WINDOWS_UNSUPPORTED) { + usbi_err(ctx, "This version of Windows is NOT supported"); + r = LIBUSB_ERROR_NOT_SUPPORTED; + goto init_exit; + } + + // We need a lock for proper auto-release + usbi_mutex_init(&autoclaim_lock, NULL); + + // Initialize pollable file descriptors + init_polling(); + + // Load DLL imports + if (init_dlls() != LIBUSB_SUCCESS) { + usbi_err(ctx, "could not resolve DLL functions"); + return LIBUSB_ERROR_NOT_FOUND; + } + + // Initialize the low level APIs (we don't care about errors at this stage) + for (i=0; inum_configurations = 1; + priv->dev_descriptor.bLength = sizeof(USB_DEVICE_DESCRIPTOR); + priv->dev_descriptor.bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE; + priv->dev_descriptor.bNumConfigurations = 1; + priv->active_config = 1; + + if (priv->parent_dev == NULL) { + usbi_err(ctx, "program assertion failed - HCD hub has no parent"); + return LIBUSB_ERROR_NO_DEVICE; + } + parent_priv = _device_priv(priv->parent_dev); + if (sscanf(parent_priv->path, "\\\\.\\PCI#VEN_%04x&DEV_%04x%*s", &vid, &pid) == 2) { + priv->dev_descriptor.idVendor = (uint16_t)vid; + priv->dev_descriptor.idProduct = (uint16_t)pid; + } else { + usbi_warn(ctx, "could not infer VID/PID of HCD hub from '%s'", parent_priv->path); + priv->dev_descriptor.idVendor = 0x1d6b; // Linux Foundation root hub + priv->dev_descriptor.idProduct = 1; + } + return LIBUSB_SUCCESS; +} + +/* + * fetch and cache all the config descriptors through I/O + */ +static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle, char* device_id) +{ + DWORD size, ret_size; + struct libusb_context *ctx = DEVICE_CTX(dev); + struct windows_device_priv *priv = _device_priv(dev); + int r; + uint8_t i; + + USB_CONFIGURATION_DESCRIPTOR_SHORT cd_buf_short; // dummy request + PUSB_DESCRIPTOR_REQUEST cd_buf_actual = NULL; // actual request + PUSB_CONFIGURATION_DESCRIPTOR cd_data = NULL; + + if (dev->num_configurations == 0) + return LIBUSB_ERROR_INVALID_PARAM; + + priv->config_descriptor = (unsigned char**) calloc(dev->num_configurations, sizeof(unsigned char*)); + if (priv->config_descriptor == NULL) + return LIBUSB_ERROR_NO_MEM; + for (i=0; inum_configurations; i++) + priv->config_descriptor[i] = NULL; + + for (i=0, r=LIBUSB_SUCCESS; ; i++) + { + // safe loop: release all dynamic resources + safe_free(cd_buf_actual); + + // safe loop: end of loop condition + if ((i >= dev->num_configurations) || (r != LIBUSB_SUCCESS)) + break; + + size = sizeof(USB_CONFIGURATION_DESCRIPTOR_SHORT); + memset(&cd_buf_short, 0, size); + + cd_buf_short.req.ConnectionIndex = (ULONG)priv->port; + cd_buf_short.req.SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN; + cd_buf_short.req.SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR; + cd_buf_short.req.SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i; + cd_buf_short.req.SetupPacket.wIndex = i; + cd_buf_short.req.SetupPacket.wLength = (USHORT)(size - sizeof(USB_DESCRIPTOR_REQUEST)); + + // Dummy call to get the required data size. Initial failures are reported as info rather + // than error as they can occur for non-penalizing situations, such as with some hubs. + // coverity[tainted_data_argument] + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, &cd_buf_short, size, + &cd_buf_short, size, &ret_size, NULL)) { + usbi_info(ctx, "could not access configuration descriptor (dummy) for '%s': %s", device_id, windows_error_str(0)); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + if ((ret_size != size) || (cd_buf_short.data.wTotalLength < sizeof(USB_CONFIGURATION_DESCRIPTOR))) { + usbi_info(ctx, "unexpected configuration descriptor size (dummy) for '%s'.", device_id); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + size = sizeof(USB_DESCRIPTOR_REQUEST) + cd_buf_short.data.wTotalLength; + if ((cd_buf_actual = (PUSB_DESCRIPTOR_REQUEST) calloc(1, size)) == NULL) { + usbi_err(ctx, "could not allocate configuration descriptor buffer for '%s'.", device_id); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + memset(cd_buf_actual, 0, size); + + // Actual call + cd_buf_actual->ConnectionIndex = (ULONG)priv->port; + cd_buf_actual->SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN; + cd_buf_actual->SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR; + cd_buf_actual->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i; + cd_buf_actual->SetupPacket.wIndex = i; + cd_buf_actual->SetupPacket.wLength = (USHORT)(size - sizeof(USB_DESCRIPTOR_REQUEST)); + + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, cd_buf_actual, size, + cd_buf_actual, size, &ret_size, NULL)) { + usbi_err(ctx, "could not access configuration descriptor (actual) for '%s': %s", device_id, windows_error_str(0)); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + cd_data = (PUSB_CONFIGURATION_DESCRIPTOR)((UCHAR*)cd_buf_actual+sizeof(USB_DESCRIPTOR_REQUEST)); + + if ((size != ret_size) || (cd_data->wTotalLength != cd_buf_short.data.wTotalLength)) { + usbi_err(ctx, "unexpected configuration descriptor size (actual) for '%s'.", device_id); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + if (cd_data->bDescriptorType != USB_CONFIGURATION_DESCRIPTOR_TYPE) { + usbi_err(ctx, "not a configuration descriptor for '%s'", device_id); + LOOP_BREAK(LIBUSB_ERROR_IO); + } + + usbi_dbg("cached config descriptor %d (bConfigurationValue=%d, %d bytes)", + i, cd_data->bConfigurationValue, cd_data->wTotalLength); + + // Cache the descriptor + priv->config_descriptor[i] = (unsigned char*) malloc(cd_data->wTotalLength); + if (priv->config_descriptor[i] == NULL) + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + memcpy(priv->config_descriptor[i], cd_data, cd_data->wTotalLength); + } + return LIBUSB_SUCCESS; +} + +/* + * Populate a libusb device structure + */ +static int init_device(struct libusb_device* dev, struct libusb_device* parent_dev, + uint8_t port_number, char* device_id, DWORD devinst) +{ + HANDLE handle; + DWORD size; + USB_NODE_CONNECTION_INFORMATION_EX conn_info; + USB_NODE_CONNECTION_INFORMATION_EX_V2 conn_info_v2; + struct windows_device_priv *priv, *parent_priv; + struct libusb_context *ctx; + struct libusb_device* tmp_dev; + unsigned i; + + if ((dev == NULL) || (parent_dev == NULL)) { + return LIBUSB_ERROR_NOT_FOUND; + } + ctx = DEVICE_CTX(dev); + priv = _device_priv(dev); + parent_priv = _device_priv(parent_dev); + if (parent_priv->apib->id != USB_API_HUB) { + usbi_warn(ctx, "parent for device '%s' is not a hub", device_id); + return LIBUSB_ERROR_NOT_FOUND; + } + + // It is possible for the parent hub not to have been initialized yet + // If that's the case, lookup the ancestors to set the bus number + if (parent_dev->bus_number == 0) { + for (i=2; ; i++) { + tmp_dev = usbi_get_device_by_session_id(ctx, get_ancestor_session_id(devinst, i)); + if (tmp_dev == NULL) break; + if (tmp_dev->bus_number != 0) { + usbi_dbg("got bus number from ancestor #%d", i); + parent_dev->bus_number = tmp_dev->bus_number; + libusb_unref_device(tmp_dev); + break; + } + libusb_unref_device(tmp_dev); + } + } + if (parent_dev->bus_number == 0) { + usbi_err(ctx, "program assertion failed: unable to find ancestor bus number for '%s'", device_id); + return LIBUSB_ERROR_NOT_FOUND; + } + dev->bus_number = parent_dev->bus_number; + priv->port = port_number; + dev->port_number = port_number; + priv->depth = parent_priv->depth + 1; + priv->parent_dev = parent_dev; + dev->parent_dev = parent_dev; + + // If the device address is already set, we can stop here + if (dev->device_address != 0) { + return LIBUSB_SUCCESS; + } + memset(&conn_info, 0, sizeof(conn_info)); + if (priv->depth != 0) { // Not a HCD hub + handle = CreateFileA(parent_priv->path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + if (handle == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "could not open hub %s: %s", parent_priv->path, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + size = sizeof(conn_info); + conn_info.ConnectionIndex = (ULONG)port_number; + // coverity[tainted_data_argument] + if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, &conn_info, size, + &conn_info, size, &size, NULL)) { + usbi_warn(ctx, "could not get node connection information for device '%s': %s", + device_id, windows_error_str(0)); + safe_closehandle(handle); + return LIBUSB_ERROR_NO_DEVICE; + } + if (conn_info.ConnectionStatus == NoDeviceConnected) { + usbi_err(ctx, "device '%s' is no longer connected!", device_id); + safe_closehandle(handle); + return LIBUSB_ERROR_NO_DEVICE; + } + memcpy(&priv->dev_descriptor, &(conn_info.DeviceDescriptor), sizeof(USB_DEVICE_DESCRIPTOR)); + dev->num_configurations = priv->dev_descriptor.bNumConfigurations; + priv->active_config = conn_info.CurrentConfigurationValue; + usbi_dbg("found %d configurations (active conf: %d)", dev->num_configurations, priv->active_config); + // If we can't read the config descriptors, just set the number of confs to zero + if (cache_config_descriptors(dev, handle, device_id) != LIBUSB_SUCCESS) { + dev->num_configurations = 0; + priv->dev_descriptor.bNumConfigurations = 0; + } + + // In their great wisdom, Microsoft decided to BREAK the USB speed report between Windows 7 and Windows 8 + if (windows_version >= WINDOWS_8) { + memset(&conn_info_v2, 0, sizeof(conn_info_v2)); + size = sizeof(conn_info_v2); + conn_info_v2.ConnectionIndex = (ULONG)port_number; + conn_info_v2.Length = size; + conn_info_v2.SupportedUsbProtocols.Usb300 = 1; + if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, + &conn_info_v2, size, &conn_info_v2, size, &size, NULL)) { + usbi_warn(ctx, "could not get node connection information (V2) for device '%s': %s", + device_id, windows_error_str(0)); + } else if (conn_info_v2.Flags.DeviceIsOperatingAtSuperSpeedOrHigher) { + conn_info.Speed = 3; + } + } + + safe_closehandle(handle); + + if (conn_info.DeviceAddress > UINT8_MAX) { + usbi_err(ctx, "program assertion failed: device address overflow"); + } + dev->device_address = (uint8_t)conn_info.DeviceAddress + 1; + if (dev->device_address == 1) { + usbi_err(ctx, "program assertion failed: device address collision with root hub"); + } + switch (conn_info.Speed) { + case 0: dev->speed = LIBUSB_SPEED_LOW; break; + case 1: dev->speed = LIBUSB_SPEED_FULL; break; + case 2: dev->speed = LIBUSB_SPEED_HIGH; break; + case 3: dev->speed = LIBUSB_SPEED_SUPER; break; + default: + usbi_warn(ctx, "Got unknown device speed %d", conn_info.Speed); + break; + } + } else { + dev->device_address = 1; // root hubs are set to use device number 1 + force_hcd_device_descriptor(dev); + } + + usbi_sanitize_device(dev); + + usbi_dbg("(bus: %d, addr: %d, depth: %d, port: %d): '%s'", + dev->bus_number, dev->device_address, priv->depth, priv->port, device_id); + + return LIBUSB_SUCCESS; +} + +// Returns the api type, or 0 if not found/unsupported +static void get_api_type(struct libusb_context *ctx, HDEVINFO *dev_info, + SP_DEVINFO_DATA *dev_info_data, int *api, int *sub_api) +{ + // Precedence for filter drivers vs driver is in the order of this array + struct driver_lookup lookup[3] = { + {"\0\0", SPDRP_SERVICE, "driver"}, + {"\0\0", SPDRP_UPPERFILTERS, "upper filter driver"}, + {"\0\0", SPDRP_LOWERFILTERS, "lower filter driver"} + }; + DWORD size, reg_type; + unsigned k, l; + int i, j; + + *api = USB_API_UNSUPPORTED; + *sub_api = SUB_API_NOTSET; + // Check the service & filter names to know the API we should use + for (k=0; k<3; k++) { + if (pSetupDiGetDeviceRegistryPropertyA(*dev_info, dev_info_data, lookup[k].reg_prop, + ®_type, (BYTE*)lookup[k].list, MAX_KEY_LENGTH, &size)) { + // Turn the REG_SZ SPDRP_SERVICE into REG_MULTI_SZ + if (lookup[k].reg_prop == SPDRP_SERVICE) { + // our buffers are MAX_KEY_LENGTH+1 so we can overflow if needed + lookup[k].list[safe_strlen(lookup[k].list)+1] = 0; + } + // MULTI_SZ is a pain to work with. Turn it into something much more manageable + // NB: none of the driver names we check against contain LIST_SEPARATOR, + // (currently ';'), so even if an unsuported one does, it's not an issue + for (l=0; (lookup[k].list[l] != 0) || (lookup[k].list[l+1] != 0); l++) { + if (lookup[k].list[l] == 0) { + lookup[k].list[l] = LIST_SEPARATOR; + } + } + usbi_dbg("%s(s): %s", lookup[k].designation, lookup[k].list); + } else { + if (GetLastError() != ERROR_INVALID_DATA) { + usbi_dbg("could not access %s: %s", lookup[k].designation, windows_error_str(0)); + } + lookup[k].list[0] = 0; + } + } + + for (i=1; i= 0) { + usbi_dbg("matched %s name against %s", + lookup[k].designation, (i!=USB_API_WINUSBX)?usb_api_backend[i].designation:sub_api_name[j]); + *api = i; + *sub_api = j; + return; + } + } + } +} + +static int set_composite_interface(struct libusb_context* ctx, struct libusb_device* dev, + char* dev_interface_path, char* device_id, int api, int sub_api) +{ + unsigned i; + struct windows_device_priv *priv = _device_priv(dev); + int interface_number; + + if (priv->apib->id != USB_API_COMPOSITE) { + usbi_err(ctx, "program assertion failed: '%s' is not composite", device_id); + return LIBUSB_ERROR_NO_DEVICE; + } + + // Because MI_## are not necessarily in sequential order (some composite + // devices will have only MI_00 & MI_03 for instance), we retrieve the actual + // interface number from the path's MI value + interface_number = 0; + for (i=0; device_id[i] != 0; ) { + if ( (device_id[i++] == 'M') && (device_id[i++] == 'I') + && (device_id[i++] == '_') ) { + interface_number = (device_id[i++] - '0')*10; + interface_number += device_id[i] - '0'; + break; + } + } + + if (device_id[i] == 0) { + usbi_warn(ctx, "failure to read interface number for %s. Using default value %d", + device_id, interface_number); + } + + if (priv->usb_interface[interface_number].path != NULL) { + if (api == USB_API_HID) { + // HID devices can have multiple collections (COL##) for each MI_## interface + usbi_dbg("interface[%d] already set - ignoring HID collection: %s", + interface_number, device_id); + return LIBUSB_ERROR_ACCESS; + } + // In other cases, just use the latest data + safe_free(priv->usb_interface[interface_number].path); + } + + usbi_dbg("interface[%d] = %s", interface_number, dev_interface_path); + priv->usb_interface[interface_number].path = dev_interface_path; + priv->usb_interface[interface_number].apib = &usb_api_backend[api]; + priv->usb_interface[interface_number].sub_api = sub_api; + if ((api == USB_API_HID) && (priv->hid == NULL)) { + priv->hid = (struct hid_device_priv*) calloc(1, sizeof(struct hid_device_priv)); + if (priv->hid == NULL) + return LIBUSB_ERROR_NO_MEM; + } + + return LIBUSB_SUCCESS; +} + +static int set_hid_interface(struct libusb_context* ctx, struct libusb_device* dev, + char* dev_interface_path) +{ + int i; + struct windows_device_priv *priv = _device_priv(dev); + + if (priv->hid == NULL) { + usbi_err(ctx, "program assertion failed: parent is not HID"); + return LIBUSB_ERROR_NO_DEVICE; + } + if (priv->hid->nb_interfaces == USB_MAXINTERFACES) { + usbi_err(ctx, "program assertion failed: max USB interfaces reached for HID device"); + return LIBUSB_ERROR_NO_DEVICE; + } + for (i=0; ihid->nb_interfaces; i++) { + if (safe_strcmp(priv->usb_interface[i].path, dev_interface_path) == 0) { + usbi_dbg("interface[%d] already set to %s", i, dev_interface_path); + return LIBUSB_SUCCESS; + } + } + + priv->usb_interface[priv->hid->nb_interfaces].path = dev_interface_path; + priv->usb_interface[priv->hid->nb_interfaces].apib = &usb_api_backend[USB_API_HID]; + usbi_dbg("interface[%d] = %s", priv->hid->nb_interfaces, dev_interface_path); + priv->hid->nb_interfaces++; + return LIBUSB_SUCCESS; +} + +/* + * get_device_list: libusb backend device enumeration function + */ +static int windows_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) +{ + struct discovered_devs *discdevs; + HDEVINFO dev_info = { 0 }; + const char* usb_class[] = {"USB", "NUSB3", "IUSB3"}; + SP_DEVINFO_DATA dev_info_data = { 0 }; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL; + GUID hid_guid; +#define MAX_ENUM_GUIDS 64 + const GUID* guid[MAX_ENUM_GUIDS]; +#define HCD_PASS 0 +#define HUB_PASS 1 +#define GEN_PASS 2 +#define DEV_PASS 3 +#define HID_PASS 4 + int r = LIBUSB_SUCCESS; + int api, sub_api; + size_t class_index = 0; + unsigned int nb_guids, pass, i, j, ancestor; + char path[MAX_PATH_LENGTH]; + char strbuf[MAX_PATH_LENGTH]; + struct libusb_device *dev, *parent_dev; + struct windows_device_priv *priv, *parent_priv; + char* dev_interface_path = NULL; + char* dev_id_path = NULL; + unsigned long session_id; + DWORD size, reg_type, port_nr, install_state; + HKEY key; + WCHAR guid_string_w[MAX_GUID_STRING_LENGTH]; + GUID* if_guid; + LONG s; + // Keep a list of newly allocated devs to unref + libusb_device** unref_list; + unsigned int unref_size = 64; + unsigned int unref_cur = 0; + + // PASS 1 : (re)enumerate HCDs (allows for HCD hotplug) + // PASS 2 : (re)enumerate HUBS + // PASS 3 : (re)enumerate generic USB devices (including driverless) + // and list additional USB device interface GUIDs to explore + // PASS 4 : (re)enumerate master USB devices that have a device interface + // PASS 5+: (re)enumerate device interfaced GUIDs (including HID) and + // set the device interfaces. + + // Init the GUID table + guid[HCD_PASS] = &GUID_DEVINTERFACE_USB_HOST_CONTROLLER; + guid[HUB_PASS] = &GUID_DEVINTERFACE_USB_HUB; + guid[GEN_PASS] = NULL; + guid[DEV_PASS] = &GUID_DEVINTERFACE_USB_DEVICE; + HidD_GetHidGuid(&hid_guid); + guid[HID_PASS] = &hid_guid; + nb_guids = HID_PASS+1; + + unref_list = (libusb_device**) calloc(unref_size, sizeof(libusb_device*)); + if (unref_list == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + + for (pass = 0; ((pass < nb_guids) && (r == LIBUSB_SUCCESS)); pass++) { +//#define ENUM_DEBUG +#ifdef ENUM_DEBUG + const char *passname[] = { "HCD", "HUB", "GEN", "DEV", "HID", "EXT" }; + usbi_dbg("\n#### PROCESSING %ss %s", passname[(pass<=HID_PASS)?pass:HID_PASS+1], + (pass!=GEN_PASS)?guid_to_string(guid[pass]):""); +#endif + for (i = 0; ; i++) { + // safe loop: free up any (unprotected) dynamic resource + // NB: this is always executed before breaking the loop + safe_free(dev_interface_details); + safe_free(dev_interface_path); + safe_free(dev_id_path); + priv = parent_priv = NULL; + dev = parent_dev = NULL; + + // Safe loop: end of loop conditions + if (r != LIBUSB_SUCCESS) { + break; + } + if ((pass == HCD_PASS) && (i == UINT8_MAX)) { + usbi_warn(ctx, "program assertion failed - found more than %d buses, skipping the rest.", UINT8_MAX); + break; + } + if (pass != GEN_PASS) { + // Except for GEN, all passes deal with device interfaces + dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid[pass], i); + if (dev_interface_details == NULL) { + break; + } else { + dev_interface_path = sanitize_path(dev_interface_details->DevicePath); + if (dev_interface_path == NULL) { + usbi_warn(ctx, "could not sanitize device interface path for '%s'", dev_interface_details->DevicePath); + continue; + } + } + } else { + // Workaround for a Nec/Renesas USB 3.0 driver bug where root hubs are + // being listed under the "NUSB3" PnP Symbolic Name rather than "USB". + // The Intel USB 3.0 driver behaves similar, but uses "IUSB3" + for (; class_index < ARRAYSIZE(usb_class); class_index++) { + if (get_devinfo_data(ctx, &dev_info, &dev_info_data, usb_class[class_index], i)) + break; + i = 0; + } + if (class_index >= ARRAYSIZE(usb_class)) + break; + } + + // Read the Device ID path. This is what we'll use as UID + // Note that if the device is plugged in a different port or hub, the Device ID changes + if (CM_Get_Device_IDA(dev_info_data.DevInst, path, sizeof(path), 0) != CR_SUCCESS) { + usbi_warn(ctx, "could not read the device id path for devinst %X, skipping", + dev_info_data.DevInst); + continue; + } + dev_id_path = sanitize_path(path); + if (dev_id_path == NULL) { + usbi_warn(ctx, "could not sanitize device id path for devinst %X, skipping", + dev_info_data.DevInst); + continue; + } +#ifdef ENUM_DEBUG + usbi_dbg("PRO: %s", dev_id_path); +#endif + + // The SPDRP_ADDRESS for USB devices is the device port number on the hub + port_nr = 0; + if ((pass >= HUB_PASS) && (pass <= GEN_PASS)) { + if ( (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ADDRESS, + ®_type, (BYTE*)&port_nr, 4, &size)) + || (size != 4) ) { + usbi_warn(ctx, "could not retrieve port number for device '%s', skipping: %s", + dev_id_path, windows_error_str(0)); + continue; + } + } + + // Set API to use or get additional data from generic pass + api = USB_API_UNSUPPORTED; + sub_api = SUB_API_NOTSET; + switch (pass) { + case HCD_PASS: + break; + case GEN_PASS: + // We use the GEN pass to detect driverless devices... + size = sizeof(strbuf); + if (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_DRIVER, + ®_type, (BYTE*)strbuf, size, &size)) { + usbi_info(ctx, "The following device has no driver: '%s'", dev_id_path); + usbi_info(ctx, "libusb will not be able to access it."); + } + // ...and to add the additional device interface GUIDs + key = pSetupDiOpenDevRegKey(dev_info, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); + if (key != INVALID_HANDLE_VALUE) { + size = sizeof(guid_string_w); + s = pRegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, ®_type, + (BYTE*)guid_string_w, &size); + pRegCloseKey(key); + if (s == ERROR_SUCCESS) { + if (nb_guids >= MAX_ENUM_GUIDS) { + // If this assert is ever reported, grow a GUID table dynamically + usbi_err(ctx, "program assertion failed: too many GUIDs"); + LOOP_BREAK(LIBUSB_ERROR_OVERFLOW); + } + if_guid = (GUID*) calloc(1, sizeof(GUID)); + pCLSIDFromString(guid_string_w, if_guid); + guid[nb_guids++] = if_guid; + usbi_dbg("extra GUID: %s", guid_to_string(if_guid)); + } + } + break; + case HID_PASS: + api = USB_API_HID; + break; + default: + // Get the API type (after checking that the driver installation is OK) + if ( (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_INSTALL_STATE, + ®_type, (BYTE*)&install_state, 4, &size)) + || (size != 4) ){ + usbi_warn(ctx, "could not detect installation state of driver for '%s': %s", + dev_id_path, windows_error_str(0)); + } else if (install_state != 0) { + usbi_warn(ctx, "driver for device '%s' is reporting an issue (code: %d) - skipping", + dev_id_path, install_state); + continue; + } + get_api_type(ctx, &dev_info, &dev_info_data, &api, &sub_api); + break; + } + + // Find parent device (for the passes that need it) + switch (pass) { + case HCD_PASS: + case DEV_PASS: + case HUB_PASS: + break; + default: + // Go through the ancestors until we see a face we recognize + parent_dev = NULL; + for (ancestor = 1; parent_dev == NULL; ancestor++) { + session_id = get_ancestor_session_id(dev_info_data.DevInst, ancestor); + if (session_id == 0) { + break; + } + parent_dev = usbi_get_device_by_session_id(ctx, session_id); + } + if (parent_dev == NULL) { + usbi_dbg("unlisted ancestor for '%s' (non USB HID, newly connected, etc.) - ignoring", dev_id_path); + continue; + } + parent_priv = _device_priv(parent_dev); + // virtual USB devices are also listed during GEN - don't process these yet + if ( (pass == GEN_PASS) && (parent_priv->apib->id != USB_API_HUB) ) { + libusb_unref_device(parent_dev); + continue; + } + break; + } + + // Create new or match existing device, using the (hashed) device_id as session id + if (pass <= DEV_PASS) { // For subsequent passes, we'll lookup the parent + // These are the passes that create "new" devices + session_id = htab_hash(dev_id_path); + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev == NULL) { + if (pass == DEV_PASS) { + // This can occur if the OS only reports a newly plugged device after we started enum + usbi_warn(ctx, "'%s' was only detected in late pass (newly connected device?)" + " - ignoring", dev_id_path); + continue; + } + usbi_dbg("allocating new device for session [%X]", session_id); + if ((dev = usbi_alloc_device(ctx, session_id)) == NULL) { + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + windows_device_priv_init(dev); + } else { + usbi_dbg("found existing device for session [%X] (%d.%d)", + session_id, dev->bus_number, dev->device_address); + } + // Keep track of devices that need unref + unref_list[unref_cur++] = dev; + if (unref_cur >= unref_size) { + unref_size += 64; + unref_list = usbi_reallocf(unref_list, unref_size*sizeof(libusb_device*)); + if (unref_list == NULL) { + usbi_err(ctx, "could not realloc list for unref - aborting."); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + } + priv = _device_priv(dev); + } + + // Setup device + switch (pass) { + case HCD_PASS: + dev->bus_number = (uint8_t)(i + 1); // bus 0 is reserved for disconnected + dev->device_address = 0; + dev->num_configurations = 0; + priv->apib = &usb_api_backend[USB_API_HUB]; + priv->sub_api = SUB_API_NOTSET; + priv->depth = UINT8_MAX; // Overflow to 0 for HCD Hubs + priv->path = dev_interface_path; dev_interface_path = NULL; + break; + case HUB_PASS: + case DEV_PASS: + // If the device has already been setup, don't do it again + if (priv->path != NULL) + break; + // Take care of API initialization + priv->path = dev_interface_path; dev_interface_path = NULL; + priv->apib = &usb_api_backend[api]; + priv->sub_api = sub_api; + switch(api) { + case USB_API_COMPOSITE: + case USB_API_HUB: + break; + case USB_API_HID: + priv->hid = calloc(1, sizeof(struct hid_device_priv)); + if (priv->hid == NULL) { + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + priv->hid->nb_interfaces = 0; + break; + default: + // For other devices, the first interface is the same as the device + priv->usb_interface[0].path = (char*) calloc(safe_strlen(priv->path)+1, 1); + if (priv->usb_interface[0].path != NULL) { + safe_strcpy(priv->usb_interface[0].path, safe_strlen(priv->path)+1, priv->path); + } else { + usbi_warn(ctx, "could not duplicate interface path '%s'", priv->path); + } + // The following is needed if we want API calls to work for both simple + // and composite devices. + for(j=0; jusb_interface[j].apib = &usb_api_backend[api]; + } + break; + } + break; + case GEN_PASS: + r = init_device(dev, parent_dev, (uint8_t)port_nr, dev_id_path, dev_info_data.DevInst); + if (r == LIBUSB_SUCCESS) { + // Append device to the list of discovered devices + discdevs = discovered_devs_append(*_discdevs, dev); + if (!discdevs) { + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + *_discdevs = discdevs; + } else if (r == LIBUSB_ERROR_NO_DEVICE) { + // This can occur if the device was disconnected but Windows hasn't + // refreshed its enumeration yet - in that case, we ignore the device + r = LIBUSB_SUCCESS; + } + break; + default: // HID_PASS and later + if (parent_priv->apib->id == USB_API_HID) { + usbi_dbg("setting HID interface for [%lX]:", parent_dev->session_data); + r = set_hid_interface(ctx, parent_dev, dev_interface_path); + if (r != LIBUSB_SUCCESS) LOOP_BREAK(r); + dev_interface_path = NULL; + } else if (parent_priv->apib->id == USB_API_COMPOSITE) { + usbi_dbg("setting composite interface for [%lX]:", parent_dev->session_data); + switch (set_composite_interface(ctx, parent_dev, dev_interface_path, dev_id_path, api, sub_api)) { + case LIBUSB_SUCCESS: + dev_interface_path = NULL; + break; + case LIBUSB_ERROR_ACCESS: + // interface has already been set => make sure dev_interface_path is freed then + break; + default: + LOOP_BREAK(r); + break; + } + } + libusb_unref_device(parent_dev); + break; + } + } + } + + // Free any additional GUIDs + for (pass = HID_PASS+1; pass < nb_guids; pass++) { + safe_free(guid[pass]); + } + + // Unref newly allocated devs + if (unref_list != NULL) { + for (i=0; i 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 + for (i=0; idev_descriptor), DEVICE_DESC_LENGTH); + *host_endian = 0; + + return LIBUSB_SUCCESS; +} + +static int windows_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) +{ + struct windows_device_priv *priv = _device_priv(dev); + PUSB_CONFIGURATION_DESCRIPTOR config_header; + size_t size; + + // config index is zero based + if (config_index >= dev->num_configurations) + return LIBUSB_ERROR_INVALID_PARAM; + + if ((priv->config_descriptor == NULL) || (priv->config_descriptor[config_index] == NULL)) + return LIBUSB_ERROR_NOT_FOUND; + + config_header = (PUSB_CONFIGURATION_DESCRIPTOR)priv->config_descriptor[config_index]; + + size = min(config_header->wTotalLength, len); + memcpy(buffer, priv->config_descriptor[config_index], size); + *host_endian = 0; + + return (int)size; +} + +/* + * return the cached copy of the active config descriptor + */ +static int windows_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) +{ + struct windows_device_priv *priv = _device_priv(dev); + + if (priv->active_config == 0) + return LIBUSB_ERROR_NOT_FOUND; + + // config index is zero based + return windows_get_config_descriptor(dev, (uint8_t)(priv->active_config-1), buffer, len, host_endian); +} + +static int windows_open(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + + if (priv->apib == NULL) { + usbi_err(ctx, "program assertion failed - device is not initialized"); + return LIBUSB_ERROR_NO_DEVICE; + } + + return priv->apib->open(SUB_API_NOTSET, dev_handle); +} + +static void windows_close(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + priv->apib->close(SUB_API_NOTSET, dev_handle); +} + +static int windows_get_configuration(struct libusb_device_handle *dev_handle, int *config) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + if (priv->active_config == 0) { + *config = 0; + return LIBUSB_ERROR_NOT_FOUND; + } + + *config = priv->active_config; + return LIBUSB_SUCCESS; +} + +/* + * from http://msdn.microsoft.com/en-us/library/ms793522.aspx: "The port driver + * does not currently expose a service that allows higher-level drivers to set + * the configuration." + */ +static int windows_set_configuration(struct libusb_device_handle *dev_handle, int config) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int r = LIBUSB_SUCCESS; + + if (config >= USB_MAXCONFIG) + return LIBUSB_ERROR_INVALID_PARAM; + + r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_OUT | + LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE, + LIBUSB_REQUEST_SET_CONFIGURATION, (uint16_t)config, + 0, NULL, 0, 1000); + + if (r == LIBUSB_SUCCESS) { + priv->active_config = (uint8_t)config; + } + return r; +} + +static int windows_claim_interface(struct libusb_device_handle *dev_handle, int iface) +{ + int r = LIBUSB_SUCCESS; + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + if (iface >= USB_MAXINTERFACES) + return LIBUSB_ERROR_INVALID_PARAM; + + safe_free(priv->usb_interface[iface].endpoint); + priv->usb_interface[iface].nb_endpoints= 0; + + r = priv->apib->claim_interface(SUB_API_NOTSET, dev_handle, iface); + + if (r == LIBUSB_SUCCESS) { + r = windows_assign_endpoints(dev_handle, iface, 0); + } + + return r; +} + +static int windows_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + int r = LIBUSB_SUCCESS; + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + safe_free(priv->usb_interface[iface].endpoint); + priv->usb_interface[iface].nb_endpoints= 0; + + r = priv->apib->set_interface_altsetting(SUB_API_NOTSET, dev_handle, iface, altsetting); + + if (r == LIBUSB_SUCCESS) { + r = windows_assign_endpoints(dev_handle, iface, altsetting); + } + + return r; +} + +static int windows_release_interface(struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + return priv->apib->release_interface(SUB_API_NOTSET, dev_handle, iface); +} + +static int windows_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + return priv->apib->clear_halt(SUB_API_NOTSET, dev_handle, endpoint); +} + +static int windows_reset_device(struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + return priv->apib->reset_device(SUB_API_NOTSET, dev_handle); +} + +// The 3 functions below are unlikely to ever get supported on Windows +static int windows_kernel_driver_active(struct libusb_device_handle *dev_handle, int iface) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int windows_attach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static int windows_detach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) +{ + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +static void windows_destroy_device(struct libusb_device *dev) +{ + windows_device_priv_release(dev); +} + +static void windows_clear_transfer_priv(struct usbi_transfer *itransfer) +{ + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + + usbi_free_fd(&transfer_priv->pollable_fd); + safe_free(transfer_priv->hid_buffer); + // When auto claim is in use, attempt to release the auto-claimed interface + auto_release(itransfer); +} + +static int submit_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 windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int r; + + r = priv->apib->submit_bulk_transfer(SUB_API_NOTSET, itransfer); + if (r != LIBUSB_SUCCESS) { + return r; + } + + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, + (short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT)); + + itransfer->flags |= USBI_TRANSFER_UPDATED_FDS; + return LIBUSB_SUCCESS; +} + +static int submit_iso_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 windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int r; + + r = priv->apib->submit_iso_transfer(SUB_API_NOTSET, itransfer); + if (r != LIBUSB_SUCCESS) { + return r; + } + + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, + (short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT)); + + itransfer->flags |= USBI_TRANSFER_UPDATED_FDS; + return LIBUSB_SUCCESS; +} + +static int submit_control_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 windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int r; + + r = priv->apib->submit_control_transfer(SUB_API_NOTSET, itransfer); + if (r != LIBUSB_SUCCESS) { + return r; + } + + usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN); + + itransfer->flags |= USBI_TRANSFER_UPDATED_FDS; + return LIBUSB_SUCCESS; + +} + +static int windows_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: + if (IS_XFEROUT(transfer) && + transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) + return LIBUSB_ERROR_NOT_SUPPORTED; + return submit_bulk_transfer(itransfer); + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return 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 int windows_abort_control(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + + return priv->apib->abort_control(SUB_API_NOTSET, itransfer); +} + +static int windows_abort_transfers(struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + + return priv->apib->abort_transfers(SUB_API_NOTSET, itransfer); +} + +static int windows_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 windows_abort_control(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return windows_abort_transfers(itransfer); + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + return LIBUSB_ERROR_NOT_SUPPORTED; + default: + usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static void windows_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 windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int status, istatus; + + usbi_dbg("handling I/O completion with errcode %d, size %d", io_result, io_size); + + switch(io_result) { + case NO_ERROR: + status = priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, io_size); + break; + 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: + istatus = priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, io_size); + if (istatus != LIBUSB_TRANSFER_COMPLETED) { + usbi_dbg("Failed to copy partial data in aborted operation: %d", istatus); + } + 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 %d: %s", io_result, windows_error_str(io_result)); + status = LIBUSB_TRANSFER_ERROR; + break; + } + windows_clear_transfer_priv(itransfer); // Cancel polling + usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status); +} + +static void windows_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: + windows_transfer_callback (itransfer, io_result, io_size); + break; + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + usbi_warn(ITRANSFER_CTX(itransfer), "bulk stream transfers are not yet supported on this platform"); + break; + default: + usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); + } +} + +static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready) +{ + struct windows_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) { + // Handle async requests that completed synchronously first + if (HasOverlappedIoCompletedSync(transfer_priv->pollable_fd.overlapped)) { + io_result = NO_ERROR; + io_size = (DWORD)transfer_priv->pollable_fd.overlapped->InternalHigh; + // Regular async overlapped + } else if (GetOverlappedResult(transfer_priv->pollable_fd.handle, + transfer_priv->pollable_fd.overlapped, &io_size, false)) { + io_result = NO_ERROR; + } else { + io_result = GetLastError(); + } + 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. + windows_handle_callback(transfer, io_result, io_size); + } 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 windows_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 windows_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) && (hires_frequency != 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. + windows_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; + } + } +} + +static int windows_clock_gettime(int clk_id, struct timespec *tp) +{ + FILETIME filetime; + ULARGE_INTEGER rtime; + DWORD r; + 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) + GetSystemTimeAsFileTime(&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; + } +} + + +// NB: MSVC6 does not support named initializers. +const struct usbi_os_backend windows_backend = { + "Windows", + USBI_CAP_HAS_HID_ACCESS, + windows_init, + windows_exit, + + windows_get_device_list, + NULL, /* hotplug_poll */ + windows_open, + windows_close, + + windows_get_device_descriptor, + windows_get_active_config_descriptor, + windows_get_config_descriptor, + NULL, /* get_config_descriptor_by_value() */ + + windows_get_configuration, + windows_set_configuration, + windows_claim_interface, + windows_release_interface, + + windows_set_interface_altsetting, + windows_clear_halt, + windows_reset_device, + + NULL, /* alloc_streams */ + NULL, /* free_streams */ + + windows_kernel_driver_active, + windows_detach_kernel_driver, + windows_attach_kernel_driver, + + windows_destroy_device, + + windows_submit_transfer, + windows_cancel_transfer, + windows_clear_transfer_priv, + + windows_handle_events, + + windows_clock_gettime, +#if defined(USBI_TIMERFD_AVAILABLE) + NULL, +#endif + sizeof(struct windows_device_priv), + sizeof(struct windows_device_handle_priv), + sizeof(struct windows_transfer_priv), + 0, +}; + + +/* + * USB API backends + */ +static int unsupported_init(int sub_api, struct libusb_context *ctx) { + return LIBUSB_SUCCESS; +} +static int unsupported_exit(int sub_api) { + return LIBUSB_SUCCESS; +} +static int unsupported_open(int sub_api, struct libusb_device_handle *dev_handle) { + PRINT_UNSUPPORTED_API(open); +} +static void unsupported_close(int sub_api, struct libusb_device_handle *dev_handle) { + usbi_dbg("unsupported API call for 'close'"); +} +static int unsupported_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface) { + PRINT_UNSUPPORTED_API(configure_endpoints); +} +static int unsupported_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) { + PRINT_UNSUPPORTED_API(claim_interface); +} +static int unsupported_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting) { + PRINT_UNSUPPORTED_API(set_interface_altsetting); +} +static int unsupported_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) { + PRINT_UNSUPPORTED_API(release_interface); +} +static int unsupported_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) { + PRINT_UNSUPPORTED_API(clear_halt); +} +static int unsupported_reset_device(int sub_api, struct libusb_device_handle *dev_handle) { + PRINT_UNSUPPORTED_API(reset_device); +} +static int unsupported_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(submit_bulk_transfer); +} +static int unsupported_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(submit_iso_transfer); +} +static int unsupported_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(submit_control_transfer); +} +static int unsupported_abort_control(int sub_api, struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(abort_control); +} +static int unsupported_abort_transfers(int sub_api, struct usbi_transfer *itransfer) { + PRINT_UNSUPPORTED_API(abort_transfers); +} +static int unsupported_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size) { + PRINT_UNSUPPORTED_API(copy_transfer_data); +} +static int common_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface) { + return LIBUSB_SUCCESS; +} +// These names must be uppercase +const char* hub_driver_names[] = {"USBHUB", "USBHUB3", "USB3HUB", "NUSB3HUB", "RUSB3HUB", "FLXHCIH", "TIHUB3", "ETRONHUB3", "VIAHUB3", "ASMTHUB3", "IUSB3HUB", "VUSB3HUB", "AMDHUB30"}; +const char* composite_driver_names[] = {"USBCCGP"}; +const char* winusbx_driver_names[] = WINUSBX_DRV_NAMES; +const char* hid_driver_names[] = {"HIDUSB", "MOUHID", "KBDHID"}; +const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { + { + USB_API_UNSUPPORTED, + "Unsupported API", + NULL, + 0, + unsupported_init, + unsupported_exit, + unsupported_open, + unsupported_close, + unsupported_configure_endpoints, + unsupported_claim_interface, + unsupported_set_interface_altsetting, + unsupported_release_interface, + unsupported_clear_halt, + unsupported_reset_device, + unsupported_submit_bulk_transfer, + unsupported_submit_iso_transfer, + unsupported_submit_control_transfer, + unsupported_abort_control, + unsupported_abort_transfers, + unsupported_copy_transfer_data, + }, { + USB_API_HUB, + "HUB API", + hub_driver_names, + ARRAYSIZE(hub_driver_names), + unsupported_init, + unsupported_exit, + unsupported_open, + unsupported_close, + unsupported_configure_endpoints, + unsupported_claim_interface, + unsupported_set_interface_altsetting, + unsupported_release_interface, + unsupported_clear_halt, + unsupported_reset_device, + unsupported_submit_bulk_transfer, + unsupported_submit_iso_transfer, + unsupported_submit_control_transfer, + unsupported_abort_control, + unsupported_abort_transfers, + unsupported_copy_transfer_data, + }, { + USB_API_COMPOSITE, + "Composite API", + composite_driver_names, + ARRAYSIZE(composite_driver_names), + composite_init, + composite_exit, + composite_open, + composite_close, + common_configure_endpoints, + composite_claim_interface, + composite_set_interface_altsetting, + composite_release_interface, + composite_clear_halt, + composite_reset_device, + composite_submit_bulk_transfer, + composite_submit_iso_transfer, + composite_submit_control_transfer, + composite_abort_control, + composite_abort_transfers, + composite_copy_transfer_data, + }, { + USB_API_WINUSBX, + "WinUSB-like APIs", + winusbx_driver_names, + ARRAYSIZE(winusbx_driver_names), + winusbx_init, + winusbx_exit, + winusbx_open, + winusbx_close, + winusbx_configure_endpoints, + winusbx_claim_interface, + winusbx_set_interface_altsetting, + winusbx_release_interface, + winusbx_clear_halt, + winusbx_reset_device, + winusbx_submit_bulk_transfer, + unsupported_submit_iso_transfer, + winusbx_submit_control_transfer, + winusbx_abort_control, + winusbx_abort_transfers, + winusbx_copy_transfer_data, + }, { + USB_API_HID, + "HID API", + hid_driver_names, + ARRAYSIZE(hid_driver_names), + hid_init, + hid_exit, + hid_open, + hid_close, + common_configure_endpoints, + hid_claim_interface, + hid_set_interface_altsetting, + hid_release_interface, + hid_clear_halt, + hid_reset_device, + hid_submit_bulk_transfer, + unsupported_submit_iso_transfer, + hid_submit_control_transfer, + hid_abort_transfers, + hid_abort_transfers, + hid_copy_transfer_data, + }, +}; + + +/* + * WinUSB-like (WinUSB, libusb0/libusbK through libusbk DLL) API functions + */ +#define WinUSBX_Set(fn) do { if (native_winusb) WinUSBX[i].fn = (WinUsb_##fn##_t) GetProcAddress(h, "WinUsb_" #fn); \ + else pLibK_GetProcAddress((PVOID*)&WinUSBX[i].fn, i, KUSB_FNID_##fn); } while (0) + +static int winusbx_init(int sub_api, struct libusb_context *ctx) +{ + HMODULE h = NULL; + bool native_winusb = false; + int i; + KLIB_VERSION LibK_Version; + LibK_GetProcAddress_t pLibK_GetProcAddress = NULL; + LibK_GetVersion_t pLibK_GetVersion = NULL; + + h = GetModuleHandleA("libusbK"); + if (h == NULL) { + h = LoadLibraryA("libusbK"); + } + if (h == NULL) { + usbi_info(ctx, "libusbK DLL is not available, will use native WinUSB"); + h = GetModuleHandleA("WinUSB"); + if (h == NULL) { + h = LoadLibraryA("WinUSB"); + } if (h == NULL) { + usbi_warn(ctx, "WinUSB DLL is not available either,\n" + "you will not be able to access devices outside of enumeration"); + return LIBUSB_ERROR_NOT_FOUND; + } + } else { + usbi_dbg("using libusbK DLL for universal access"); + pLibK_GetVersion = (LibK_GetVersion_t) GetProcAddress(h, "LibK_GetVersion"); + if (pLibK_GetVersion != NULL) { + pLibK_GetVersion(&LibK_Version); + usbi_dbg("libusbK version: %d.%d.%d.%d", LibK_Version.Major, LibK_Version.Minor, + LibK_Version.Micro, LibK_Version.Nano); + } + pLibK_GetProcAddress = (LibK_GetProcAddress_t) GetProcAddress(h, "LibK_GetProcAddress"); + if (pLibK_GetProcAddress == NULL) { + usbi_err(ctx, "LibK_GetProcAddress() not found in libusbK DLL"); + return LIBUSB_ERROR_NOT_FOUND; + } + } + native_winusb = (pLibK_GetProcAddress == NULL); + for (i=SUB_API_LIBUSBK; idev); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + + HANDLE file_handle; + int i; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // WinUSB requires a seperate handle for each interface + for (i = 0; i < USB_MAXINTERFACES; i++) { + if ( (priv->usb_interface[i].path != NULL) + && (priv->usb_interface[i].apib->id == USB_API_WINUSBX) ) { + file_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + if (file_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not open device %s (interface %d): %s", priv->usb_interface[i].path, i, windows_error_str(0)); + switch(GetLastError()) { + case ERROR_FILE_NOT_FOUND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ACCESS_DENIED: + return LIBUSB_ERROR_ACCESS; + default: + return LIBUSB_ERROR_IO; + } + } + handle_priv->interface_handle[i].dev_handle = file_handle; + } + } + + return LIBUSB_SUCCESS; +} + +static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE file_handle; + int i; + + if (sub_api == SUB_API_NOTSET) + sub_api = priv->sub_api; + if (!WinUSBX[sub_api].initialized) + return; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].apib->id == USB_API_WINUSBX) { + file_handle = handle_priv->interface_handle[i].dev_handle; + if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) { + CloseHandle(file_handle); + } + } + } +} + +static int winusbx_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE winusb_handle = handle_priv->interface_handle[iface].api_handle; + UCHAR policy; + ULONG timeout = 0; + uint8_t endpoint_address; + int i; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // With handle and enpoints set (in parent), we can setup the default pipe properties + // see http://download.microsoft.com/download/D/1/D/D1DD7745-426B-4CC3-A269-ABBBE427C0EF/DVC-T705_DDC08.pptx + for (i=-1; iusb_interface[iface].nb_endpoints; i++) { + endpoint_address =(i==-1)?0:priv->usb_interface[iface].endpoint[i]; + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout)) { + usbi_dbg("failed to set PIPE_TRANSFER_TIMEOUT for control endpoint %02X", endpoint_address); + } + if ((i == -1) || (sub_api == SUB_API_LIBUSB0)) { + continue; // Other policies don't apply to control endpoint or libusb0 + } + policy = false; + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + SHORT_PACKET_TERMINATE, sizeof(UCHAR), &policy)) { + usbi_dbg("failed to disable SHORT_PACKET_TERMINATE for endpoint %02X", endpoint_address); + } + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + IGNORE_SHORT_PACKETS, sizeof(UCHAR), &policy)) { + usbi_dbg("failed to disable IGNORE_SHORT_PACKETS for endpoint %02X", endpoint_address); + } + policy = true; + /* ALLOW_PARTIAL_READS must be enabled due to likely libusbK bug. See: + https://sourceforge.net/mailarchive/message.php?msg_id=29736015 */ + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + ALLOW_PARTIAL_READS, sizeof(UCHAR), &policy)) { + usbi_dbg("failed to enable ALLOW_PARTIAL_READS for endpoint %02X", endpoint_address); + } + if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address, + AUTO_CLEAR_STALL, sizeof(UCHAR), &policy)) { + usbi_dbg("failed to enable AUTO_CLEAR_STALL for endpoint %02X", endpoint_address); + } + } + + return LIBUSB_SUCCESS; +} + +static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + bool is_using_usbccgp = (priv->apib->id == USB_API_COMPOSITE); + HANDLE file_handle, winusb_handle; + DWORD err; + int i; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL; + HDEVINFO dev_info = INVALID_HANDLE_VALUE; + SP_DEVINFO_DATA dev_info_data; + char* dev_path_no_guid = NULL; + char filter_path[] = "\\\\.\\libusb0-0000"; + bool found_filter = false; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // If the device is composite, but using the default Windows composite parent driver (usbccgp) + // or if it's the first WinUSB-like interface, we get a handle through Initialize(). + if ((is_using_usbccgp) || (iface == 0)) { + // composite device (independent interfaces) or interface 0 + file_handle = handle_priv->interface_handle[iface].dev_handle; + if ((file_handle == 0) || (file_handle == INVALID_HANDLE_VALUE)) { + return LIBUSB_ERROR_NOT_FOUND; + } + + if (!WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) { + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + err = GetLastError(); + switch(err) { + case ERROR_BAD_COMMAND: + // The device was disconnected + usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + default: + // it may be that we're using the libusb0 filter driver. + // TODO: can we move this whole business into the K/0 DLL? + for (i = 0; ; i++) { + safe_free(dev_interface_details); + safe_free(dev_path_no_guid); + dev_interface_details = get_interface_details_filter(ctx, &dev_info, &dev_info_data, &GUID_DEVINTERFACE_LIBUSB0_FILTER, i, filter_path); + if ((found_filter) || (dev_interface_details == NULL)) { + break; + } + // ignore GUID part + dev_path_no_guid = sanitize_path(strtok(dev_interface_details->DevicePath, "{")); + if (safe_strncmp(dev_path_no_guid, priv->usb_interface[iface].path, safe_strlen(dev_path_no_guid)) == 0) { + file_handle = CreateFileA(filter_path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + if (file_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not open device %s: %s", filter_path, windows_error_str(0)); + } else { + WinUSBX[sub_api].Free(winusb_handle); + if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) + found_filter = true; + else + usbi_err(ctx, "could not initialize filter driver for %s", filter_path); + } + } + } + if (!found_filter) { + usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(err)); + return LIBUSB_ERROR_ACCESS; + } + } + } + handle_priv->interface_handle[iface].api_handle = winusb_handle; + } else { + // For all other interfaces, use GetAssociatedInterface() + winusb_handle = handle_priv->interface_handle[0].api_handle; + // It is a requirement for multiple interface devices on Windows that, to you + // must first claim the first interface before you claim the others + if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) { + file_handle = handle_priv->interface_handle[0].dev_handle; + if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) { + handle_priv->interface_handle[0].api_handle = winusb_handle; + usbi_warn(ctx, "auto-claimed interface 0 (required to claim %d with WinUSB)", iface); + } else { + usbi_warn(ctx, "failed to auto-claim interface 0 (required to claim %d with WinUSB): %s", iface, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + } + if (!WinUSBX[sub_api].GetAssociatedInterface(winusb_handle, (UCHAR)(iface-1), + &handle_priv->interface_handle[iface].api_handle)) { + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + switch(GetLastError()) { + case ERROR_NO_MORE_ITEMS: // invalid iface + return LIBUSB_ERROR_NOT_FOUND; + case ERROR_BAD_COMMAND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ALREADY_EXISTS: // already claimed + return LIBUSB_ERROR_BUSY; + default: + usbi_err(ctx, "could not claim interface %d: %s", iface, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + } + } + usbi_dbg("claimed interface %d", iface); + handle_priv->active_interface = iface; + + return LIBUSB_SUCCESS; +} + +static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE winusb_handle; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + winusb_handle = handle_priv->interface_handle[iface].api_handle; + if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) { + return LIBUSB_ERROR_NOT_FOUND; + } + + WinUSBX[sub_api].Free(winusb_handle); + handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; + + return LIBUSB_SUCCESS; +} + +/* + * Return the first valid interface (of the same API type), for control transfers + */ +static int get_valid_interface(struct libusb_device_handle *dev_handle, int api_id) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int i; + + if ((api_id < USB_API_WINUSBX) || (api_id > USB_API_HID)) { + usbi_dbg("unsupported API ID"); + return -1; + } + + for (i=0; iinterface_handle[i].dev_handle != 0) + && (handle_priv->interface_handle[i].dev_handle != INVALID_HANDLE_VALUE) + && (handle_priv->interface_handle[i].api_handle != 0) + && (handle_priv->interface_handle[i].api_handle != INVALID_HANDLE_VALUE) + && (priv->usb_interface[i].apib->id == api_id) ) { + return i; + } + } + return -1; +} + +/* + * Lookup interface by endpoint address. -1 if not found + */ +static int interface_by_endpoint(struct windows_device_priv *priv, + struct windows_device_handle_priv *handle_priv, uint8_t endpoint_address) +{ + int i, j; + for (i=0; iinterface_handle[i].api_handle == INVALID_HANDLE_VALUE) + continue; + if (handle_priv->interface_handle[i].api_handle == 0) + continue; + if (priv->usb_interface[i].endpoint == NULL) + continue; + for (j=0; jusb_interface[i].nb_endpoints; j++) { + if (priv->usb_interface[i].endpoint[j] == endpoint_address) { + return i; + } + } + } + return -1; +} + +static int winusbx_submit_control_transfer(int sub_api, 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 windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = _device_handle_priv( + transfer->dev_handle); + WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *) transfer->buffer; + ULONG size; + HANDLE winusb_handle; + int current_interface; + struct winfd wfd; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + transfer_priv->pollable_fd = INVALID_WINFD; + size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + if (size > MAX_CTRL_BUFFER_LENGTH) + return LIBUSB_ERROR_INVALID_PARAM; + + current_interface = get_valid_interface(transfer->dev_handle, USB_API_WINUSBX); + if (current_interface < 0) { + if (auto_claim(transfer, ¤t_interface, USB_API_WINUSBX) != LIBUSB_SUCCESS) { + return LIBUSB_ERROR_NOT_FOUND; + } + } + + usbi_dbg("will use interface %d", current_interface); + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + wfd = usbi_create_fd(winusb_handle, RW_READ, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) { + return LIBUSB_ERROR_NO_MEM; + } + + // Sending of set configuration control requests from WinUSB creates issues + if ( ((setup->request_type & (0x03 << 5)) == LIBUSB_REQUEST_TYPE_STANDARD) + && (setup->request == LIBUSB_REQUEST_SET_CONFIGURATION) ) { + if (setup->value != priv->active_config) { + usbi_warn(ctx, "cannot set configuration other than the default one"); + usbi_free_fd(&wfd); + return LIBUSB_ERROR_INVALID_PARAM; + } + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = 0; + } else { + ULONG timeout = 0; + WinUSBX[sub_api].SetPipePolicy(wfd.handle, 0, PIPE_TRANSFER_TIMEOUT, + sizeof(ULONG), &timeout); + if (!WinUSBX[sub_api].ControlTransfer(wfd.handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, wfd.overlapped)) { + if(GetLastError() != ERROR_IO_PENDING) { + usbi_warn(ctx, "ControlTransfer failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + return LIBUSB_ERROR_IO; + } + } else { + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = (DWORD)size; + } + } + + // Use priv_transfer to store data needed for async polling + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + + return LIBUSB_SUCCESS; +} + +static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE winusb_handle; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + if (altsetting > 255) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + winusb_handle = handle_priv->interface_handle[iface].api_handle; + if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) { + usbi_err(ctx, "interface must be claimed first"); + return LIBUSB_ERROR_NOT_FOUND; + } + + if (!WinUSBX[sub_api].SetCurrentAlternateSetting(winusb_handle, (UCHAR)altsetting)) { + usbi_err(ctx, "SetCurrentAlternateSetting failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + + return LIBUSB_SUCCESS; +} + +static int winusbx_submit_bulk_transfer(int sub_api, 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 windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + HANDLE winusb_handle; + bool ret; + int current_interface; + struct winfd wfd; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + transfer_priv->pollable_fd = INVALID_WINFD; + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface); + + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + wfd = usbi_create_fd(winusb_handle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) { + return LIBUSB_ERROR_NO_MEM; + } + + if (IS_XFERIN(transfer)) { + usbi_dbg("reading %d bytes", transfer->length); + ret = WinUSBX[sub_api].ReadPipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped); + } else { + usbi_dbg("writing %d bytes", transfer->length); + ret = WinUSBX[sub_api].WritePipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped); + } + if (!ret) { + if(GetLastError() != ERROR_IO_PENDING) { + usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + return LIBUSB_ERROR_IO; + } + } else { + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = (DWORD)transfer->length; + } + + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + + return LIBUSB_SUCCESS; +} + +static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE winusb_handle; + int current_interface; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface); + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + if (!WinUSBX[sub_api].ResetPipe(winusb_handle, endpoint)) { + usbi_err(ctx, "ResetPipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +/* + * from http://www.winvistatips.com/winusb-bugchecks-t335323.html (confirmed + * through testing as well): + * "You can not call WinUsb_AbortPipe on control pipe. You can possibly cancel + * the control transfer using CancelIo" + */ +static int winusbx_abort_control(int sub_api, struct usbi_transfer *itransfer) +{ + // Cancelling of the I/O is done in the parent + return LIBUSB_SUCCESS; +} + +static int winusbx_abort_transfers(int sub_api, 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 windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + HANDLE winusb_handle; + int current_interface; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + current_interface = transfer_priv->interface_number; + if ((current_interface < 0) || (current_interface >= USB_MAXINTERFACES)) { + usbi_err(ctx, "program assertion failed: invalid interface_number"); + return LIBUSB_ERROR_NOT_FOUND; + } + usbi_dbg("will use interface %d", current_interface); + + winusb_handle = handle_priv->interface_handle[current_interface].api_handle; + + if (!WinUSBX[sub_api].AbortPipe(winusb_handle, transfer->endpoint)) { + usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0)); + + + libusb_lock_events(ctx); + usbi_mutex_lock(&ctx->open_devs_lock); + usbi_mutex_lock(&ctx->flying_transfers_lock); + + + + usbi_remove_pollfd(ctx, transfer_priv->pollable_fd.fd); + list_del(&itransfer->list); + + + usbi_mutex_unlock(&ctx->flying_transfers_lock); + usbi_mutex_unlock(&ctx->open_devs_lock); + + libusb_unlock_events(ctx); + + + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +/* + * from the "How to Use WinUSB to Communicate with a USB Device" Microsoft white paper + * (http://www.microsoft.com/whdc/connect/usb/winusb_howto.mspx): + * "WinUSB does not support host-initiated reset port and cycle port operations" and + * IOCTL_INTERNAL_USB_CYCLE_PORT is only available in kernel mode and the + * IOCTL_USB_HUB_CYCLE_PORT ioctl was removed from Vista => the best we can do is + * cycle the pipes (and even then, the control pipe can not be reset using WinUSB) + */ +// TODO: (post hotplug): see if we can force eject the device and redetect it (reuse hotplug?) +static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct winfd wfd; + HANDLE winusb_handle; + int i, j; + + CHECK_WINUSBX_AVAILABLE(sub_api); + + // Reset any available pipe (except control) + for (i=0; iinterface_handle[i].api_handle; + for (wfd = handle_to_winfd(winusb_handle); wfd.fd > 0;) + { + // Cancel any pollable I/O + usbi_remove_pollfd(ctx, wfd.fd); + usbi_free_fd(&wfd); + wfd = handle_to_winfd(winusb_handle); + } + + if ( (winusb_handle != 0) && (winusb_handle != INVALID_HANDLE_VALUE)) { + for (j=0; jusb_interface[i].nb_endpoints; j++) { + usbi_dbg("resetting ep %02X", priv->usb_interface[i].endpoint[j]); + if (!WinUSBX[sub_api].AbortPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) { + usbi_err(ctx, "AbortPipe (pipe address %02X) failed: %s", + priv->usb_interface[i].endpoint[j], windows_error_str(0)); + } + // FlushPipe seems to fail on OUT pipes + if (IS_EPIN(priv->usb_interface[i].endpoint[j]) + && (!WinUSBX[sub_api].FlushPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) ) { + usbi_err(ctx, "FlushPipe (pipe address %02X) failed: %s", + priv->usb_interface[i].endpoint[j], windows_error_str(0)); + } + if (!WinUSBX[sub_api].ResetPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) { + usbi_err(ctx, "ResetPipe (pipe address %02X) failed: %s", + priv->usb_interface[i].endpoint[j], windows_error_str(0)); + } + } + } + } + + // libusbK & libusb0 have the ability to issue an actual device reset + if (WinUSBX[sub_api].ResetDevice != NULL) { + winusb_handle = handle_priv->interface_handle[0].api_handle; + if ( (winusb_handle != 0) && (winusb_handle != INVALID_HANDLE_VALUE)) { + WinUSBX[sub_api].ResetDevice(winusb_handle); + } + } + return LIBUSB_SUCCESS; +} + +static int winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size) +{ + itransfer->transferred += io_size; + return LIBUSB_TRANSFER_COMPLETED; +} + +/* + * Internal HID Support functions (from libusb-win32) + * Note that functions that complete data transfer synchronously must return + * LIBUSB_COMPLETED instead of LIBUSB_SUCCESS + */ +static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size); +static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size); + +static int _hid_wcslen(WCHAR *str) +{ + int i = 0; + while (str[i] && (str[i] != 0x409)) { + i++; + } + return i; +} + +static int _hid_get_device_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + struct libusb_device_descriptor d; + + d.bLength = LIBUSB_DT_DEVICE_SIZE; + d.bDescriptorType = LIBUSB_DT_DEVICE; + d.bcdUSB = 0x0200; /* 2.00 */ + d.bDeviceClass = 0; + d.bDeviceSubClass = 0; + d.bDeviceProtocol = 0; + d.bMaxPacketSize0 = 64; /* fix this! */ + d.idVendor = (uint16_t)dev->vid; + d.idProduct = (uint16_t)dev->pid; + d.bcdDevice = 0x0100; + d.iManufacturer = dev->string_index[0]; + d.iProduct = dev->string_index[1]; + d.iSerialNumber = dev->string_index[2]; + d.bNumConfigurations = 1; + + if (*size > LIBUSB_DT_DEVICE_SIZE) + *size = LIBUSB_DT_DEVICE_SIZE; + memcpy(data, &d, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_config_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + char num_endpoints = 0; + size_t config_total_len = 0; + char tmp[HID_MAX_CONFIG_DESC_SIZE]; + struct libusb_config_descriptor *cd; + struct libusb_interface_descriptor *id; + struct libusb_hid_descriptor *hd; + struct libusb_endpoint_descriptor *ed; + size_t tmp_size; + + if (dev->input_report_size) + num_endpoints++; + if (dev->output_report_size) + num_endpoints++; + + config_total_len = LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE + + LIBUSB_DT_HID_SIZE + num_endpoints * LIBUSB_DT_ENDPOINT_SIZE; + + + cd = (struct libusb_config_descriptor *)tmp; + id = (struct libusb_interface_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE); + hd = (struct libusb_hid_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE + + LIBUSB_DT_INTERFACE_SIZE); + ed = (struct libusb_endpoint_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE + + LIBUSB_DT_INTERFACE_SIZE + + LIBUSB_DT_HID_SIZE); + + cd->bLength = LIBUSB_DT_CONFIG_SIZE; + cd->bDescriptorType = LIBUSB_DT_CONFIG; + cd->wTotalLength = (uint16_t) config_total_len; + cd->bNumInterfaces = 1; + cd->bConfigurationValue = 1; + cd->iConfiguration = 0; + cd->bmAttributes = 1 << 7; /* bus powered */ + cd->MaxPower = 50; + + id->bLength = LIBUSB_DT_INTERFACE_SIZE; + id->bDescriptorType = LIBUSB_DT_INTERFACE; + id->bInterfaceNumber = 0; + id->bAlternateSetting = 0; + id->bNumEndpoints = num_endpoints; + id->bInterfaceClass = 3; + id->bInterfaceSubClass = 0; + id->bInterfaceProtocol = 0; + id->iInterface = 0; + + tmp_size = LIBUSB_DT_HID_SIZE; + _hid_get_hid_descriptor(dev, hd, &tmp_size); + + if (dev->input_report_size) { + ed->bLength = LIBUSB_DT_ENDPOINT_SIZE; + ed->bDescriptorType = LIBUSB_DT_ENDPOINT; + ed->bEndpointAddress = HID_IN_EP; + ed->bmAttributes = 3; + ed->wMaxPacketSize = dev->input_report_size - 1; + ed->bInterval = 10; + ed = (struct libusb_endpoint_descriptor *)((char*)ed + LIBUSB_DT_ENDPOINT_SIZE); + } + + if (dev->output_report_size) { + ed->bLength = LIBUSB_DT_ENDPOINT_SIZE; + ed->bDescriptorType = LIBUSB_DT_ENDPOINT; + ed->bEndpointAddress = HID_OUT_EP; + ed->bmAttributes = 3; + ed->wMaxPacketSize = dev->output_report_size - 1; + ed->bInterval = 10; + } + + if (*size > config_total_len) + *size = config_total_len; + memcpy(data, tmp, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_string_descriptor(struct hid_device_priv* dev, int _index, + void *data, size_t *size) +{ + void *tmp = NULL; + size_t tmp_size = 0; + int i; + + /* language ID, EN-US */ + char string_langid[] = { + 0x09, + 0x04 + }; + + if ((*size < 2) || (*size > 255)) { + return LIBUSB_ERROR_OVERFLOW; + } + + if (_index == 0) { + tmp = string_langid; + tmp_size = sizeof(string_langid)+2; + } else { + for (i=0; i<3; i++) { + if (_index == (dev->string_index[i])) { + tmp = dev->string[i]; + tmp_size = (_hid_wcslen(dev->string[i])+1) * sizeof(WCHAR); + break; + } + } + if (i == 3) { // not found + return LIBUSB_ERROR_INVALID_PARAM; + } + } + + if(!tmp_size) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (tmp_size < *size) { + *size = tmp_size; + } + // 2 byte header + ((uint8_t*)data)[0] = (uint8_t)*size; + ((uint8_t*)data)[1] = LIBUSB_DT_STRING; + memcpy((uint8_t*)data+2, tmp, *size-2); + return LIBUSB_COMPLETED; +} + +static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + struct libusb_hid_descriptor d; + uint8_t tmp[MAX_HID_DESCRIPTOR_SIZE]; + size_t report_len = MAX_HID_DESCRIPTOR_SIZE; + + _hid_get_report_descriptor(dev, tmp, &report_len); + + d.bLength = LIBUSB_DT_HID_SIZE; + d.bDescriptorType = LIBUSB_DT_HID; + d.bcdHID = 0x0110; /* 1.10 */ + d.bCountryCode = 0; + d.bNumDescriptors = 1; + d.bClassDescriptorType = LIBUSB_DT_REPORT; + d.wClassDescriptorLength = (uint16_t)report_len; + + if (*size > LIBUSB_DT_HID_SIZE) + *size = LIBUSB_DT_HID_SIZE; + memcpy(data, &d, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + uint8_t d[MAX_HID_DESCRIPTOR_SIZE]; + size_t i = 0; + + /* usage page (0xFFA0 == vendor defined) */ + d[i++] = 0x06; d[i++] = 0xA0; d[i++] = 0xFF; + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x01; + /* start collection (application) */ + d[i++] = 0xA1; d[i++] = 0x01; + /* input report */ + if (dev->input_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x01; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->input_report_size - 1; + /* input (data, variable, absolute) */ + d[i++] = 0x81; d[i++] = 0x00; + } + /* output report */ + if (dev->output_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x02; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->output_report_size - 1; + /* output (data, variable, absolute) */ + d[i++] = 0x91; d[i++] = 0x00; + } + /* feature report */ + if (dev->feature_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x03; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->feature_report_size - 1; + /* feature (data, variable, absolute) */ + d[i++] = 0xb2; d[i++] = 0x02; d[i++] = 0x01; + } + + /* end collection */ + d[i++] = 0xC0; + + if (*size > i) + *size = i; + memcpy(data, d, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_descriptor(struct hid_device_priv* dev, HANDLE hid_handle, int recipient, + int type, int _index, void *data, size_t *size) +{ + switch(type) { + case LIBUSB_DT_DEVICE: + usbi_dbg("LIBUSB_DT_DEVICE"); + return _hid_get_device_descriptor(dev, data, size); + case LIBUSB_DT_CONFIG: + usbi_dbg("LIBUSB_DT_CONFIG"); + if (!_index) + return _hid_get_config_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_STRING: + usbi_dbg("LIBUSB_DT_STRING"); + return _hid_get_string_descriptor(dev, _index, data, size); + case LIBUSB_DT_HID: + usbi_dbg("LIBUSB_DT_HID"); + if (!_index) + return _hid_get_hid_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_REPORT: + usbi_dbg("LIBUSB_DT_REPORT"); + if (!_index) + return _hid_get_report_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_PHYSICAL: + usbi_dbg("LIBUSB_DT_PHYSICAL"); + if (HidD_GetPhysicalDescriptor(hid_handle, data, (ULONG)*size)) + return LIBUSB_COMPLETED; + return LIBUSB_ERROR_OTHER; + } + usbi_dbg("unsupported"); + return LIBUSB_ERROR_INVALID_PARAM; +} + +static int _hid_get_report(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data, + struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped, + int report_type) +{ + uint8_t *buf; + DWORD ioctl_code, read_size, expected_size = (DWORD)*size; + int r = LIBUSB_SUCCESS; + + if (tp->hid_buffer != NULL) { + usbi_dbg("program assertion failed: hid_buffer is not NULL"); + } + + if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) { + usbi_dbg("invalid size (%d)", *size); + return LIBUSB_ERROR_INVALID_PARAM; + } + + switch (report_type) { + case HID_REPORT_TYPE_INPUT: + ioctl_code = IOCTL_HID_GET_INPUT_REPORT; + break; + case HID_REPORT_TYPE_FEATURE: + ioctl_code = IOCTL_HID_GET_FEATURE; + break; + default: + usbi_dbg("unknown HID report type %d", report_type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + // Add a trailing byte to detect overflows + buf = (uint8_t*)calloc(expected_size+1, 1); + if (buf == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + buf[0] = (uint8_t)id; // Must be set always + usbi_dbg("report ID: 0x%02X", buf[0]); + + tp->hid_expected_size = expected_size; + read_size = expected_size; + + // NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0) + if (!DeviceIoControl(hid_handle, ioctl_code, buf, expected_size+1, + buf, expected_size+1, &read_size, overlapped)) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_dbg("Failed to Read HID Report: %s", windows_error_str(0)); + safe_free(buf); + return LIBUSB_ERROR_IO; + } + // Asynchronous wait + tp->hid_buffer = buf; + tp->hid_dest = (uint8_t*)data; // copy dest, as not necessarily the start of the transfer buffer + return LIBUSB_SUCCESS; + } + + // Transfer completed synchronously => copy and discard extra buffer + if (read_size == 0) { + usbi_warn(NULL, "program assertion failed - read completed synchronously, but no data was read"); + *size = 0; + } else { + if (buf[0] != id) { + usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id); + } + if ((size_t)read_size > expected_size) { + r = LIBUSB_ERROR_OVERFLOW; + usbi_dbg("OVERFLOW!"); + } else { + r = LIBUSB_COMPLETED; + } + + *size = MIN((size_t)read_size, *size); + if (id == 0) { + // Discard report ID + memcpy(data, buf+1, *size); + } else { + memcpy(data, buf, *size); + } + } + safe_free(buf); + return r; +} + +static int _hid_set_report(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data, + struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped, + int report_type) +{ + uint8_t *buf = NULL; + DWORD ioctl_code, write_size= (DWORD)*size; + + if (tp->hid_buffer != NULL) { + usbi_dbg("program assertion failed: hid_buffer is not NULL"); + } + + if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) { + usbi_dbg("invalid size (%d)", *size); + return LIBUSB_ERROR_INVALID_PARAM; + } + + switch (report_type) { + case HID_REPORT_TYPE_OUTPUT: + ioctl_code = IOCTL_HID_SET_OUTPUT_REPORT; + break; + case HID_REPORT_TYPE_FEATURE: + ioctl_code = IOCTL_HID_SET_FEATURE; + break; + default: + usbi_dbg("unknown HID report type %d", report_type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + usbi_dbg("report ID: 0x%02X", id); + // When report IDs are not used (i.e. when id == 0), we must add + // a null report ID. Otherwise, we just use original data buffer + if (id == 0) { + write_size++; + } + buf = (uint8_t*) malloc(write_size); + if (buf == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + if (id == 0) { + buf[0] = 0; + memcpy(buf + 1, data, *size); + } else { + // This seems like a waste, but if we don't duplicate the + // data, we'll get issues when freeing hid_buffer + memcpy(buf, data, *size); + if (buf[0] != id) { + usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id); + } + } + + // NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0) + if (!DeviceIoControl(hid_handle, ioctl_code, buf, write_size, + buf, write_size, &write_size, overlapped)) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_dbg("Failed to Write HID Output Report: %s", windows_error_str(0)); + safe_free(buf); + return LIBUSB_ERROR_IO; + } + tp->hid_buffer = buf; + tp->hid_dest = NULL; + return LIBUSB_SUCCESS; + } + + // Transfer completed synchronously + *size = write_size; + if (write_size == 0) { + usbi_dbg("program assertion failed - write completed synchronously, but no data was written"); + } + safe_free(buf); + return LIBUSB_COMPLETED; +} + +static int _hid_class_request(struct hid_device_priv* dev, HANDLE hid_handle, int request_type, + int request, int value, int _index, void *data, struct windows_transfer_priv *tp, + size_t *size, OVERLAPPED* overlapped) +{ + int report_type = (value >> 8) & 0xFF; + int report_id = value & 0xFF; + + if ( (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_INTERFACE) + && (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_DEVICE) ) + return LIBUSB_ERROR_INVALID_PARAM; + + if (LIBUSB_REQ_OUT(request_type) && request == HID_REQ_SET_REPORT) + return _hid_set_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type); + + if (LIBUSB_REQ_IN(request_type) && request == HID_REQ_GET_REPORT) + return _hid_get_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type); + + return LIBUSB_ERROR_INVALID_PARAM; +} + + +/* + * HID API functions + */ +static int hid_init(int sub_api, struct libusb_context *ctx) +{ + DLL_LOAD(hid.dll, HidD_GetAttributes, TRUE); + DLL_LOAD(hid.dll, HidD_GetHidGuid, TRUE); + DLL_LOAD(hid.dll, HidD_GetPreparsedData, TRUE); + DLL_LOAD(hid.dll, HidD_FreePreparsedData, TRUE); + DLL_LOAD(hid.dll, HidD_GetManufacturerString, TRUE); + DLL_LOAD(hid.dll, HidD_GetProductString, TRUE); + DLL_LOAD(hid.dll, HidD_GetSerialNumberString, TRUE); + DLL_LOAD(hid.dll, HidP_GetCaps, TRUE); + DLL_LOAD(hid.dll, HidD_SetNumInputBuffers, TRUE); + DLL_LOAD(hid.dll, HidD_SetFeature, TRUE); + DLL_LOAD(hid.dll, HidD_GetFeature, TRUE); + DLL_LOAD(hid.dll, HidD_GetPhysicalDescriptor, TRUE); + DLL_LOAD(hid.dll, HidD_GetInputReport, FALSE); + DLL_LOAD(hid.dll, HidD_SetOutputReport, FALSE); + DLL_LOAD(hid.dll, HidD_FlushQueue, TRUE); + DLL_LOAD(hid.dll, HidP_GetValueCaps, TRUE); + + api_hid_available = true; + return LIBUSB_SUCCESS; +} + +static int hid_exit(int sub_api) +{ + return LIBUSB_SUCCESS; +} + +// NB: open and close must ensure that they only handle interface of +// the right API type, as these functions can be called wholesale from +// composite_open(), with interfaces belonging to different APIs +static int hid_open(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + + HIDD_ATTRIBUTES hid_attributes; + PHIDP_PREPARSED_DATA preparsed_data = NULL; + HIDP_CAPS capabilities; + HIDP_VALUE_CAPS *value_caps; + + HANDLE hid_handle = INVALID_HANDLE_VALUE; + int i, j; + // report IDs handling + ULONG size[3]; + const char* type[3] = {"input", "output", "feature"}; + int nb_ids[2]; // zero and nonzero report IDs + + CHECK_HID_AVAILABLE; + if (priv->hid == NULL) { + usbi_err(ctx, "program assertion failed - private HID structure is unitialized"); + return LIBUSB_ERROR_NOT_FOUND; + } + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if ( (priv->usb_interface[i].path != NULL) + && (priv->usb_interface[i].apib->id == USB_API_HID) ) { + hid_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + /* + * http://www.lvr.com/hidfaq.htm: Why do I receive "Access denied" when attempting to access my HID? + * "Windows 2000 and later have exclusive read/write access to HIDs that are configured as a system + * keyboards or mice. An application can obtain a handle to a system keyboard or mouse by not + * requesting READ or WRITE access with CreateFile. Applications can then use HidD_SetFeature and + * HidD_GetFeature (if the device supports Feature reports)." + */ + if (hid_handle == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "could not open HID device in R/W mode (keyboard or mouse?) - trying without"); + hid_handle = CreateFileA(priv->usb_interface[i].path, 0, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + if (hid_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not open device %s (interface %d): %s", priv->path, i, windows_error_str(0)); + switch(GetLastError()) { + case ERROR_FILE_NOT_FOUND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ACCESS_DENIED: + return LIBUSB_ERROR_ACCESS; + default: + return LIBUSB_ERROR_IO; + } + } + priv->usb_interface[i].restricted_functionality = true; + } + handle_priv->interface_handle[i].api_handle = hid_handle; + } + } + + hid_attributes.Size = sizeof(hid_attributes); + do { + if (!HidD_GetAttributes(hid_handle, &hid_attributes)) { + usbi_err(ctx, "could not gain access to HID top collection (HidD_GetAttributes)"); + break; + } + + priv->hid->vid = hid_attributes.VendorID; + priv->hid->pid = hid_attributes.ProductID; + + // Set the maximum available input buffer size + for (i=32; HidD_SetNumInputBuffers(hid_handle, i); i*=2); + usbi_dbg("set maximum input buffer size to %d", i/2); + + // Get the maximum input and output report size + if (!HidD_GetPreparsedData(hid_handle, &preparsed_data) || !preparsed_data) { + usbi_err(ctx, "could not read HID preparsed data (HidD_GetPreparsedData)"); + break; + } + if (HidP_GetCaps(preparsed_data, &capabilities) != HIDP_STATUS_SUCCESS) { + usbi_err(ctx, "could not parse HID capabilities (HidP_GetCaps)"); + break; + } + + // Find out if interrupt will need report IDs + size[0] = capabilities.NumberInputValueCaps; + size[1] = capabilities.NumberOutputValueCaps; + size[2] = capabilities.NumberFeatureValueCaps; + for (j=HidP_Input; j<=HidP_Feature; j++) { + usbi_dbg("%d HID %s report value(s) found", size[j], type[j]); + priv->hid->uses_report_ids[j] = false; + if (size[j] > 0) { + value_caps = (HIDP_VALUE_CAPS*) calloc(size[j], sizeof(HIDP_VALUE_CAPS)); + if ( (value_caps != NULL) + && (HidP_GetValueCaps((HIDP_REPORT_TYPE)j, value_caps, &size[j], preparsed_data) == HIDP_STATUS_SUCCESS) + && (size[j] >= 1) ) { + nb_ids[0] = 0; + nb_ids[1] = 0; + for (i=0; i<(int)size[j]; i++) { + usbi_dbg(" Report ID: 0x%02X", value_caps[i].ReportID); + if (value_caps[i].ReportID != 0) { + nb_ids[1]++; + } else { + nb_ids[0]++; + } + } + if (nb_ids[1] != 0) { + if (nb_ids[0] != 0) { + usbi_warn(ctx, "program assertion failed: zero and nonzero report IDs used for %s", + type[j]); + } + priv->hid->uses_report_ids[j] = true; + } + } else { + usbi_warn(ctx, " could not process %s report IDs", type[j]); + } + safe_free(value_caps); + } + } + + // Set the report sizes + priv->hid->input_report_size = capabilities.InputReportByteLength; + priv->hid->output_report_size = capabilities.OutputReportByteLength; + priv->hid->feature_report_size = capabilities.FeatureReportByteLength; + + // Fetch string descriptors + priv->hid->string_index[0] = priv->dev_descriptor.iManufacturer; + if (priv->hid->string_index[0] != 0) { + HidD_GetManufacturerString(hid_handle, priv->hid->string[0], + sizeof(priv->hid->string[0])); + } else { + priv->hid->string[0][0] = 0; + } + priv->hid->string_index[1] = priv->dev_descriptor.iProduct; + if (priv->hid->string_index[1] != 0) { + HidD_GetProductString(hid_handle, priv->hid->string[1], + sizeof(priv->hid->string[1])); + } else { + priv->hid->string[1][0] = 0; + } + priv->hid->string_index[2] = priv->dev_descriptor.iSerialNumber; + if (priv->hid->string_index[2] != 0) { + HidD_GetSerialNumberString(hid_handle, priv->hid->string[2], + sizeof(priv->hid->string[2])); + } else { + priv->hid->string[2][0] = 0; + } + } while(0); + + if (preparsed_data) { + HidD_FreePreparsedData(preparsed_data); + } + + return LIBUSB_SUCCESS; +} + +static void hid_close(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + HANDLE file_handle; + int i; + + if (!api_hid_available) + return; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].apib->id == USB_API_HID) { + file_handle = handle_priv->interface_handle[i].api_handle; + if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) { + CloseHandle(file_handle); + } + } + } +} + +static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + // NB: Disconnection detection is not possible in this function + if (priv->usb_interface[iface].path == NULL) { + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + } + + // We use dev_handle as a flag for interface claimed + if (handle_priv->interface_handle[iface].dev_handle == INTERFACE_CLAIMED) { + return LIBUSB_ERROR_BUSY; // already claimed + } + + handle_priv->interface_handle[iface].dev_handle = INTERFACE_CLAIMED; + + usbi_dbg("claimed interface %d", iface); + handle_priv->active_interface = iface; + + return LIBUSB_SUCCESS; +} + +static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + if (priv->usb_interface[iface].path == NULL) { + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + } + + if (handle_priv->interface_handle[iface].dev_handle != INTERFACE_CLAIMED) { + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + } + + handle_priv->interface_handle[iface].dev_handle = INVALID_HANDLE_VALUE; + + return LIBUSB_SUCCESS; +} + +static int hid_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + if (altsetting > 255) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (altsetting != 0) { + usbi_err(ctx, "set interface altsetting not supported for altsetting >0"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + return LIBUSB_SUCCESS; +} + +static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *) transfer->buffer; + HANDLE hid_handle; + struct winfd wfd; + int current_interface, config; + size_t size; + int r = LIBUSB_ERROR_INVALID_PARAM; + + CHECK_HID_AVAILABLE; + + transfer_priv->pollable_fd = INVALID_WINFD; + safe_free(transfer_priv->hid_buffer); + transfer_priv->hid_dest = NULL; + size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + if (size > MAX_CTRL_BUFFER_LENGTH) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + current_interface = get_valid_interface(transfer->dev_handle, USB_API_HID); + if (current_interface < 0) { + if (auto_claim(transfer, ¤t_interface, USB_API_HID) != LIBUSB_SUCCESS) { + return LIBUSB_ERROR_NOT_FOUND; + } + } + + usbi_dbg("will use interface %d", current_interface); + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + // Always use the handle returned from usbi_create_fd (wfd.handle) + wfd = usbi_create_fd(hid_handle, RW_READ, NULL, NULL); + if (wfd.fd < 0) { + return LIBUSB_ERROR_NOT_FOUND; + } + + switch(LIBUSB_REQ_TYPE(setup->request_type)) { + case LIBUSB_REQUEST_TYPE_STANDARD: + switch(setup->request) { + case LIBUSB_REQUEST_GET_DESCRIPTOR: + r = _hid_get_descriptor(priv->hid, wfd.handle, LIBUSB_REQ_RECIPIENT(setup->request_type), + (setup->value >> 8) & 0xFF, setup->value & 0xFF, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, &size); + break; + case LIBUSB_REQUEST_GET_CONFIGURATION: + r = windows_get_configuration(transfer->dev_handle, &config); + if (r == LIBUSB_SUCCESS) { + size = 1; + ((uint8_t*)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = (uint8_t)config; + r = LIBUSB_COMPLETED; + } + break; + case LIBUSB_REQUEST_SET_CONFIGURATION: + if (setup->value == priv->active_config) { + r = LIBUSB_COMPLETED; + } else { + usbi_warn(ctx, "cannot set configuration other than the default one"); + r = LIBUSB_ERROR_INVALID_PARAM; + } + break; + case LIBUSB_REQUEST_GET_INTERFACE: + size = 1; + ((uint8_t*)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = 0; + r = LIBUSB_COMPLETED; + break; + case LIBUSB_REQUEST_SET_INTERFACE: + r = hid_set_interface_altsetting(0, transfer->dev_handle, setup->index, setup->value); + if (r == LIBUSB_SUCCESS) { + r = LIBUSB_COMPLETED; + } + break; + default: + usbi_warn(ctx, "unsupported HID control request"); + r = LIBUSB_ERROR_INVALID_PARAM; + break; + } + break; + case LIBUSB_REQUEST_TYPE_CLASS: + r =_hid_class_request(priv->hid, wfd.handle, setup->request_type, setup->request, setup->value, + setup->index, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, transfer_priv, + &size, wfd.overlapped); + break; + default: + usbi_warn(ctx, "unsupported HID control request"); + r = LIBUSB_ERROR_INVALID_PARAM; + break; + } + + if (r == LIBUSB_COMPLETED) { + // Force request to be completed synchronously. Transferred size has been set by previous call + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + // http://msdn.microsoft.com/en-us/library/ms684342%28VS.85%29.aspx + // set InternalHigh to the number of bytes transferred + wfd.overlapped->InternalHigh = (DWORD)size; + r = LIBUSB_SUCCESS; + } + + if (r == LIBUSB_SUCCESS) { + // Use priv_transfer to store data needed for async polling + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + } else { + usbi_free_fd(&wfd); + } + + return r; +} + +static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + struct winfd wfd; + HANDLE hid_handle; + bool direction_in, ret; + int current_interface, length; + DWORD size; + int r = LIBUSB_SUCCESS; + + CHECK_HID_AVAILABLE; + + transfer_priv->pollable_fd = INVALID_WINFD; + transfer_priv->hid_dest = NULL; + safe_free(transfer_priv->hid_buffer); + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface); + + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN; + + wfd = usbi_create_fd(hid_handle, direction_in?RW_READ:RW_WRITE, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) { + return LIBUSB_ERROR_NO_MEM; + } + + // If report IDs are not in use, an extra prefix byte must be added + if ( ((direction_in) && (!priv->hid->uses_report_ids[0])) + || ((!direction_in) && (!priv->hid->uses_report_ids[1])) ) { + length = transfer->length+1; + } else { + length = transfer->length; + } + // Add a trailing byte to detect overflows on input + transfer_priv->hid_buffer = (uint8_t*)calloc(length+1, 1); + if (transfer_priv->hid_buffer == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + transfer_priv->hid_expected_size = length; + + if (direction_in) { + transfer_priv->hid_dest = transfer->buffer; + usbi_dbg("reading %d bytes (report ID: 0x00)", length); + ret = ReadFile(wfd.handle, transfer_priv->hid_buffer, length+1, &size, wfd.overlapped); + } else { + if (!priv->hid->uses_report_ids[1]) { + memcpy(transfer_priv->hid_buffer+1, transfer->buffer, transfer->length); + } else { + // We could actually do without the calloc and memcpy in this case + memcpy(transfer_priv->hid_buffer, transfer->buffer, transfer->length); + } + usbi_dbg("writing %d bytes (report ID: 0x%02X)", length, transfer_priv->hid_buffer[0]); + ret = WriteFile(wfd.handle, transfer_priv->hid_buffer, length, &size, wfd.overlapped); + } + if (!ret) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_err(ctx, "HID transfer failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + safe_free(transfer_priv->hid_buffer); + return LIBUSB_ERROR_IO; + } + } else { + // Only write operations that completed synchronously need to free up + // hid_buffer. For reads, copy_transfer_data() handles that process. + if (!direction_in) { + safe_free(transfer_priv->hid_buffer); + } + if (size == 0) { + usbi_err(ctx, "program assertion failed - no data was transferred"); + size = 1; + } + if (size > (size_t)length) { + usbi_err(ctx, "OVERFLOW!"); + r = LIBUSB_ERROR_OVERFLOW; + } + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = size; + } + + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + + return r; +} + +static int hid_abort_transfers(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + current_interface = transfer_priv->interface_number; + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + CancelIo(hid_handle); + + return LIBUSB_SUCCESS; +} + +static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + // Flushing the queues on all interfaces is the best we can achieve + for (current_interface = 0; current_interface < USB_MAXINTERFACES; current_interface++) { + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + if ((hid_handle != 0) && (hid_handle != INVALID_HANDLE_VALUE)) { + HidD_FlushQueue(hid_handle); + } + } + return LIBUSB_SUCCESS; +} + +static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface); + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + + // No endpoint selection with Microsoft's implementation, so we try to flush the + // whole interface. Should be OK for most case scenarios + if (!HidD_FlushQueue(hid_handle)) { + usbi_err(ctx, "Flushing of HID queue failed: %s", windows_error_str(0)); + // Device was probably disconnected + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +// This extra function is only needed for HID +static int hid_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + int r = LIBUSB_TRANSFER_COMPLETED; + uint32_t corrected_size = io_size; + + if (transfer_priv->hid_buffer != NULL) { + // If we have a valid hid_buffer, it means the transfer was async + if (transfer_priv->hid_dest != NULL) { // Data readout + if (corrected_size > 0) { + // First, check for overflow + if (corrected_size > transfer_priv->hid_expected_size) { + usbi_err(ctx, "OVERFLOW!"); + corrected_size = (uint32_t)transfer_priv->hid_expected_size; + r = LIBUSB_TRANSFER_OVERFLOW; + } + + if (transfer_priv->hid_buffer[0] == 0) { + // Discard the 1 byte report ID prefix + corrected_size--; + memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer+1, corrected_size); + } else { + memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer, corrected_size); + } + } + transfer_priv->hid_dest = NULL; + } + // For write, we just need to free the hid buffer + safe_free(transfer_priv->hid_buffer); + } + itransfer->transferred += corrected_size; + return r; +} + + +/* + * Composite API functions + */ +static int composite_init(int sub_api, struct libusb_context *ctx) +{ + return LIBUSB_SUCCESS; +} + +static int composite_exit(int sub_api) +{ + return LIBUSB_SUCCESS; +} + +static int composite_open(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int r = LIBUSB_ERROR_NOT_FOUND; + uint8_t i; + // SUB_API_MAX+1 as the SUB_API_MAX pos is used to indicate availability of HID + bool available[SUB_API_MAX+1] = {0}; + + for (i=0; iusb_interface[i].apib->id) { + case USB_API_WINUSBX: + if (priv->usb_interface[i].sub_api != SUB_API_NOTSET) + available[priv->usb_interface[i].sub_api] = true; + break; + case USB_API_HID: + available[SUB_API_MAX] = true; + break; + default: + break; + } + } + + for (i=0; idev); + uint8_t i; + bool available[SUB_API_MAX]; + + for (i = 0; iusb_interface[i].apib->id == USB_API_WINUSBX) + && (priv->usb_interface[i].sub_api != SUB_API_NOTSET) ) { + available[priv->usb_interface[i].sub_api] = true; + } + } + + for (i=0; idev); + return priv->usb_interface[iface].apib-> + claim_interface(priv->usb_interface[iface].sub_api, dev_handle, iface); +} + +static int composite_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + return priv->usb_interface[iface].apib-> + set_interface_altsetting(priv->usb_interface[iface].sub_api, dev_handle, iface, altsetting); +} + +static int composite_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + return priv->usb_interface[iface].apib-> + release_interface(priv->usb_interface[iface].sub_api, dev_handle, iface); +} + +static int composite_submit_control_transfer(int sub_api, 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 windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int i, pass; + + // Interface shouldn't matter for control, but it does in practice, with Windows' + // restrictions with regards to accessing HID keyboards and mice. Try a 2 pass approach + for (pass = 0; pass < 2; pass++) { + for (i=0; iusb_interface[i].path != NULL) { + if ((pass == 0) && (priv->usb_interface[i].restricted_functionality)) { + usbi_dbg("trying to skip restricted interface #%d (HID keyboard or mouse?)", i); + continue; + } + usbi_dbg("using interface %d", i); + return priv->usb_interface[i].apib->submit_control_transfer(priv->usb_interface[i].sub_api, itransfer); + } + } + } + + usbi_err(ctx, "no libusb supported interfaces to complete request"); + return LIBUSB_ERROR_NOT_FOUND; +} + +static int composite_submit_bulk_transfer(int sub_api, 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 windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int current_interface; + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib-> + submit_bulk_transfer(priv->usb_interface[current_interface].sub_api, itransfer);} + +static int composite_submit_iso_transfer(int sub_api, 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 windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int current_interface; + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib-> + submit_iso_transfer(priv->usb_interface[current_interface].sub_api, itransfer);} + +static int composite_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int current_interface; + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + return priv->usb_interface[current_interface].apib-> + clear_halt(priv->usb_interface[current_interface].sub_api, dev_handle, endpoint);} + +static int composite_abort_control(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + + return priv->usb_interface[transfer_priv->interface_number].apib-> + abort_control(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer);} + +static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + + return priv->usb_interface[transfer_priv->interface_number].apib-> + abort_transfers(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer);} + +static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + int r; + uint8_t i; + bool available[SUB_API_MAX]; + for (i = 0; iusb_interface[i].apib->id == USB_API_WINUSBX) + && (priv->usb_interface[i].sub_api != SUB_API_NOTSET) ) { + available[priv->usb_interface[i].sub_api] = true; + } + } + for (i=0; idev_handle->dev); + + return priv->usb_interface[transfer_priv->interface_number].apib-> + copy_transfer_data(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer, io_size); +} diff --git a/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/windows_usb.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/windows_usb.h new file mode 100644 index 0000000..e9e50ef --- /dev/null +++ b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/os/windows_usb.h @@ -0,0 +1,958 @@ +/* + * 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 + * 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" + +#if defined(_MSC_VER) +// disable /W4 MSVC warnings that are benign +#pragma warning(disable:4127) // conditional expression is constant +#pragma warning(disable:4100) // unreferenced formal parameter +#pragma warning(disable:4214) // bit field types other than int +#pragma warning(disable:4201) // nameless struct/union +#endif + +// Missing from MSVC6 setupapi.h +#if !defined(SPDRP_ADDRESS) +#define SPDRP_ADDRESS 28 +#endif +#if !defined(SPDRP_INSTALL_STATE) +#define SPDRP_INSTALL_STATE 34 +#endif + +// Missing from MinGW +#if !defined(FACILITY_SETUPAPI) +#define FACILITY_SETUPAPI 15 +#endif + +#if defined(__CYGWIN__ ) +#define _stricmp stricmp +// cygwin produces a warning unless these prototypes are defined +extern int _snprintf(char *buffer, size_t count, const char *format, ...); +extern char *_strdup(const char *strSource); +// _beginthreadex is MSVCRT => unavailable for cygwin. Fallback to using CreateThread +#define _beginthreadex(a, b, c, d, e, f) CreateThread(a, b, (LPTHREAD_START_ROUTINE)c, d, e, f) +#endif + +#define MAX_CTRL_BUFFER_LENGTH 4096 +#define MAX_USB_DEVICES 256 +#define MAX_USB_STRING_LENGTH 128 +#define MAX_HID_REPORT_SIZE 1024 +#define MAX_HID_DESCRIPTOR_SIZE 256 +#define MAX_GUID_STRING_LENGTH 40 +#define MAX_PATH_LENGTH 128 +#define MAX_KEY_LENGTH 256 +#define LIST_SEPARATOR ';' +#define HTAB_SIZE (32*1021) + +// Handle code for HID interface that have been claimed ("dibs") +#define INTERFACE_CLAIMED ((HANDLE)(intptr_t)0xD1B5) +// Additional return code for HID operations that completed synchronously +#define LIBUSB_COMPLETED (LIBUSB_SUCCESS + 1) + +// http://msdn.microsoft.com/en-us/library/ff545978.aspx +// http://msdn.microsoft.com/en-us/library/ff545972.aspx +// http://msdn.microsoft.com/en-us/library/ff545982.aspx +#if !defined(GUID_DEVINTERFACE_USB_HOST_CONTROLLER) +const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = { 0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27} }; +#endif +#if !defined(GUID_DEVINTERFACE_USB_DEVICE) +const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED} }; +#endif +#if !defined(GUID_DEVINTERFACE_USB_HUB) +const GUID GUID_DEVINTERFACE_USB_HUB = { 0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8} }; +#endif +#if !defined(GUID_DEVINTERFACE_LIBUSB0_FILTER) +const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = { 0xF9F3FF14, 0xAE21, 0x48A0, {0x8A, 0x25, 0x80, 0x11, 0xA7, 0xA9, 0x31, 0xD9} }; +#endif + + +/* + * Multiple USB API backend support + */ +#define USB_API_UNSUPPORTED 0 +#define USB_API_HUB 1 +#define USB_API_COMPOSITE 2 +#define USB_API_WINUSBX 3 +#define USB_API_HID 4 +#define USB_API_MAX 5 +// The following is used to indicate if the HID or composite extra props have already been set. +#define USB_API_SET (1<os_priv; +} + +static inline void windows_device_priv_init(libusb_device* dev) { + struct windows_device_priv* p = _device_priv(dev); + int i; + p->depth = 0; + p->port = 0; + p->parent_dev = NULL; + p->path = NULL; + p->apib = &usb_api_backend[USB_API_UNSUPPORTED]; + p->sub_api = SUB_API_NOTSET; + p->hid = NULL; + p->active_config = 0; + p->config_descriptor = NULL; + memset(&(p->dev_descriptor), 0, sizeof(USB_DEVICE_DESCRIPTOR)); + for (i=0; iusb_interface[i].path = NULL; + p->usb_interface[i].apib = &usb_api_backend[USB_API_UNSUPPORTED]; + p->usb_interface[i].sub_api = SUB_API_NOTSET; + p->usb_interface[i].nb_endpoints = 0; + p->usb_interface[i].endpoint = NULL; + p->usb_interface[i].restricted_functionality = false; + } +} + +static inline void windows_device_priv_release(libusb_device* dev) { + struct windows_device_priv* p = _device_priv(dev); + int i; + safe_free(p->path); + if ((dev->num_configurations > 0) && (p->config_descriptor != NULL)) { + for (i=0; i < dev->num_configurations; i++) + safe_free(p->config_descriptor[i]); + } + safe_free(p->config_descriptor); + safe_free(p->hid); + for (i=0; iusb_interface[i].path); + safe_free(p->usb_interface[i].endpoint); + } +} + +struct interface_handle_t { + HANDLE dev_handle; // WinUSB needs an extra handle for the file + HANDLE api_handle; // used by the API to communicate with the device +}; + +struct windows_device_handle_priv { + int active_interface; + struct interface_handle_t interface_handle[USB_MAXINTERFACES]; + int autoclaim_count[USB_MAXINTERFACES]; // For auto-release +}; + +static inline struct windows_device_handle_priv *_device_handle_priv( + struct libusb_device_handle *handle) +{ + return (struct windows_device_handle_priv *) handle->os_priv; +} + +// used for async polling functions +struct windows_transfer_priv { + struct winfd pollable_fd; + uint8_t interface_number; + uint8_t *hid_buffer; // 1 byte extended data buffer, required for HID + uint8_t *hid_dest; // transfer buffer destination, required for HID + size_t hid_expected_size; +}; + +// used to match a device driver (including filter drivers) against a supported API +struct driver_lookup { + char list[MAX_KEY_LENGTH+1];// REG_MULTI_SZ list of services (driver) names + const DWORD reg_prop; // SPDRP registry key to use to retreive list + const char* designation; // internal designation (for debug output) +}; + +/* OLE32 dependency */ +DLL_DECLARE_PREFIXED(WINAPI, HRESULT, p, CLSIDFromString, (LPCOLESTR, LPCLSID)); + +/* This call is only available from XP SP2 */ +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, IsWow64Process, (HANDLE, PBOOL)); + +/* SetupAPI dependencies */ +DLL_DECLARE_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (const GUID*, PCSTR, HWND, DWORD)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInfo, (HDEVINFO, DWORD, PSP_DEVINFO_DATA)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInterfaces, (HDEVINFO, PSP_DEVINFO_DATA, + const GUID*, DWORD, PSP_DEVICE_INTERFACE_DATA)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceInterfaceDetailA, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, + PSP_DEVICE_INTERFACE_DETAIL_DATA_A, DWORD, PDWORD, PSP_DEVINFO_DATA)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiDestroyDeviceInfoList, (HDEVINFO)); +DLL_DECLARE_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDevRegKey, (HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceRegistryPropertyA, (HDEVINFO, + PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD)); +DLL_DECLARE_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDeviceInterfaceRegKey, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, DWORD, DWORD)); +DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegQueryValueExW, (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD)); +DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY)); + +/* + * Windows DDK API definitions. Most of it copied from MinGW's includes + */ +typedef DWORD DEVNODE, DEVINST; +typedef DEVNODE *PDEVNODE, *PDEVINST; +typedef DWORD RETURN_TYPE; +typedef RETURN_TYPE CONFIGRET; + +#define CR_SUCCESS 0x00000000 +#define CR_NO_SUCH_DEVNODE 0x0000000D + +#define USB_DEVICE_DESCRIPTOR_TYPE LIBUSB_DT_DEVICE +#define USB_CONFIGURATION_DESCRIPTOR_TYPE LIBUSB_DT_CONFIG +#define USB_STRING_DESCRIPTOR_TYPE LIBUSB_DT_STRING +#define USB_INTERFACE_DESCRIPTOR_TYPE LIBUSB_DT_INTERFACE +#define USB_ENDPOINT_DESCRIPTOR_TYPE LIBUSB_DT_ENDPOINT + +#define USB_REQUEST_GET_STATUS LIBUSB_REQUEST_GET_STATUS +#define USB_REQUEST_CLEAR_FEATURE LIBUSB_REQUEST_CLEAR_FEATURE +#define USB_REQUEST_SET_FEATURE LIBUSB_REQUEST_SET_FEATURE +#define USB_REQUEST_SET_ADDRESS LIBUSB_REQUEST_SET_ADDRESS +#define USB_REQUEST_GET_DESCRIPTOR LIBUSB_REQUEST_GET_DESCRIPTOR +#define USB_REQUEST_SET_DESCRIPTOR LIBUSB_REQUEST_SET_DESCRIPTOR +#define USB_REQUEST_GET_CONFIGURATION LIBUSB_REQUEST_GET_CONFIGURATION +#define USB_REQUEST_SET_CONFIGURATION LIBUSB_REQUEST_SET_CONFIGURATION +#define USB_REQUEST_GET_INTERFACE LIBUSB_REQUEST_GET_INTERFACE +#define USB_REQUEST_SET_INTERFACE LIBUSB_REQUEST_SET_INTERFACE +#define USB_REQUEST_SYNC_FRAME LIBUSB_REQUEST_SYNCH_FRAME + +#define USB_GET_NODE_INFORMATION 258 +#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260 +#define USB_GET_NODE_CONNECTION_NAME 261 +#define USB_GET_HUB_CAPABILITIES 271 +#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX) +#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274 +#endif +#if !defined(USB_GET_HUB_CAPABILITIES_EX) +#define USB_GET_HUB_CAPABILITIES_EX 276 +#endif +#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX_V2) +#define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 279 +#endif + +#ifndef METHOD_BUFFERED +#define METHOD_BUFFERED 0 +#endif +#ifndef FILE_ANY_ACCESS +#define FILE_ANY_ACCESS 0x00000000 +#endif +#ifndef FILE_DEVICE_UNKNOWN +#define FILE_DEVICE_UNKNOWN 0x00000022 +#endif +#ifndef FILE_DEVICE_USB +#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN +#endif + +#ifndef CTL_CODE +#define CTL_CODE(DeviceType, Function, Method, Access)( \ + ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) +#endif + +typedef enum USB_CONNECTION_STATUS { + NoDeviceConnected, + DeviceConnected, + DeviceFailedEnumeration, + DeviceGeneralFailure, + DeviceCausedOvercurrent, + DeviceNotEnoughPower, + DeviceNotEnoughBandwidth, + DeviceHubNestedTooDeeply, + DeviceInLegacyHub +} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS; + +typedef enum USB_HUB_NODE { + UsbHub, + UsbMIParent +} USB_HUB_NODE; + +/* Cfgmgr32.dll interface */ +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG)); + +#define IOCTL_USB_GET_HUB_CAPABILITIES_EX \ + CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_HUB_CAPABILITIES \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_ROOT_HUB_NAME \ + CTL_CODE(FILE_DEVICE_USB, HCD_GET_ROOT_HUB_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_INFORMATION \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_ATTRIBUTES, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_NAME \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) + +// Most of the structures below need to be packed +#pragma pack(push, 1) + +typedef struct USB_INTERFACE_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bInterfaceNumber; + UCHAR bAlternateSetting; + UCHAR bNumEndpoints; + UCHAR bInterfaceClass; + UCHAR bInterfaceSubClass; + UCHAR bInterfaceProtocol; + UCHAR iInterface; +} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR; + +typedef struct USB_CONFIGURATION_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + USHORT wTotalLength; + UCHAR bNumInterfaces; + UCHAR bConfigurationValue; + UCHAR iConfiguration; + UCHAR bmAttributes; + UCHAR MaxPower; +} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR; + +typedef struct USB_CONFIGURATION_DESCRIPTOR_SHORT { + struct { + ULONG ConnectionIndex; + struct { + UCHAR bmRequest; + UCHAR bRequest; + USHORT wValue; + USHORT wIndex; + USHORT wLength; + } SetupPacket; + } req; + USB_CONFIGURATION_DESCRIPTOR data; +} USB_CONFIGURATION_DESCRIPTOR_SHORT; + +typedef struct USB_ENDPOINT_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bEndpointAddress; + UCHAR bmAttributes; + USHORT wMaxPacketSize; + UCHAR bInterval; +} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR; + +typedef struct USB_DESCRIPTOR_REQUEST { + ULONG ConnectionIndex; + struct { + UCHAR bmRequest; + UCHAR bRequest; + USHORT wValue; + USHORT wIndex; + USHORT wLength; + } SetupPacket; +// UCHAR Data[0]; +} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST; + +typedef struct USB_HUB_DESCRIPTOR { + UCHAR bDescriptorLength; + UCHAR bDescriptorType; + UCHAR bNumberOfPorts; + USHORT wHubCharacteristics; + UCHAR bPowerOnToPowerGood; + UCHAR bHubControlCurrent; + UCHAR bRemoveAndPowerMask[64]; +} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR; + +typedef struct USB_ROOT_HUB_NAME { + ULONG ActualLength; + WCHAR RootHubName[1]; +} USB_ROOT_HUB_NAME, *PUSB_ROOT_HUB_NAME; + +typedef struct USB_ROOT_HUB_NAME_FIXED { + ULONG ActualLength; + WCHAR RootHubName[MAX_PATH_LENGTH]; +} USB_ROOT_HUB_NAME_FIXED; + +typedef struct USB_NODE_CONNECTION_NAME { + ULONG ConnectionIndex; + ULONG ActualLength; + WCHAR NodeName[1]; +} USB_NODE_CONNECTION_NAME, *PUSB_NODE_CONNECTION_NAME; + +typedef struct USB_NODE_CONNECTION_NAME_FIXED { + ULONG ConnectionIndex; + ULONG ActualLength; + WCHAR NodeName[MAX_PATH_LENGTH]; +} USB_NODE_CONNECTION_NAME_FIXED; + +typedef struct USB_HUB_NAME_FIXED { + union { + USB_ROOT_HUB_NAME_FIXED root; + USB_NODE_CONNECTION_NAME_FIXED node; + } u; +} USB_HUB_NAME_FIXED; + +typedef struct USB_HUB_INFORMATION { + USB_HUB_DESCRIPTOR HubDescriptor; + BOOLEAN HubIsBusPowered; +} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION; + +typedef struct USB_MI_PARENT_INFORMATION { + ULONG NumberOfInterfaces; +} USB_MI_PARENT_INFORMATION, *PUSB_MI_PARENT_INFORMATION; + +typedef struct USB_NODE_INFORMATION { + USB_HUB_NODE NodeType; + union { + USB_HUB_INFORMATION HubInformation; + USB_MI_PARENT_INFORMATION MiParentInformation; + } u; +} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION; + +typedef struct USB_PIPE_INFO { + USB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + ULONG ScheduleOffset; +} USB_PIPE_INFO, *PUSB_PIPE_INFO; + +typedef struct USB_NODE_CONNECTION_INFORMATION_EX { + ULONG ConnectionIndex; + USB_DEVICE_DESCRIPTOR DeviceDescriptor; + UCHAR CurrentConfigurationValue; + UCHAR Speed; + BOOLEAN DeviceIsHub; + USHORT DeviceAddress; + ULONG NumberOfOpenPipes; + USB_CONNECTION_STATUS ConnectionStatus; +// USB_PIPE_INFO PipeList[0]; +} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX; + +typedef union _USB_PROTOCOLS { + ULONG ul; + struct { + ULONG Usb110:1; + ULONG Usb200:1; + ULONG Usb300:1; + ULONG ReservedMBZ:29; + }; +} USB_PROTOCOLS, *PUSB_PROTOCOLS; + +typedef union _USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS { + ULONG ul; + struct { + ULONG DeviceIsOperatingAtSuperSpeedOrHigher:1; + ULONG DeviceIsSuperSpeedCapableOrHigher:1; + ULONG ReservedMBZ:30; + }; +} USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS; + +typedef struct _USB_NODE_CONNECTION_INFORMATION_EX_V2 { + ULONG ConnectionIndex; + ULONG Length; + USB_PROTOCOLS SupportedUsbProtocols; + USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS Flags; +} USB_NODE_CONNECTION_INFORMATION_EX_V2, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2; + +typedef struct USB_HUB_CAP_FLAGS { + ULONG HubIsHighSpeedCapable:1; + ULONG HubIsHighSpeed:1; + ULONG HubIsMultiTtCapable:1; + ULONG HubIsMultiTt:1; + ULONG HubIsRoot:1; + ULONG HubIsArmedWakeOnConnect:1; + ULONG ReservedMBZ:26; +} USB_HUB_CAP_FLAGS, *PUSB_HUB_CAP_FLAGS; + +typedef struct USB_HUB_CAPABILITIES { + ULONG HubIs2xCapable : 1; +} USB_HUB_CAPABILITIES, *PUSB_HUB_CAPABILITIES; + +typedef struct USB_HUB_CAPABILITIES_EX { + USB_HUB_CAP_FLAGS CapabilityFlags; +} USB_HUB_CAPABILITIES_EX, *PUSB_HUB_CAPABILITIES_EX; + +#pragma pack(pop) + +/* winusb.dll interface */ + +#define SHORT_PACKET_TERMINATE 0x01 +#define AUTO_CLEAR_STALL 0x02 +#define PIPE_TRANSFER_TIMEOUT 0x03 +#define IGNORE_SHORT_PACKETS 0x04 +#define ALLOW_PARTIAL_READS 0x05 +#define AUTO_FLUSH 0x06 +#define RAW_IO 0x07 +#define MAXIMUM_TRANSFER_SIZE 0x08 +#define AUTO_SUSPEND 0x81 +#define SUSPEND_DELAY 0x83 +#define DEVICE_SPEED 0x01 +#define LowSpeed 0x01 +#define FullSpeed 0x02 +#define HighSpeed 0x03 + +typedef enum USBD_PIPE_TYPE { + UsbdPipeTypeControl, + UsbdPipeTypeIsochronous, + UsbdPipeTypeBulk, + UsbdPipeTypeInterrupt +} USBD_PIPE_TYPE; + +typedef struct { + USBD_PIPE_TYPE PipeType; + UCHAR PipeId; + USHORT MaximumPacketSize; + UCHAR Interval; +} WINUSB_PIPE_INFORMATION, *PWINUSB_PIPE_INFORMATION; + +#pragma pack(1) +typedef struct { + UCHAR request_type; + UCHAR request; + USHORT value; + USHORT index; + USHORT length; +} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET; +#pragma pack() + +typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; + +typedef BOOL (WINAPI *WinUsb_AbortPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID +); +typedef BOOL (WINAPI *WinUsb_ControlTransfer_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + WINUSB_SETUP_PACKET SetupPacket, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred, + LPOVERLAPPED Overlapped +); +typedef BOOL (WINAPI *WinUsb_FlushPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID +); +typedef BOOL (WINAPI *WinUsb_Free_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle +); +typedef BOOL (WINAPI *WinUsb_GetAssociatedInterface_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AssociatedInterfaceIndex, + PWINUSB_INTERFACE_HANDLE AssociatedInterfaceHandle +); +typedef BOOL (WINAPI *WinUsb_GetCurrentAlternateSetting_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + PUCHAR AlternateSetting +); +typedef BOOL (WINAPI *WinUsb_GetDescriptor_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR DescriptorType, + UCHAR Index, + USHORT LanguageID, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred +); +typedef BOOL (WINAPI *WinUsb_GetOverlappedResult_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + LPOVERLAPPED lpOverlapped, + LPDWORD lpNumberOfBytesTransferred, + BOOL bWait +); +typedef BOOL (WINAPI *WinUsb_GetPipePolicy_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + ULONG PolicyType, + PULONG ValueLength, + PVOID Value +); +typedef BOOL (WINAPI *WinUsb_GetPowerPolicy_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + ULONG PolicyType, + PULONG ValueLength, + PVOID Value +); +typedef BOOL (WINAPI *WinUsb_Initialize_t)( + HANDLE DeviceHandle, + PWINUSB_INTERFACE_HANDLE InterfaceHandle +); +typedef BOOL (WINAPI *WinUsb_QueryDeviceInformation_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + ULONG InformationType, + PULONG BufferLength, + PVOID Buffer +); +typedef BOOL (WINAPI *WinUsb_QueryInterfaceSettings_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AlternateSettingNumber, + PUSB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor +); +typedef BOOL (WINAPI *WinUsb_QueryPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AlternateInterfaceNumber, + UCHAR PipeIndex, + PWINUSB_PIPE_INFORMATION PipeInformation +); +typedef BOOL (WINAPI *WinUsb_ReadPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred, + LPOVERLAPPED Overlapped +); +typedef BOOL (WINAPI *WinUsb_ResetPipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID +); +typedef BOOL (WINAPI *WinUsb_SetCurrentAlternateSetting_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AlternateSetting +); +typedef BOOL (WINAPI *WinUsb_SetPipePolicy_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + ULONG PolicyType, + ULONG ValueLength, + PVOID Value +); +typedef BOOL (WINAPI *WinUsb_SetPowerPolicy_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + ULONG PolicyType, + ULONG ValueLength, + PVOID Value +); +typedef BOOL (WINAPI *WinUsb_WritePipe_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + PUCHAR Buffer, + ULONG BufferLength, + PULONG LengthTransferred, + LPOVERLAPPED Overlapped +); +typedef BOOL (WINAPI *WinUsb_ResetDevice_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle +); + +/* /!\ These must match the ones from the official libusbk.h */ +typedef enum _KUSB_FNID +{ + KUSB_FNID_Init, + KUSB_FNID_Free, + KUSB_FNID_ClaimInterface, + KUSB_FNID_ReleaseInterface, + KUSB_FNID_SetAltInterface, + KUSB_FNID_GetAltInterface, + KUSB_FNID_GetDescriptor, + KUSB_FNID_ControlTransfer, + KUSB_FNID_SetPowerPolicy, + KUSB_FNID_GetPowerPolicy, + KUSB_FNID_SetConfiguration, + KUSB_FNID_GetConfiguration, + KUSB_FNID_ResetDevice, + KUSB_FNID_Initialize, + KUSB_FNID_SelectInterface, + KUSB_FNID_GetAssociatedInterface, + KUSB_FNID_Clone, + KUSB_FNID_QueryInterfaceSettings, + KUSB_FNID_QueryDeviceInformation, + KUSB_FNID_SetCurrentAlternateSetting, + KUSB_FNID_GetCurrentAlternateSetting, + KUSB_FNID_QueryPipe, + KUSB_FNID_SetPipePolicy, + KUSB_FNID_GetPipePolicy, + KUSB_FNID_ReadPipe, + KUSB_FNID_WritePipe, + KUSB_FNID_ResetPipe, + KUSB_FNID_AbortPipe, + KUSB_FNID_FlushPipe, + KUSB_FNID_IsoReadPipe, + KUSB_FNID_IsoWritePipe, + KUSB_FNID_GetCurrentFrameNumber, + KUSB_FNID_GetOverlappedResult, + KUSB_FNID_GetProperty, + KUSB_FNID_COUNT, +} KUSB_FNID; + +typedef struct _KLIB_VERSION { + INT Major; + INT Minor; + INT Micro; + INT Nano; +} KLIB_VERSION; +typedef KLIB_VERSION* PKLIB_VERSION; + +typedef BOOL (WINAPI *LibK_GetProcAddress_t)( + PVOID* ProcAddress, + ULONG DriverID, + ULONG FunctionID +); + +typedef VOID (WINAPI *LibK_GetVersion_t)( + PKLIB_VERSION Version +); + +struct winusb_interface { + bool initialized; + WinUsb_AbortPipe_t AbortPipe; + WinUsb_ControlTransfer_t ControlTransfer; + WinUsb_FlushPipe_t FlushPipe; + WinUsb_Free_t Free; + WinUsb_GetAssociatedInterface_t GetAssociatedInterface; + WinUsb_GetCurrentAlternateSetting_t GetCurrentAlternateSetting; + WinUsb_GetDescriptor_t GetDescriptor; + WinUsb_GetOverlappedResult_t GetOverlappedResult; + WinUsb_GetPipePolicy_t GetPipePolicy; + WinUsb_GetPowerPolicy_t GetPowerPolicy; + WinUsb_Initialize_t Initialize; + WinUsb_QueryDeviceInformation_t QueryDeviceInformation; + WinUsb_QueryInterfaceSettings_t QueryInterfaceSettings; + WinUsb_QueryPipe_t QueryPipe; + WinUsb_ReadPipe_t ReadPipe; + WinUsb_ResetPipe_t ResetPipe; + WinUsb_SetCurrentAlternateSetting_t SetCurrentAlternateSetting; + WinUsb_SetPipePolicy_t SetPipePolicy; + WinUsb_SetPowerPolicy_t SetPowerPolicy; + WinUsb_WritePipe_t WritePipe; + WinUsb_ResetDevice_t ResetDevice; +}; + +/* hid.dll interface */ + +#define HIDP_STATUS_SUCCESS 0x110000 +typedef void* PHIDP_PREPARSED_DATA; + +#pragma pack(1) +typedef struct { + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; +#pragma pack() + +typedef USHORT USAGE; +typedef struct { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +} HIDP_CAPS, *PHIDP_CAPS; + +typedef enum _HIDP_REPORT_TYPE { + HidP_Input, + HidP_Output, + HidP_Feature +} HIDP_REPORT_TYPE; + +typedef struct _HIDP_VALUE_CAPS { + USAGE UsagePage; + UCHAR ReportID; + BOOLEAN IsAlias; + USHORT BitField; + USHORT LinkCollection; + USAGE LinkUsage; + USAGE LinkUsagePage; + BOOLEAN IsRange; + BOOLEAN IsStringRange; + BOOLEAN IsDesignatorRange; + BOOLEAN IsAbsolute; + BOOLEAN HasNull; + UCHAR Reserved; + USHORT BitSize; + USHORT ReportCount; + USHORT Reserved2[5]; + ULONG UnitsExp; + ULONG Units; + LONG LogicalMin, LogicalMax; + LONG PhysicalMin, PhysicalMax; + union { + struct { + USAGE UsageMin, UsageMax; + USHORT StringMin, StringMax; + USHORT DesignatorMin, DesignatorMax; + USHORT DataIndexMin, DataIndexMax; + } Range; + struct { + USAGE Usage, Reserved1; + USHORT StringIndex, Reserved2; + USHORT DesignatorIndex, Reserved3; + USHORT DataIndex, Reserved4; + } NotRange; + } u; +} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS; + +DLL_DECLARE(WINAPI, BOOL, HidD_GetAttributes, (HANDLE, PHIDD_ATTRIBUTES)); +DLL_DECLARE(WINAPI, VOID, HidD_GetHidGuid, (LPGUID)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetPreparsedData, (HANDLE, PHIDP_PREPARSED_DATA *)); +DLL_DECLARE(WINAPI, BOOL, HidD_FreePreparsedData, (PHIDP_PREPARSED_DATA)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetManufacturerString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetProductString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetSerialNumberString, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, LONG, HidP_GetCaps, (PHIDP_PREPARSED_DATA, PHIDP_CAPS)); +DLL_DECLARE(WINAPI, BOOL, HidD_SetNumInputBuffers, (HANDLE, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_SetFeature, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetFeature, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetPhysicalDescriptor, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_GetInputReport, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_SetOutputReport, (HANDLE, PVOID, ULONG)); +DLL_DECLARE(WINAPI, BOOL, HidD_FlushQueue, (HANDLE)); +DLL_DECLARE(WINAPI, BOOL, HidP_GetValueCaps, (HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PULONG, PHIDP_PREPARSED_DATA)); diff --git a/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/strerror.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/strerror.c new file mode 100644 index 0000000..7f04b4e --- /dev/null +++ b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/strerror.c @@ -0,0 +1,169 @@ +/* + * libusb strerror code + * Copyright © 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 "libusb.h" +#include "libusbi.h" + +#if defined(_MSC_VER) +#define strncasecmp _strnicmp +#endif + +static size_t usbi_locale = 0; + +/** \ingroup misc + * How to add a new \ref libusb_strerror() translation: + *
    + *
  1. Download the latest \c strerror.c from:
    + * https://raw.github.com/libusb/libusb/master/libusb/sterror.c
  2. + *
  3. Open the file in an UTF-8 capable editor
  4. + *
  5. Add the 2 letter ISO 639-1 + * code for your locale at the end of \c usbi_locale_supported[]
    + * Eg. for Chinese, you would add "zh" so that: + * \code... usbi_locale_supported[] = { "en", "nl", "fr" };\endcode + * becomes: + * \code... usbi_locale_supported[] = { "en", "nl", "fr", "zh" };\endcode
  6. + *
  7. Copy the { / * English (en) * / ... } section and add it at the end of \c usbi_localized_errors
    + * Eg. for Chinese, the last section of \c usbi_localized_errors could look like: + * \code + * }, { / * Chinese (zh) * / + * "Success", + * ... + * "Other error", + * } + * };\endcode
  8. + *
  9. Translate each of the English messages from the section you copied into your language
  10. + *
  11. Save the file (in UTF-8 format) and send it to \c libusb-devel\@lists.sourceforge.net
  12. + *
+ */ + +static const char* usbi_locale_supported[] = { "en", "ru" }; +static const char* usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUSB_ERROR_COUNT] = { + { /* English (en) */ + "Success", + "Input/Output Error", + "Invalid parameter", + "Access denied (insufficient permissions)", + "No such device (it may have been disconnected)", + "Entity not found", + "Resource busy", + "Operation timed out", + "Overflow", + "Pipe error", + "System call interrupted (perhaps due to signal)", + "Insufficient memory", + "Operation not supported or unimplemented on this platform", + "Other error", + }, { /* Russian (ru) */ + "Успех", + "Ошибка ввода/вывода", + "Неверный параметр", + "Доступ запрещён (не хватает прав)", + "Устройство отсутствует (возможно, оно было отсоединено)", + "Элемент не найден", + "Ресурс занят", + "Истекло время ожидания операции", + "Переполнение", + "Ошибка канала", + "Системный вызов прерван (возможно, сигналом)", + "Память исчерпана", + "Операция не поддерживается данной платформой", + "Неизвестная ошибка" + } +}; + +/** \ingroup misc + * Set the language, and only the language, not the encoding! used for + * translatable libusb messages. + * + * This takes a locale string in the default setlocale format: lang[-region] + * or lang[_country_region][.codeset]. Only the lang part of the string is + * used, and only 2 letter ISO 639-1 codes are accepted for it, such as "de". + * The optional region, country_region or codeset parts are ignored. This + * means that functions which return translatable strings will NOT honor the + * specified encoding. + * All strings returned are encoded as UTF-8 strings. + * + * If libusb_setlocale() is not called, all messages will be in English. + * + * The following functions return translatable strings: libusb_strerror(). + * Note that the libusb log messages controlled through libusb_set_debug() + * are not translated, they are always in English. + * + * For POSIX UTF-8 environments if you want libusb to follow the standard + * locale settings, call libusb_setlocale(setlocale(LC_MESSAGES, NULL)), + * after your app has done its locale setup. + * + * \param locale locale-string in the form of lang[_country_region][.codeset] + * or lang[-region], where lang is a 2 letter ISO 639-1 code + * \returns LIBUSB_SUCCESS on success + * \returns LIBUSB_ERROR_INVALID_PARAM if the locale doesn't meet the requirements + * \returns LIBUSB_ERROR_NOT_FOUND if the requested language is not supported + * \returns a LIBUSB_ERROR code on other errors + */ + +int API_EXPORTED libusb_setlocale(const char *locale) +{ + size_t i; + + if ( (locale == NULL) || (strlen(locale) < 2) + || ((strlen(locale) > 2) && (locale[2] != '-') && (locale[2] != '_') && (locale[2] != '.')) ) + return LIBUSB_ERROR_INVALID_PARAM; + + for (i=0; i= ARRAYSIZE(usbi_locale_supported)) { + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_locale = i; + + return LIBUSB_SUCCESS; +} + +/** \ingroup misc + * Returns a constant string with a short description of the given error code, + * this description is intended for displaying to the end user and will be in + * the language set by libusb_setlocale(). + * + * The returned string is encoded in UTF-8. + * + * The messages always start with a capital letter and end without any dot. + * The caller must not free() the returned string. + * + * \param errcode the error code whose description is desired + * \returns a short description of the error code in UTF-8 encoding + */ +DEFAULT_VISIBILITY const char* LIBUSB_CALL libusb_strerror(enum libusb_error errcode) +{ + int errcode_index = -errcode; + + if ((errcode_index < 0) || (errcode_index >= LIBUSB_ERROR_COUNT)) { + /* "Other Error", which should always be our last message, is returned */ + errcode_index = LIBUSB_ERROR_COUNT - 1; + } + + return usbi_localized_errors[usbi_locale][errcode_index]; +} diff --git a/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/sync.c b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/sync.c new file mode 100644 index 0000000..d87032d --- /dev/null +++ b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/sync.c @@ -0,0 +1,307 @@ +/* + * Synchronous I/O functions for libusb + * Copyright © 2007-2008 Daniel Drake + * + * 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 "libusbi.h" + +/** + * @defgroup syncio Synchronous device I/O + * + * This page documents libusb's synchronous (blocking) API for USB device I/O. + * This interface is easy to use but has some limitations. More advanced users + * may wish to consider using the \ref asyncio "asynchronous I/O API" instead. + */ + +static void LIBUSB_CALL sync_transfer_cb(struct libusb_transfer *transfer) +{ + int *completed = transfer->user_data; + *completed = 1; + usbi_dbg("actual_length=%d", transfer->actual_length); + /* caller interprets result and frees transfer */ +} + +static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) +{ + int r, *completed = transfer->user_data; + struct libusb_context *ctx = HANDLE_CTX(transfer->dev_handle); + + while (!*completed) { + r = libusb_handle_events_completed(ctx, completed); + if (r < 0) { + if (r == LIBUSB_ERROR_INTERRUPTED) + continue; + usbi_err(ctx, "libusb_handle_events failed: %s, cancelling transfer and retrying", + libusb_error_name(r)); + libusb_cancel_transfer(transfer); + continue; + } + } +} + +/** \ingroup syncio + * Perform a USB control transfer. + * + * The direction of the transfer is inferred from the bmRequestType field of + * the setup packet. + * + * The wValue, wIndex and wLength fields values should be given in host-endian + * byte order. + * + * \param dev_handle a handle for the device to communicate with + * \param bmRequestType the request type field for the setup packet + * \param bRequest the request field for the setup packet + * \param wValue the value field for the setup packet + * \param wIndex the index field for the setup packet + * \param data a suitably-sized data buffer for either input or output + * (depending on direction bits within bmRequestType) + * \param wLength the length field for the setup packet. The data buffer should + * be at least this size. + * \param timeout timeout (in millseconds) that this function should wait + * before giving up due to no response being received. For an unlimited + * timeout, use value 0. + * \returns on success, the number of bytes actually transferred + * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out + * \returns LIBUSB_ERROR_PIPE if the control request was not supported by the + * device + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failures + */ +int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout) +{ + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + unsigned char *buffer; + int completed = 0; + int r; + + if (!transfer) + return LIBUSB_ERROR_NO_MEM; + + buffer = (unsigned char*) malloc(LIBUSB_CONTROL_SETUP_SIZE + wLength); + if (!buffer) { + libusb_free_transfer(transfer); + return LIBUSB_ERROR_NO_MEM; + } + + libusb_fill_control_setup(buffer, bmRequestType, bRequest, wValue, wIndex, + wLength); + if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) + memcpy(buffer + LIBUSB_CONTROL_SETUP_SIZE, data, wLength); + + libusb_fill_control_transfer(transfer, dev_handle, buffer, + sync_transfer_cb, &completed, timeout); + transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER; + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + return r; + } + + sync_transfer_wait_for_completion(transfer); + + if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) + memcpy(data, libusb_control_transfer_get_data(transfer), + transfer->actual_length); + + switch (transfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + r = transfer->actual_length; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + r = LIBUSB_ERROR_TIMEOUT; + break; + case LIBUSB_TRANSFER_STALL: + r = LIBUSB_ERROR_PIPE; + break; + case LIBUSB_TRANSFER_NO_DEVICE: + r = LIBUSB_ERROR_NO_DEVICE; + break; + case LIBUSB_TRANSFER_OVERFLOW: + r = LIBUSB_ERROR_OVERFLOW; + break; + case LIBUSB_TRANSFER_ERROR: + case LIBUSB_TRANSFER_CANCELLED: + r = LIBUSB_ERROR_IO; + break; + default: + usbi_warn(HANDLE_CTX(dev_handle), + "unrecognised status code %d", transfer->status); + r = LIBUSB_ERROR_OTHER; + } + + libusb_free_transfer(transfer); + return r; +} + +static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + int *transferred, unsigned int timeout, unsigned char type) +{ + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + int completed = 0; + int r; + + if (!transfer) + return LIBUSB_ERROR_NO_MEM; + + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length, + sync_transfer_cb, &completed, timeout); + transfer->type = type; + + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + return r; + } + + sync_transfer_wait_for_completion(transfer); + + *transferred = transfer->actual_length; + switch (transfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + r = 0; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + r = LIBUSB_ERROR_TIMEOUT; + break; + case LIBUSB_TRANSFER_STALL: + r = LIBUSB_ERROR_PIPE; + break; + case LIBUSB_TRANSFER_OVERFLOW: + r = LIBUSB_ERROR_OVERFLOW; + break; + case LIBUSB_TRANSFER_NO_DEVICE: + r = LIBUSB_ERROR_NO_DEVICE; + break; + case LIBUSB_TRANSFER_ERROR: + case LIBUSB_TRANSFER_CANCELLED: + r = LIBUSB_ERROR_IO; + break; + default: + usbi_warn(HANDLE_CTX(dev_handle), + "unrecognised status code %d", transfer->status); + r = LIBUSB_ERROR_OTHER; + } + + libusb_free_transfer(transfer); + return r; +} + +/** \ingroup syncio + * Perform a USB bulk transfer. The direction of the transfer is inferred from + * the direction bits of the endpoint address. + * + * For bulk reads, the length field indicates the maximum length of + * data you are expecting to receive. If less data arrives than expected, + * this function will return that data, so be sure to check the + * transferred output parameter. + * + * You should also check the transferred parameter for bulk writes. + * Not all of the data may have been written. + * + * Also check transferred when dealing with a timeout error code. + * libusb may have to split your transfer into a number of chunks to satisfy + * underlying O/S requirements, meaning that the timeout may expire after + * the first few chunks have completed. libusb is careful not to lose any data + * that may have been transferred; do not assume that timeout conditions + * indicate a complete lack of I/O. + * + * \param dev_handle a handle for the device to communicate with + * \param endpoint the address of a valid endpoint to communicate with + * \param data a suitably-sized data buffer for either input or output + * (depending on endpoint) + * \param length for bulk writes, the number of bytes from data to be sent. for + * bulk reads, the maximum number of bytes to receive into the data buffer. + * \param transferred output location for the number of bytes actually + * transferred. + * \param timeout timeout (in millseconds) that this function should wait + * before giving up due to no response being received. For an unlimited + * timeout, use value 0. + * + * \returns 0 on success (and populates transferred) + * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates + * transferred) + * \returns LIBUSB_ERROR_PIPE if the endpoint halted + * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see + * \ref packetoverflow + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failures + */ +int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, int *transferred, + unsigned int timeout) +{ + return do_sync_bulk_transfer(dev_handle, endpoint, data, length, + transferred, timeout, LIBUSB_TRANSFER_TYPE_BULK); +} + +/** \ingroup syncio + * Perform a USB interrupt transfer. The direction of the transfer is inferred + * from the direction bits of the endpoint address. + * + * For interrupt reads, the length field indicates the maximum length + * of data you are expecting to receive. If less data arrives than expected, + * this function will return that data, so be sure to check the + * transferred output parameter. + * + * You should also check the transferred parameter for interrupt + * writes. Not all of the data may have been written. + * + * Also check transferred when dealing with a timeout error code. + * libusb may have to split your transfer into a number of chunks to satisfy + * underlying O/S requirements, meaning that the timeout may expire after + * the first few chunks have completed. libusb is careful not to lose any data + * that may have been transferred; do not assume that timeout conditions + * indicate a complete lack of I/O. + * + * The default endpoint bInterval value is used as the polling interval. + * + * \param dev_handle a handle for the device to communicate with + * \param endpoint the address of a valid endpoint to communicate with + * \param data a suitably-sized data buffer for either input or output + * (depending on endpoint) + * \param length for bulk writes, the number of bytes from data to be sent. for + * bulk reads, the maximum number of bytes to receive into the data buffer. + * \param transferred output location for the number of bytes actually + * transferred. + * \param timeout timeout (in millseconds) that this function should wait + * before giving up due to no response being received. For an unlimited + * timeout, use value 0. + * + * \returns 0 on success (and populates transferred) + * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out + * \returns LIBUSB_ERROR_PIPE if the endpoint halted + * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see + * \ref packetoverflow + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other error + */ +int API_EXPORTED libusb_interrupt_transfer( + struct libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *data, int length, int *transferred, unsigned int timeout) +{ + return do_sync_bulk_transfer(dev_handle, endpoint, data, length, + transferred, timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT); +} diff --git a/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/version.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/version.h new file mode 100644 index 0000000..3305765 --- /dev/null +++ b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/version.h @@ -0,0 +1,18 @@ +/* This file is parsed by m4 and windres and RC.EXE so please keep it simple. */ +#include "version_nano.h" +#ifndef LIBUSB_MAJOR +#define LIBUSB_MAJOR 1 +#endif +#ifndef LIBUSB_MINOR +#define LIBUSB_MINOR 0 +#endif +#ifndef LIBUSB_MICRO +#define LIBUSB_MICRO 19 +#endif +#ifndef LIBUSB_NANO +#define LIBUSB_NANO 0 +#endif +/* LIBUSB_RC is the release candidate suffix. Should normally be empty. */ +#ifndef LIBUSB_RC +#define LIBUSB_RC "" +#endif diff --git a/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/version_nano.h b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/version_nano.h new file mode 100644 index 0000000..8c4063a --- /dev/null +++ b/x502api-1.1.34/devs/e502/libusb-1.0/libusb-1.0/version_nano.h @@ -0,0 +1 @@ +#define LIBUSB_NANO 10903 diff --git a/x502api-1.1.34/devs/e502/libusb-1.0/libusb.cmake b/x502api-1.1.34/devs/e502/libusb-1.0/libusb.cmake new file mode 100644 index 0000000..6f76506 --- /dev/null +++ b/x502api-1.1.34/devs/e502/libusb-1.0/libusb.cmake @@ -0,0 +1,95 @@ +# Файл для включения в проект на CMAKE. +# После включения будут установлены следующие перменные: +# LIBUSB_HEADERS - используемые заголовочные файлы +# LIBUSB_SOURCES - используемые файлы исходных кодов +# LIBUSB_INCLUDE_DIRS - директории включения заголовков +# LIBUSB_LIBS - используемые библиотеки + +cmake_policy(PUSH) + +cmake_minimum_required(VERSION 2.8.12) + + + + +set(LIBUSB_DIR ${CMAKE_CURRENT_LIST_DIR}) +set(LIBUSB_DIR_SRC ${LIBUSB_DIR}/libusb-1.0) + +set(LIBUSB_INCLUDE_DIRS ${LIBUSB_DIR}) + +set(LIBUSB_SOURCES + ${LIBUSB_DIR_SRC}/core.c + ${LIBUSB_DIR_SRC}/descriptor.c + ${LIBUSB_DIR_SRC}/hotplug.c + ${LIBUSB_DIR_SRC}/io.c + ${LIBUSB_DIR_SRC}/strerror.c + ${LIBUSB_DIR_SRC}/sync.c +) + +set(LIBUSB_HEADERS + ${LIBUSB_DIR_SRC}/libusb.h + ${LIBUSB_DIR_SRC}/libusbi.h + ${LIBUSB_DIR_SRC}/hotplug.h + ${LIBUSB_DIR_SRC}/version.h + ${LIBUSB_DIR_SRC}/version_nano.h +) + + +if(WIN32) + 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) + set(SOURCES ${SOURCES} + ${LIBUSB_DIR_SRC}/os/poll_windows.c + ${LIBUSB_DIR_SRC}/os/threads_windows.c + ${LIBUSB_DIR_SRC}/os/windows_usb.c + ) + set(HEADERS ${HEADERS} + ${LIBUSB_DIR_SRC}/os/poll_windows.h + ${LIBUSB_DIR_SRC}/os/threads_windows.h + ${LIBUSB_DIR_SRC}/os/windows_common.h + ) +else(WIN32) + message(FATAL_ERROR "unsupported os") +endif(WIN32) + + + +if(MSVC) + set(LIBUSB_INCLUDE_DIRS ${LIBUSB_INCLUDE_DIRS} ${LIBUSB_DIR_SRC}/msvc) + + #В зависимости от версии msvc файлы errno.h,stdint.h,inttypes.h могут присутсвовать + #или отсутствовать. При этом файлы из libusb могут конфликтовать с файлами из msvc. + #Поэтому проверяем каждый из этих файлов, и прописываем до него путь в поиске include, + #только в случае, если он не найден + include(CheckIncludeFile) + check_include_file(errno.h HAVE_ERRNO) + if (NOT ${HAVE_ERRNO}) + set(LIBUSB_INCLUDE_DIRS ${LIBUSB_INCLUDE_DIRS} ${LIBUSB_DIR_SRC}/errno) + endif (NOT ${HAVE_ERRNO}) + + check_include_file(stdint.h HAVE_STDINT) + if (NOT ${HAVE_STDINT}) + set(LIBUSB_INCLUDE_DIRS ${LIBUSB_INCLUDE_DIRS} ${LIBUSB_DIR_SRC}/stdint) + endif (NOT ${HAVE_STDINT}) + + check_include_file(inttypes.h HAVE_INTTYPES) + if (NOT ${HAVE_INTTYPES}) + set(LIBUSB_INCLUDE_DIRS ${LIBUSB_INCLUDE_DIRS} ${LIBUSB_DIR_SRC}/inttypes) + endif (NOT ${HAVE_INTTYPES}) + + set(LIBUSB_HEADERS ${LIBUSB_HEADERS} + ${LIBUSB_DIR_SRC}/msvc/config.h + ${LIBUSB_DIR_SRC}/msvc/missing.h + ${LIBUSB_DIR_SRC}/msvc/errno/errno.h + ${LIBUSB_DIR_SRC}/msvc/inttypes/inttypes.h + ${LIBUSB_DIR_SRC}/msvc/stdint/stdint.h + ) +else(MSVC) + message(FATAL_ERROR "unsupported compiler") +endif(MSVC) + + +cmake_policy(POP) diff --git a/x502api-1.1.34/devs/e502/pas/e502api.pas b/x502api-1.1.34/devs/e502/pas/e502api.pas new file mode 100644 index 0000000..cb4bdf2 --- /dev/null +++ b/x502api-1.1.34/devs/e502/pas/e502api.pas @@ -0,0 +1,294 @@ +unit e502api; +interface +uses Windows, SysUtils, x502api; + + const + { } + E502_ETH_SVC_EVENT_NONE = 0; // + E502_ETH_SVC_EVENT_ADD = 1; // + E502_ETH_SVC_EVENT_REMOVE = 2; // + E502_ETH_SVC_EVENT_CHANGED = 3; // + + // . + type st_e502_eth_config_state = record + end; + type t_e502_eth_config_hnd = ^st_e502_eth_config_state; + + type t_e502_mac_addr = array[0..X502_MAC_ADDR_SIZE-1] of byte; + + // + type st_e502_eth_svc_browse_context = record + end; + type t_e502_eth_svc_browse_hnd = ^st_e502_eth_svc_browse_context; + // + type st_e502_eth_svc_record = record + end; + type t_e502_eth_svc_record_hnd = ^st_e502_eth_svc_record; + + // E502, USB + function E502_UsbGetSerialList(out serials: array of string; flags: LongWord; out devcnt: LongWord) : LongInt; overload; + function E502_UsbGetSerialList(out serials: array of string; flags: LongWord) : LongInt; overload; + // E502, USB, . + function E502_OpenUsb(hnd: t_x502_hnd; serial: string): LongInt; stdcall; + // E502 IP- + function E502_OpenByIpAddr(hnd : t_x502_hnd; ip_addr: LongWord; flags : LongWord; tout: LongWord) : LongInt; stdcall; + + // , E502 + function E502_UsbGetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord; out devcnt: LongWord) : LongInt; overload; + function E502_UsbGetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord) : LongInt; overload; + // IP- + function E502_MakeDevRecordByIpAddr(var devrec: t_x502_devrec; ip_addr: LongWord; flags : LongWord; tout: LongWord) : LongInt; stdcall; + // TCP- + function E502_EthDevRecordSetCmdPort(var devrec: t_x502_devrec; cmd_port: Word) : LongInt; stdcall; + // TCP- + function E502_EthDevRecordSetDataPort(var devrec: t_x502_devrec; data_port: Word) : LongInt; stdcall; + + // + function E502_MakeDevRecordByEthSvc(var devrec: t_x502_devrec; svc : t_e502_eth_svc_record_hnd; flags : LongWord; tout: LongWord) : LongInt; stdcall; + + + // IP- + function E502_GetIpAddr(hnd: t_x502_hnd; out ip_addr : LongWord) : LongInt; stdcall; + + + + // . + function E502_EthConfigCreate() : t_e502_eth_config_hnd; stdcall; + // . + function E502_EthConfigFree(cfg: t_e502_eth_config_hnd): LongInt; stdcall; + // + function E502_EthConfigRead(hnd: t_x502_hnd; cfg: t_e502_eth_config_hnd): LongInt; stdcall; + // + function E502_EthConfigWrite(hnd: t_x502_hnd; cfg: t_e502_eth_config_hnd; passwd : string): LongInt; stdcall; + // + function E502_EthConfigCopy(src_cfg: t_e502_eth_config_hnd; dst_cfg: t_e502_eth_config_hnd): LongInt; stdcall; + // , Ethernet + function E502_EthConfigGetEnabled(cfg: t_e502_eth_config_hnd; out en : LongBool): LongInt; stdcall; + // Ethernet + function E502_EthConfigSetEnabled(cfg: t_e502_eth_config_hnd; en : LongBool): LongInt; stdcall; + // , IP + function E502_EthConfigGetAutoIPEnabled(cfg: t_e502_eth_config_hnd; out en: LongBool): LongInt; stdcall; + // IP + function E502_EthConfigSetAutoIPEnabled(cfg: t_e502_eth_config_hnd; en: LongBool): LongInt; stdcall; + // , MAC- + function E502_EthConfigGetUserMACEnabled(cfg: t_e502_eth_config_hnd; out en: LongBool): LongInt; stdcall; + // , MAC- + function E502_EthConfigSetUserMACEnabled(cfg: t_e502_eth_config_hnd; en : LongBool): LongInt; stdcall; + // IP- + function E502_EthConfigGetIPv4Addr(cfg: t_e502_eth_config_hnd; out ip_addr : LongWord): LongInt; stdcall; + // IP- + function E502_EthConfigSetIPv4Addr(cfg: t_e502_eth_config_hnd; ip_addr: LongWord): LongInt; stdcall; + // + function E502_EthConfigGetIPv4Mask(cfg: t_e502_eth_config_hnd; out mask : LongWord): LongInt; stdcall; + // + function E502_EthConfigSetIPv4Mask(cfg: t_e502_eth_config_hnd; mask : LongWord): LongInt; stdcall; + // + function E502_EthConfigGetIPv4Gate(cfg: t_e502_eth_config_hnd; out gate: LongWord): LongInt; stdcall; + // + function E502_EthConfigSetIPv4Gate(cfg: t_e502_eth_config_hnd; gate: LongWord): LongInt; stdcall; + // MAC- + function E502_EthConfigGetUserMac(cfg: t_e502_eth_config_hnd; mac : t_e502_mac_addr): LongInt; stdcall; + // MAC- + function E502_EthConfigSetUserMac(cfg: t_e502_eth_config_hnd; mac: t_e502_mac_addr): LongInt; stdcall; + // MAC- + function E502_EthConfigGetFactoryMac(cfg: t_e502_eth_config_hnd; mac : t_e502_mac_addr): LongInt; stdcall; + // + function E502_EthConfigGetInstanceName(cfg: t_e502_eth_config_hnd; out name: string): LongInt; stdcall; + // + function E502_EthConfigSetInstanceName(cfg: t_e502_eth_config_hnd; const name: string): LongInt; stdcall; + // + function E502_EthConfigSetNewPassword(cfg: t_e502_eth_config_hnd; const new_passwd: string): LongInt; stdcall; + + // E502 + function E502_SwitchToBootloader(hnd: t_x502_hnd): LongInt; stdcall; + // + function E502_ReloadFPGA(hnd: t_x502_hnd): LongInt; stdcall; + // Cortex-M4. + function E502_CortexExecCmd(hnd: t_x502_hnd; cmd_code: LongWord; par: LongWord; + const snd_data : array of byte; snd_size : LongWord; + rcv_data : array of byte; rcv_size : LongWord; + tout: LongWord; out recvd_size: LongWord): LongInt; stdcall; + + // + function E502_EthSvcBrowseStart(out context : t_e502_eth_svc_browse_hnd; flags : LongWord): LongInt; stdcall; + // + function E502_EthSvcBrowseGetEvent(context : t_e502_eth_svc_browse_hnd; out svc: t_e502_eth_svc_record_hnd; out event: LongWord; out flags : LongWord; tout : LongWord): LongInt; stdcall; + // + function E502_EthSvcBrowseStop(context : t_e502_eth_svc_browse_hnd): LongInt; stdcall; + // + function E502_EthSvcRecordFree(svc : t_e502_eth_svc_record_hnd): LongInt; stdcall; + // + function E502_EthSvcRecordGetInstanceName(svc : t_e502_eth_svc_record_hnd; out name: string): LongInt; stdcall; + // + function E502_EthSvcRecordGetDevSerial(svc : t_e502_eth_svc_record_hnd; out serial : string): LongInt; stdcall; + // IP + function E502_EthSvcRecordResolveIPv4Addr(svc : t_e502_eth_svc_record_hnd; out addr :LongWord; tout : LongWord): LongInt; stdcall; + // , + function E502_EthSvcRecordIsSameInstance(svc1 : t_e502_eth_svc_record_hnd; svc2 : t_e502_eth_svc_record_hnd): LongInt; stdcall; + +implementation + + function _get_serials( ser_arr: p_x502_serial_array; size:LongWord; + flags:LongWord; out devcnt: LongWord) : LongInt; + stdcall; external 'e502api.dll' name 'E502_UsbGetSerialList'; + function _get_dev_records_list(out list; size:LongWord; + flags : LongWord; out devcnt: LongWord) : LongInt; + stdcall; external 'e502api.dll' name 'E502_UsbGetDevRecordsList'; + + function _open_usb(hnd: t_x502_hnd; serial: PAnsiChar) : LongInt; stdcall; external 'e502api.dll' name 'E502_OpenUsb'; + function E502_OpenByIpAddr(hnd : t_x502_hnd; ip_addr: LongWord; flags : LongWord; tout: LongWord) : LongInt; stdcall; external 'e502api.dll'; + function E502_MakeDevRecordByIpAddr(var devrec: t_x502_devrec; ip_addr: LongWord; flags : LongWord; tout: LongWord) : LongInt; stdcall; external 'e502api.dll'; + function E502_EthDevRecordSetCmdPort(var devrec: t_x502_devrec; cmd_port: Word) : LongInt; stdcall; external 'e502api.dll'; + function E502_EthDevRecordSetDataPort(var devrec: t_x502_devrec; data_port: Word) : LongInt; stdcall; external 'e502api.dll'; + function E502_MakeDevRecordByEthSvc(var devrec: t_x502_devrec; svc : t_e502_eth_svc_record_hnd; flags : LongWord; tout: LongWord) : LongInt; stdcall; external 'e502api.dll'; + function E502_GetIpAddr(hnd: t_x502_hnd; out ip_addr : LongWord) : LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigCreate() : t_e502_eth_config_hnd; stdcall; external 'e502api.dll'; + function E502_EthConfigFree(cfg: t_e502_eth_config_hnd): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigRead(hnd: t_x502_hnd; cfg: t_e502_eth_config_hnd): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigWrite(hnd: t_x502_hnd; cfg: t_e502_eth_config_hnd; passwd : string): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigCopy(src_cfg: t_e502_eth_config_hnd; dst_cfg: t_e502_eth_config_hnd): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetEnabled(cfg: t_e502_eth_config_hnd; out en : LongBool): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigSetEnabled(cfg: t_e502_eth_config_hnd; en : LongBool): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetAutoIPEnabled(cfg: t_e502_eth_config_hnd; out en: LongBool): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigSetAutoIPEnabled(cfg: t_e502_eth_config_hnd; en: LongBool): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetUserMACEnabled(cfg: t_e502_eth_config_hnd; out en: LongBool): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigSetUserMACEnabled(cfg: t_e502_eth_config_hnd; en : LongBool): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetIPv4Addr(cfg: t_e502_eth_config_hnd; out ip_addr : LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigSetIPv4Addr(cfg: t_e502_eth_config_hnd; ip_addr: LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetIPv4Mask(cfg: t_e502_eth_config_hnd; out mask : LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigSetIPv4Mask(cfg: t_e502_eth_config_hnd; mask : LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetIPv4Gate(cfg: t_e502_eth_config_hnd; out gate: LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigSetIPv4Gate(cfg: t_e502_eth_config_hnd; gate: LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetUserMac(cfg: t_e502_eth_config_hnd; mac : t_e502_mac_addr): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigSetUserMac(cfg: t_e502_eth_config_hnd; mac: t_e502_mac_addr): LongInt; stdcall; external 'e502api.dll'; + function E502_EthConfigGetFactoryMac(cfg: t_e502_eth_config_hnd; mac : t_e502_mac_addr): LongInt; stdcall; external 'e502api.dll'; + + function _eth_config_get_instance_name(cfg: t_e502_eth_config_hnd; name: PAnsiChar): LongInt; stdcall; external 'e502api.dll' name 'E502_EthConfigGetInstanceName'; + function _eth_config_set_instance_name(cfg: t_e502_eth_config_hnd; name: PAnsiChar): LongInt; stdcall; external 'e502api.dll' name 'E502_EthConfigSetInstanceName'; + function _eth_config_set_new_password(cfg: t_e502_eth_config_hnd; new_passwd: PAnsiChar): LongInt; stdcall; external 'e502api.dll' name 'E502_EthConfigSetNewPassword'; + + function E502_SwitchToBootloader(hnd: t_x502_hnd): LongInt; stdcall; external 'e502api.dll'; + function E502_ReloadFPGA(hnd: t_x502_hnd): LongInt; stdcall; external 'e502api.dll'; + function _cortex_exec_cmd(hnd: t_x502_hnd; cmd_code: LongWord; par: LongWord; + const snd_data; snd_size : LongWord; + out rcv_data; rcv_size : LongWord; + tout: LongWord; out recvd_size: LongWord): LongInt; stdcall; external 'e502api.dll' name 'E502_CortexExecCmd'; + + function E502_EthSvcBrowseStart(out context : t_e502_eth_svc_browse_hnd; flags : LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthSvcBrowseGetEvent(context : t_e502_eth_svc_browse_hnd; out svc: t_e502_eth_svc_record_hnd; out event: LongWord; out flags : LongWord; tout : LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthSvcBrowseStop(context : t_e502_eth_svc_browse_hnd): LongInt; stdcall; external 'e502api.dll'; + function E502_EthSvcRecordFree(svc : t_e502_eth_svc_record_hnd): LongInt; stdcall; external 'e502api.dll'; + function _eth_svc_record_get_instance_name(svc : t_e502_eth_svc_record_hnd; name: PAnsiChar): LongInt; stdcall; external 'e502api.dll' name 'E502_EthSvcRecordGetInstanceName'; + function _eth_svc_record_get_dev_serial(svc : t_e502_eth_svc_record_hnd; serial : PAnsiChar): LongInt; stdcall; external 'e502api.dll' name 'E502_EthSvcRecordGetDevSerial'; + function E502_EthSvcRecordResolveIPv4Addr(svc : t_e502_eth_svc_record_hnd; out addr :LongWord; tout : LongWord): LongInt; stdcall; external 'e502api.dll'; + function E502_EthSvcRecordIsSameInstance(svc1 : t_e502_eth_svc_record_hnd; svc2 : t_e502_eth_svc_record_hnd): LongInt; stdcall; external 'e502api.dll'; + + + + function E502_UsbGetSerialList(out serials: array of string; flags: LongWord; out devcnt: LongWord) : LongInt; overload; + var + ser_arr : p_x502_serial_array; + res, i : LongInt; + begin + if (Length(serials) > 0) then + begin + ser_arr:=GetMemory(Length(serials)*X502_SERIAL_SIZE); + // + res := _get_serials(ser_arr, Length(serials), flags, devcnt); + if res >= 0 then + begin + // + for i:=0 to res-1 do + serials[i] := string(ser_arr[i]); + end; + // , + FreeMemory(ser_arr); + end + else + begin + res:= _get_serials(nil, 0, flags, devcnt); + end; + E502_UsbGetSerialList:=res; + end; + + function E502_UsbGetSerialList(out serials: array of string; flags: LongWord) : LongInt; overload; + begin + E502_UsbGetSerialList:= E502_UsbGetSerialList(serials, flags, PCardinal(nil)^); + end; + + function E502_UsbGetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord; out devcnt: LongWord) : LongInt; overload; + begin + E502_UsbGetDevRecordsList := _get_dev_records_list(list, Length(list), flags, devcnt); + end; + function E502_UsbGetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord) : LongInt; overload; + begin + E502_UsbGetDevRecordsList:= E502_UsbGetDevRecordsList(list, flags, PCardinal(nil)^); + end; + + function E502_OpenUsb(hnd: t_x502_hnd; serial: string) : LongInt; + begin + E502_OpenUsb:=_open_usb(hnd, PAnsiChar(AnsiString(serial))); + end; + + function E502_EthConfigGetInstanceName(cfg: t_e502_eth_config_hnd; out name: string): LongInt; stdcall; + var + strptr: PAnsiChar; + res: LongInt; + begin + strptr:=GetMemory(X502_INSTANCE_NAME_SIZE); + res:=_eth_config_get_instance_name(cfg, strptr); + if res = X502_ERR_OK then + name:=string(Utf8Decode(strptr)); + FreeMemory(strptr); + E502_EthConfigGetInstanceName:= res; + end; + + + function E502_EthConfigSetInstanceName(cfg: t_e502_eth_config_hnd; const name: string): LongInt; stdcall; + begin + E502_EthConfigSetInstanceName:=_eth_config_set_instance_name(cfg, PAnsiChar(Utf8Encode(AnsiString(name)))); + end; + + function E502_EthConfigSetNewPassword(cfg: t_e502_eth_config_hnd; const new_passwd: string): LongInt; stdcall; + begin + E502_EthConfigSetNewPassword:=_eth_config_set_new_password(cfg, PAnsiChar(AnsiString(new_passwd))); + end; + + function E502_CortexExecCmd(hnd: t_x502_hnd; cmd_code: LongWord; par: LongWord; + const snd_data : array of byte; snd_size : LongWord; + rcv_data : array of byte; rcv_size : LongWord; + tout: LongWord; out recvd_size: LongWord): LongInt; stdcall; + begin + if (LongWord(Length(snd_data)) < snd_size) or + (LongWord(Length(rcv_data)) < rcv_size) then + E502_CortexExecCmd := X502_ERR_INSUFFICIENT_ARRAY_SIZE + else + E502_CortexExecCmd:=_cortex_exec_cmd(hnd, cmd_code, par, snd_data, snd_size, rcv_data, rcv_size, tout, recvd_size); + end; + + function E502_EthSvcRecordGetInstanceName(svc : t_e502_eth_svc_record_hnd; out name: string): LongInt; stdcall; + var + strptr: PAnsiChar; + res: LongInt; + begin + strptr:=GetMemory(X502_INSTANCE_NAME_SIZE); + res:=_eth_svc_record_get_instance_name(svc, strptr); + if res = X502_ERR_OK then + name:=string(Utf8Decode(strptr)); + FreeMemory(strptr); + E502_EthSvcRecordGetInstanceName:= res; + end; + + function E502_EthSvcRecordGetDevSerial(svc : t_e502_eth_svc_record_hnd; out serial : string): LongInt; stdcall; + var + strptr: PAnsiChar; + res: LongInt; + begin + strptr:=GetMemory(X502_SERIAL_SIZE); + res:=_eth_svc_record_get_dev_serial(svc, strptr); + if res = X502_ERR_OK then + serial:=string(strptr); + FreeMemory(strptr); + E502_EthSvcRecordGetDevSerial:= res; + end; +end. diff --git a/x502api-1.1.34/devs/l502/CMakeLists.txt b/x502api-1.1.34/devs/l502/CMakeLists.txt new file mode 100644 index 0000000..56585fd --- /dev/null +++ b/x502api-1.1.34/devs/l502/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 2.8.12) + +project(l502api C) +set(PROJECT_VARNAME_PREFIX L502API) + + + +include(${LTIMER_DIR}/ltimer.cmake) + +set(SOURCES + l502api.c + l502api_compat.c + l502api_eeprom.c + l502api_bf.c + ${LTIMER_SOURCES} + ) + +set(SETUP_HEADERS + l502api.h + l502api_compat.h) + +set(HEADERS + l502api_private.h + lpcie_ioctls.h + ${LTIMER_HEADERS}) + +set(LIBS + x502api + ${LTIMER_LIBS}) + +set(L502API_COMPILE_DEFINITIONS ${L502API_COMPILE_DEFINITIONS} ${LTIMER_DEFINITIONS}) + +if(WIN32) + #В Windows используем setupapi для нахождения устройства + set(LIBS ${LIBS} setupapi) + set(SOURCES ${SOURCES} win/l502_spec.c) +elseif(UNIX) + set(SOURCES ${SOURCES} linux/l502_spec.c) +endif(WIN32) + +include(${X502_LIBS_CMAKE_FILE}) + + + + + + + diff --git a/x502api-1.1.34/devs/l502/l502_fpga_regs.h b/x502api-1.1.34/devs/l502/l502_fpga_regs.h new file mode 100644 index 0000000..4a73c95 --- /dev/null +++ b/x502api-1.1.34/devs/l502/l502_fpga_regs.h @@ -0,0 +1,212 @@ +#ifndef L5XX_REGS_H +#define L5XX_REGS_H + +#define L583_BF_ADDR_ENDPROG (0xFFFFFFFCUL) + + +#define L502_MAX_PAGES_CNT 252 + +#define L502_BF_SDRAM_SIZE (32UL*1024*1024) + +#define L502_BF_MEMADDR_CMD 0xFF800800 + + +#define L502_BF_CMD_READ 0x0001 +#define L502_BF_CMD_WRITE 0x0002 +#define L502_BF_CMD_HIRQ 0x0004 +#define L502_BF_CMD_HDMA_RST 0x0008 + + +/********************* Адреса регистров блока EEPROM *************************/ +#define L502_REGS_EEPROM_BLOCK 0x0100 + +#define L502_REGS_EEPROM_SET_RD_ADDR (L502_REGS_EEPROM_BLOCK + 0) +#define L502_REGS_EEPROM_RD_DWORD (L502_REGS_EEPROM_BLOCK + 1) +#define L502_REGS_EEPROM_RD_STATUS (L502_REGS_EEPROM_BLOCK + 2) +#define L502_REGS_EEPROM_WR_STATUS_EN (L502_REGS_EEPROM_BLOCK + 3) +#define L502_REGS_EEPROM_WR_EN (L502_REGS_EEPROM_BLOCK + 4) +#define L502_REGS_EEPROM_WR_DIS (L502_REGS_EEPROM_BLOCK + 5) +#define L502_REGS_EEPROM_WR_STATUS (L502_REGS_EEPROM_BLOCK + 6) +#define L502_REGS_EEPROM_ERASE_4K (L502_REGS_EEPROM_BLOCK + 7) +#define L502_REGS_EEPROM_ERASE_64K (L502_REGS_EEPROM_BLOCK + 8) +#define L502_REGS_EEPROM_WR_BYTE (L502_REGS_EEPROM_BLOCK + 9) +#define L502_REGS_EEPROM_HARD_WR_STATUS_EN (L502_REGS_EEPROM_BLOCK + 0xF) +#define L502_REGS_HARD_ID (L502_REGS_EEPROM_BLOCK + 0xA) +#define L502_REGS_JEDEC_RD_ID (L502_REGS_EEPROM_BLOCK + 0xB) + +/********************* Адреса регистров с отладочной информацией **************/ +#define L502_REGS_DBG_BLOCK 0x0140 +#define L502_REGS_DBG_EVENTS (L502_REGS_DBG_BLOCK + 0) +#define L502_REGS_DBG_LAST_ABORT_ADDR (L502_REGS_DBG_BLOCK + 8) +#define L502_REGS_DBG_LAST_NACK_ADDR (L502_REGS_DBG_BLOCK + 9) +#define L502_REGS_DBG_LINK_REPLAY_CNT (L502_REGS_DBG_BLOCK + 10) + +/********************* Адреса регистров блока IOHARD **************************/ +#define L502_REGS_IOHARD_BLOCK 0x0200 +//Адрес Control Table +#define L502_REGS_IOHARD_LTABLE (L502_REGS_IOHARD_BLOCK+0) +#define L502_REGS_IOHARD_LTABLE_MAX_SIZE 0x100 // Максимальный размер Control Table + +#define L502_REGS_IOHARD_LCH_CNT (L502_REGS_IOHARD_BLOCK+0x100) +#define L502_REGS_IOHARD_ADC_FREQ_DIV (L502_REGS_IOHARD_BLOCK+0x102) +#define L502_REGS_IOHARD_ADC_FRAME_DELAY (L502_REGS_IOHARD_BLOCK+0x104) +#define L502_REGS_IOHARD_DIGIN_FREQ_DIV (L502_REGS_IOHARD_BLOCK+0x106) +#define L502_REGS_IOHARD_IO_MODE (L502_REGS_IOHARD_BLOCK+0x108) +#define L502_REGS_IOHARD_GO_SYNC_IO (L502_REGS_IOHARD_BLOCK+0x10A) +#define L502_REGS_IOHARD_PRELOAD_ADC (L502_REGS_IOHARD_BLOCK+0x10C) +#define L502_REGS_IOHARD_ASYNC_OUT (L502_REGS_IOHARD_BLOCK+0x112) +#define L502_REGS_IOHARD_LED (L502_REGS_IOHARD_BLOCK+0x114) +#define L502_REGS_IOHARD_DIGIN_PULLUP (L502_REGS_IOHARD_BLOCK+0x116) +#define L502_REGS_IOHARD_OUTSWAP_BFCTL (L502_REGS_IOHARD_BLOCK+0x118) +#define L502_REGS_IOHARD_OUTSWAP_ERROR (L502_REGS_IOHARD_BLOCK+0x120) + + + +/********************* Адреса регистров блока IOARITH **************************/ +#define L502_REGS_IOARITH_BLOCK 0x0400 +#define L502_REGS_IOARITH_B10 L502_REGS_IOARITH_BLOCK +#define L502_REGS_IOARITH_B5 (L502_REGS_IOARITH_BLOCK+0x01) +#define L502_REGS_IOARITH_B2 (L502_REGS_IOARITH_BLOCK+0x02) +#define L502_REGS_IOARITH_B1 (L502_REGS_IOARITH_BLOCK+0x03) +#define L502_REGS_IOARITH_B05 (L502_REGS_IOARITH_BLOCK+0x04) +#define L502_REGS_IOARITH_B02 (L502_REGS_IOARITH_BLOCK+0x05) +#define L502_REGS_IOARITH_K10 (L502_REGS_IOARITH_BLOCK+0x08) +#define L502_REGS_IOARITH_K5 (L502_REGS_IOARITH_BLOCK+0x09) +#define L502_REGS_IOARITH_K2 (L502_REGS_IOARITH_BLOCK+0x0A) +#define L502_REGS_IOARITH_K1 (L502_REGS_IOARITH_BLOCK+0x0B) +#define L502_REGS_IOARITH_K05 (L502_REGS_IOARITH_BLOCK+0x0C) +#define L502_REGS_IOARITH_K02 (L502_REGS_IOARITH_BLOCK+0x0D) +#define L502_REGS_IOARITH_ADC_FREQ_DIV (L502_REGS_IOARITH_BLOCK+0x12) +#define L502_REGS_IOARITH_IN_STREAM_ENABLE (L502_REGS_IOARITH_BLOCK+0x19) +#define L502_REGS_IOARITH_DIN_ASYNC (L502_REGS_IOARITH_BLOCK+0x1A) + + +/********************* Адреса регистров блока управления BlackFin'ом **********/ +#define L502_REGS_BF_CTL_BLOCK 0 +#define L502_REGS_BF_CTL (L502_REGS_BF_CTL_BLOCK+0) +#define L502_REGS_BF_CMD (L502_REGS_BF_CTL_BLOCK+1) +#define L502_REGS_BF_STATUS (L502_REGS_BF_CTL_BLOCK+2) +#define L502_REGS_BF_IRQ (L502_REGS_BF_CTL_BLOCK+3) +#define L502_REGS_BF_IRQ_EN (L502_REGS_BF_CTL_BLOCK+4) +#define L502_REGS_BF_REQ_ADDR (L502_REGS_BF_CTL_BLOCK+5) +#define L502_REGS_BF_REQ_SIZE (L502_REGS_BF_CTL_BLOCK+6) +#define L502_REGS_BF_REQ_DATA (L502_REGS_BF_CTL_BLOCK+128) + +#define L502_BF_REQ_DATA_SIZE_MAX 128 +#define L502_BF_REQ_DATA_SIZE_MIN 8 + + +/********************* Адреса регистров блока DMA *****************************/ +#define L502_REGS_DMA_CTL_BLOCK 0x700 +#define L502_REGS_DMA_CAP (L502_REGS_DMA_CTL_BLOCK) +#define L502_REGS_DMA_EN (L502_REGS_DMA_CTL_BLOCK+1) +#define L502_REGS_DMA_DIS (L502_REGS_DMA_CTL_BLOCK+2) +#define L502_REGS_DMA_RST (L502_REGS_DMA_CTL_BLOCK+3) +#define L502_REGS_DMA_IRQ (L502_REGS_DMA_CTL_BLOCK+4) +#define L502_REGS_DMA_IRQ_EN (L502_REGS_DMA_CTL_BLOCK+5) +#define L502_REGS_DMA_IRQ_DIS (L502_REGS_DMA_CTL_BLOCK+6) + +#define L502_REGS_DMA_CH_PARAMS_SIZE (16 + L502_MAX_PAGES_CNT*4) +#define L502_DMA_CHNUM_IN 0 +#define L502_DMA_CHNUM_OUT 1 + +/* номер регистра, с которого начинаются параметры канала DMA */ +#define L502_REGS_DMA_CH_PARAMS(ch) (0x800 + L502_REGS_DMA_CH_PARAMS_SIZE*ch) +#define L502_REGS_DMA_CH_CTL(ch) (L502_REGS_DMA_CH_PARAMS(ch) + 0) +#define L502_REGS_DMA_CH_CMP_CNTR(ch) (L502_REGS_DMA_CH_PARAMS(ch) + 1) +#define L502_REGS_DMA_CH_CUR_CNTR(ch) (L502_REGS_DMA_CH_PARAMS(ch) + 2) +#define L502_REGS_DMA_CH_CUR_POS(ch) (L502_REGS_DMA_CH_PARAMS(ch) + 3) +#define L502_REGS_DMA_CH_PC_POS(ch) (L502_REGS_DMA_CH_PARAMS(ch) + 4) + + +/* адреса для регистров страниц DMA АЦП */ +#define L502_REGS_DMA_CH_PAGES(ch) (L502_REGS_DMA_CH_PARAMS(ch) + 16) +#define L502_REGS_DMA_CH_PAGE_ADDRL(ch,n) (L502_REGS_DMA_CH_PAGES(ch) + 4*(n)) +#define L502_REGS_DMA_CH_PAGE_ADDRH(ch,n) (L502_REGS_DMA_CH_PAGES(ch) + 4*(n)+1) +#define L502_REGS_DMA_CH_PAGE_LEN(ch,n) (L502_REGS_DMA_CH_PAGES(ch) + 4*(n)+2) + + + + + +#define L502_REGBIT_BF_STATUS_HWAIT_Pos 0 +#define L502_REGBIT_BF_STATUS_HWAIT_Msk (1UL << L502_REGBIT_BF_STATUS_HWAIT_Pos) + +#define L502_REGBIT_BF_STATUS_BUSY_Pos 1 +#define L502_REGBIT_BF_STATUS_BUSY_Msk (1UL << L502_REGBIT_BF_STATUS_BUSY_Pos) + + +/* описание отдельных битов регистров */ +#define L502_REGBIT_DMA_CTL_PACK_SIZE_Pos 0 +#define L502_REGBIT_DMA_CTL_PACK_SIZE_Msk (0xFFUL << L502_REGBIT_DMA_CTL_PACK_SIZE_Pos) + +#define L502_REGBIT_DMA_CTL_PAGE_CNT_Pos 16 +#define L502_REGBIT_DMA_CTL_PAGE_CNT_Msk (0xFFUL << L502_REGBIT_DMA_CTL_PAGE_CNT_Pos) + +#define L502_REGBIT_DMA_CTL_AUTOSTOP_Pos 31 +#define L502_REGBIT_DMA_CTL_AUTOSTOP_Msk (0x1UL << L502_REGBIT_DMA_CTL_AUTOSTOP_Pos) + +#define L502_REGBIT_DMA_CTL_PC_WAIT_Pos 30 +#define L502_REGBIT_DMA_CTL_PC_WAIT_Msk (0x1UL << L502_REGBIT_DMA_CTL_PC_WAIT_Pos) + +#define L502_REGBIT_DMA_CH_ADC_Pos 0 +#define L502_REGBIT_DMA_CH_ADC_Msk (0x1UL << L502_REGBIT_DMA_CH_ADC_Pos) + +#define L502_REGBIT_DMA_CH_DAC_Pos 1 +#define L502_REGBIT_DMA_CH_DAC_Msk (0x1UL << L502_REGBIT_DMA_CH_DAC_Pos) + + + +#define L502_REGBIT_BF_CTL_BF_RESET_Pos 1 +#define L502_REGBIT_BF_CTL_BF_RESET_Msk (0x1UL << L502_REGBIT_BF_CTL_BF_RESET_Pos) + + +#define L502_REGBIT_BF_CTL_HOST_WAIT_Pos 3 +#define L502_REGBIT_BF_CTL_HOST_WAIT_Msk (0x1UL << L502_REGBIT_BF_CTL_HOST_WAIT_Pos) + +#define L502_REGBIT_BF_CTL_DSP_MODE_Pos 4 +#define L502_REGBIT_BF_CTL_DSP_MODE_Msk (0x1UL << L502_REGBIT_BF_CTL_DSP_MODE_Pos) + +#define L502_REGBIT_BF_CTL_DBG_MODE_Pos 5 +#define L502_REGBIT_BF_CTL_DBG_MODE_Msk (0x1UL << L502_REGBIT_BF_CTL_DBG_MODE_Pos) + +#define L502_REGBIT_BF_CTL_CLK_DIV_Pos 8 +#define L502_REGBIT_BF_CTL_CLK_DIV_Msk (0xFUL << L502_REGBIT_BF_CTL_CLK_DIV_Pos) + +#define L502_REGBIT_DMA_CURPOS_PAGE_Pos 24 +#define L502_REGBIT_DMA_CURPOS_PAGE_Msk (0xFFUL << L502_REGBIT_DMA_CURPOS_PAGE_Pos) + +#define L502_REGBIT_DMA_CURPOS_OFFSET_Pos 0 +#define L502_REGBIT_DMA_CURPOS_OFFSET_Msk (0xFFFFFFUL << L502_REGBIT_DMA_CURPOS_OFFSET_Pos) + +#define L502_REGBIT_ADC_SLV_CLK_LOCK_Pos 31 +#define L502_REGBIT_ADC_SLV_CLK_LOCK_Msk (0x1UL << L502_REGBIT_ADC_SLV_CLK_LOCK_Pos) + + + +#define L502_REGBIT_IOHARD_OUT_SWAP_Pos 0 +#define L502_REGBIT_IOHARD_OUT_SWAP_Msk (0x1UL << L502_REGBIT_IOHARD_OUT_SWAP_Pos) + +#define L502_REGBIT_IOHARD_OUT_TFS_EN_Pos 1 +#define L502_REGBIT_IOHARD_OUT_TFS_EN_Msk (0x1UL << L502_REGBIT_IOHARD_OUT_TFS_EN_Pos) + +#define L502_REGBIT_IOHARD_OUT_RING_Pos 2 +#define L502_REGBIT_IOHARD_OUT_RING_Msk (0x1UL << L502_REGBIT_IOHARD_OUT_RING_Pos) + +#define L502_REGBIT_IOHARD_OUT_RFS_EN_Pos 3 +#define L502_REGBIT_IOHARD_OUT_RFS_EN_Msk (0x1UL << L502_REGBIT_IOHARD_OUT_RFS_EN_Pos) + + + + + + + + +#define L502_REGBIT_DMA_IRQ_STEP_Msk(ch) (1UL << ch) +#define L502_REGBIT_DMA_IRQ_PAGE_Msk(ch) (1UL << (ch+8)) +#define L502_REGBIT_DMA_IRQ_FLUSH_Msk(ch) (1UL << (ch+16)) + + + +#endif // L5XX_REGS_H diff --git a/x502api-1.1.34/devs/l502/l502api.c b/x502api-1.1.34/devs/l502/l502api.c new file mode 100644 index 0000000..e7086a2 --- /dev/null +++ b/x502api-1.1.34/devs/l502/l502api.c @@ -0,0 +1,164 @@ +#include "l502api.h" +#include "l502api_private.h" +#include "l502_fpga_regs.h" +#include +#include +#include + +/* минимальный размер внутреннего буфера */ +#define L502_DMA_IN_BUF_SIZE_MIN 16*1024 + + +#ifdef _WIN32 +BOOL WINAPI DllMain(HINSTANCE hmod, DWORD reason, LPVOID resvd) { + switch (reason) { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +#endif + +static int32_t f_iface_free_devinfo_ptr(t_x502_devrec_inptr *devinfo_ptr); +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_cycle_load_start(t_x502_hnd hnd, uint32_t size); +static int32_t f_iface_cycle_setup(t_x502_hnd hnd, uint32_t flags); +static int32_t f_iface_cycle_stop(t_x502_hnd hnd, uint32_t flags); +static int32_t f_iface_cycle_check_setup(t_x502_hnd hnd, uint32_t *done); +static int32_t f_iface_check_feature(t_x502_hnd hnd, uint32_t feature); + + +static const t_x502_dev_iface f_pcie_iface = { + L502_REGS_HARD_ID, + L502_DMA_IN_BUF_SIZE_MIN, + 0, + L502_BF_REQ_DATA_SIZE_MAX, + 4, //flash rd size + 1, //flash wr size + f_iface_free_devinfo_ptr, + l502_port_open, + l502_port_close, + l502_port_fpga_reg_read, + l502_port_fpga_reg_write, + f_iface_stream_cfg, + l502_port_stream_start, + l502_port_stream_stop, + l502_port_stream_free, + NULL, + l502_port_stream_read, + l502_port_stream_write, + l502_port_stream_rdy_size, + l502_iface_bf_mem_block_rd, + l502_iface_bf_mem_block_wr, + l502_iface_bf_firm_load, + l502_iface_flash_rd, + l502_iface_flash_wr, + l502_iface_flash_erase, + l502_iface_flash_set_prot, + l502_port_renew_info, + f_iface_cycle_load_start, + f_iface_cycle_setup, + f_iface_cycle_stop, + f_iface_cycle_check_setup, + NULL, + NULL, + f_iface_check_feature +}; + + +X502_EXPORT(int32_t) L502_GetDriverVersion(t_x502_hnd hnd, uint32_t* ver) { + int32_t err = X502_CHECK_HND(hnd); + if (err == X502_ERR_OK) + err = l502_port_get_drv_ver(hnd, ver); + return err; +} + +int32_t l502_devlist_gen(t_x502_devrec *info, void *iface_data) { + 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; + } else { + devinfo_ptr->iface_data = iface_data; + devinfo_ptr->iface = &f_pcie_iface; + info->iface = X502_IFACE_PCI; + info->flags |= X502_DEVFLAGS_IFACE_SUPPORT_PCI | X502_DEVFLAGS_FPGA_LOADED; + info->internal = devinfo_ptr; + } + return err; +} + +X502_EXPORT(int32_t) L502_GetSerialList(char serials[][X502_SERIAL_SIZE], uint32_t size, + uint32_t flags, uint32_t *devcnt) { + return X502_GetSerialList(serials, size, flags, devcnt, L502_DEVICE_NAME, L502_GetDevRecordsList); +} + +X502_EXPORT(int32_t) L502_Open(t_x502_hnd hnd, const char *serial) { + return X502_Open(hnd, serial, L502_DEVICE_NAME, L502_GetDevRecordsList); +} + + +static int32_t f_iface_free_devinfo_ptr(t_x502_devrec_inptr *devinfo_ptr) { + l502_port_free_iface_data(devinfo_ptr->iface_data); + free(devinfo_ptr); + return X502_ERR_OK; +} + +static int32_t f_iface_stream_cfg(t_x502_hnd hnd, uint32_t ch, t_x502_stream_ch_params *params) { + t_lpcie_stream_ch_params lpcie_ch_params; + memset(&lpcie_ch_params, 0, sizeof(lpcie_ch_params)); + lpcie_ch_params.ch = ch; + lpcie_ch_params.irq_step = params->step; + lpcie_ch_params.buf_size = params->buf_size; + return l502_port_stream_set_params(hnd, &lpcie_ch_params); +} + +static int32_t f_iface_cycle_load_start(t_x502_hnd hnd, uint32_t size) { + uint32_t irq_step = STREAM_OUT_IRQ_STEP(hnd); + return l502_port_cycle_load_start(hnd, L502_DMA_CHNUM_OUT, size, irq_step); +} +static int32_t f_iface_cycle_setup(t_x502_hnd hnd, uint32_t flags) { + return l502_port_cycle_setup(hnd, L502_DMA_CHNUM_OUT, (flags & X502_OUT_CYCLE_FLAGS_FORCE) ? + LPCIE_CYCLE_SW_EVT_IMMIDIATLY : LPCIE_CYCLE_SW_EVT_END_OF_CYCLE); +} + +static int32_t f_iface_cycle_stop(t_x502_hnd hnd, uint32_t flags) { + return l502_port_cycle_stop(hnd, L502_DMA_CHNUM_OUT, (flags & X502_OUT_CYCLE_FLAGS_FORCE) ? + LPCIE_CYCLE_SW_EVT_IMMIDIATLY : LPCIE_CYCLE_SW_EVT_END_OF_CYCLE); +} + +static int32_t f_iface_cycle_check_setup(t_x502_hnd hnd, uint32_t *done) { + uint32_t ver; + int32_t err = L502_GetDriverVersion(hnd, &ver); + if ((err == X502_ERR_OK) && !LPCIE_IOCTL_SUPPORT_CYCLE_CHECK_SETUP(ver)) + err = X502_ERR_NOT_SUP_BY_DRIVER; + if (err == X502_ERR_OK) + err = l502_port_cycle_check_setup(hnd, L502_DMA_CHNUM_OUT, done); + + if (err == X502_ERR_OK) { + /* за счет буфера в плате на вывод может пройти несколько мс после + * передачи в модуль данных до того как реально эти данные появятся + * на выходе. т.к. это отследить явно нельзя, то приходится ставить задержку */ + SLEEP_MS(3); + } + + return err; +} + +static int32_t f_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: + if (hnd->info.fpga_ver >= 0x5) + err = X502_ERR_OK; + break; + default: + err = X502_ERR_UNKNOWN_FEATURE_CODE; + break; + } + return err; +} diff --git a/x502api-1.1.34/devs/l502/l502api.def b/x502api-1.1.34/devs/l502/l502api.def new file mode 100644 index 0000000..56e8121 --- /dev/null +++ b/x502api-1.1.34/devs/l502/l502api.def @@ -0,0 +1,72 @@ +LIBRARY l502api.dll + +EXPORTS + L502_Create + L502_Free + L502_Open + L502_Close + L502_GetSerialList + L502_GetDevInfo + L502_Configure + L502_StreamsEnable + L502_StreamsDisable + L502_StreamsStart + L502_StreamsStop + L502_IsRunning + L502_Recv + L502_Send + L502_GetRecvReadyCount + L502_GetSendReadyCount + L502_SetDmaBufSize + L502_SetDmaIrqStep + L502_GetNextExpectedLchNum + L502_PreloadStart + L502_ProcessAdcData + L502_ProcessData + L502_ProcessDataWithUserExt + L502_PrepareData + L502_SetLChannel + L502_SetLChannelCount + L502_GetLChannelCount + L502_SetAdcFreqDivider + L502_SetAdcInterframeDelay + L502_SetDinFreqDivider + L502_SetAdcFreq + L502_SetDinFreq + L502_GetAdcFreq + L502_SetRefFreq + L502_SetSyncMode + L502_SetSyncStartMode + L502_SetMode + L502_GetMode + L502_SetAdcCoef + L502_GetAdcCoef + L502_SetDacCoef + L502_GetDacCoef + L502_AsyncOutDac + L502_AsyncOutDig + L502_AsyncInDig + L502_AsyncGetAdcFrame + L502_BfCheckFirmwareIsLoaded + L502_BfLoadFirmware + L502_BfMemRead + L502_BfMemWrite + L502_BfExecCmd + L502_FlashRead + L502_FlashWrite + L502_FlashErase + L502_FlashWriteEnable + L502_FlashWriteDisable + L502_FpgaRegWrite + L502_FpgaRegRead + L502_GetDllVersion + L502_GetDriverVersion + L502_GetErrorString + L502_LedBlink + L502_SetDigInPullup + L502_ReloadDevInfo + L502_OutCycleLoadStart + L502_OutCycleSetup + L502_OutCycleStop + L502_GetDevRecordsList + diff --git a/x502api-1.1.34/devs/l502/l502api.h b/x502api-1.1.34/devs/l502/l502api.h new file mode 100644 index 0000000..a7d2137 --- /dev/null +++ b/x502api-1.1.34/devs/l502/l502api.h @@ -0,0 +1,162 @@ +/***************************************************************************//** + @file l502api.h + Файл содержит все необходимые описания типов, констант и функций для работы + с модулем L-502 из пользовательской программы. + @date 11.03.2012 + @author Borisov Alexey + ******************************************************************************/ + +#ifndef L502_API_H +#define L502_API_H + +#include "l502api_compat.h" +#include "x502api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************//** + @addtogroup func_open + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Получение списка серийных номеров модулей L-502 + + Функция возвращает список номеров всех найденных модулей L-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, то в данную переменную сохраняется + общее число найденных модулей L502 + (может быть больше size). + @return Если <0 - код ошибки, иначе количество сохраненных + серийных номеров в массиве serials (всегда <= size) +*******************************************************************************/ +X502_EXPORT(int32_t) L502_GetSerialList(char serials[][X502_SERIAL_SIZE], uint32_t size, + uint32_t flags, uint32_t *devcnt); + +/***************************************************************************//** + @brief Открытие модуля L-502 по его серийному номеру + + Функция устанавливает связь с модулем L-502 по его серийному номеру. + После успешного выполнения этой функции, пользователь получает эксклюзивный + доступ к модулю через описатель модуля. До закрытия связи с помощью + X502_Close() никто другой установить связь с модулем не сможет + (будет возвращена ошибка #X502_ERR_DEVICE_ACCESS_DENIED). + + Если в качестве серийного номера передан NULL или пустая строка, то будет + установлена связь с первым найденным модулем, с которым получится успешно + ее установить. + Если в системе нет ни одного модуля, то будет возвращена ошибка + #X502_ERR_DEVICE_NOT_FOUND. Если в системе присутствуют модули L-502, но + соединение ни с одним из них установить не удалось, то будет возвращена + ошибка, полученная при попытке установить соединение с последним + найденным модулем. + + После завершения работы с устройством соединение должно быть закрыто с + помощью X502_Close(). + + @param[in] hnd Описатель устройства. + @param[in] serial Указатель на строку с серийным номером открываемого + модуля или NULL. + @return Код ошибки. +*******************************************************************************/ +X502_EXPORT(int32_t) L502_Open(t_x502_hnd hnd, const char *serial); + + +/** @} */ + + +/***************************************************************************//** + @addtogroup func_devrec + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Получить список записей, соответствующих подключенным модулям L502 + + Функция находит все подключенные модули L-502 и инициализирует + записи о каждом найденном устройстве и сохраняет их в переданный список + (если не нулевой). + Возвращенные в списке записи должны быть очищены после использования + с помощью X502_FreeDevRecordList() (также в случае повторного + вызов L502_GetDevRecordsList() с тем же массивом записей, записи, полученные + при предыдущем вызове, должны быть сперва очищены). + + @param[in] list Массив для сохранения записей о найденных устройствах. + Должен содержать место для сохранения не менее size записей. + Может быть NULL, если size=0, а devcnt!=NULL, в случае, + если нужно только получить количество модулей в системе. + @param[in] size Определяет, сколько максимально записей может + быть сохранено в массив list. Будут сохранены только + первые size записей, если устройств найденно больше. + @param[in] flags Флаги из #t_x502_getdevs_flags, определяющие поведение + функции. + @param[out] devcnt Если не нулевой указатель, то в данную переменную сохраняется + общее число найденных модулей L-502 (может быть больше size). + @return Если <0 --- код ошибки, иначе количество сохраненных + записей о найденных устройствах (всегда <= size). + Именно на этот размер нужно сделать в дальнейшем + X502_FreeDevRecordList() для освобождения памяти, + выделенной под информацию, на которую ссылается запись. + ******************************************************************************/ +X502_EXPORT(int32_t) L502_GetDevRecordsList(t_x502_devrec *list, uint32_t size, + uint32_t flags, uint32_t *devcnt) ; +/** @} */ + + + + +/***************************************************************************//** + @addtogroup func_misc + @{ +*******************************************************************************/ + +/**************************************************************************//** + @brief Получить версию драйвера модуля L-502 + + Функция возвращает версию драйвера, установленного для + указанного открытого устройства. + Версия возвращается в виде 32-битного числа. + Строковое представление возвращенной версии - четыре числа, + старшее соответствует старшему байту, младшее - младшему. + + Старший байт - мажорная версия, второй по старшинству байт - минорная, + третий - ревизия, четвертый - номер сборки (не используется - всегда 0). + + Это та версия, которая отображается в диспетчере устройств в Windows или + с помощью modinfo в Linux. + + Данная функция доступна только для устройств с интерфейсом PCI/PCI-Express (L502) + + @param[in] hnd Описатель модуля. + @param[out] ver 32-битное число, представляющее собой версию драйвера + @return Код ошибки. + *****************************************************************************/ +X502_EXPORT(int32_t) L502_GetDriverVersion(t_x502_hnd hnd, uint32_t* ver); + +/** @} */ + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/x502api-1.1.34/devs/l502/l502api.rc.in b/x502api-1.1.34/devs/l502/l502api.rc.in new file mode 100644 index 0000000..e39862f --- /dev/null +++ b/x502api-1.1.34/devs/l502/l502api.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 L502" + VALUE "FileVersion", "@X502API_VERSION@" + VALUE "OriginalFilename", "l502api.dll" + VALUE "ProductName", "l502api" + VALUE "ProductVersion", "@X502API_VERSION@" + VALUE "LegalCopyright", "© 2015 L-Card Ltd." + END + + BLOCK "04190000" + BEGIN + VALUE "CompanyName", "Л Кард" + VALUE "FileDescription", "Библиотека для работы с платой L502" + VALUE "FileVersion", "@X502API_VERSION@" + VALUE "OriginalFilename", "l502api.dll" + VALUE "ProductName", "l502api" + 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/x502api-1.1.34/devs/l502/l502api_bf.c b/x502api-1.1.34/devs/l502/l502api_bf.c new file mode 100644 index 0000000..219e8e7 --- /dev/null +++ b/x502api-1.1.34/devs/l502/l502api_bf.c @@ -0,0 +1,272 @@ +#include "l502api_private.h" +#include "ltimer.h" +#include "l502_fpga_regs.h" +#include +#include + +#define BF_LDR_HDR_SIZE (16) +#define BF_LDR_HDRSGN (0xAD) + +#define BF_LDR_HDRPOS_SGN (3) + +#define BF_LDR_FLAG_SAVE (0x0010) //не используется +#define BF_LDR_FLAG_AUX (0x0020) //не используется +#define BF_LDR_FLAG_FILL (0x0100) +#define BF_LDR_FLAG_QUICKBOOT (0x0200) //не используется +#define BF_LDR_FLAG_CALLBACK (0x0400) //не используется +#define BF_LDR_FLAG_INIT (0x0800) //не используется +#define BF_LDR_FLAG_IGNORE (0x1000) +#define BF_LDR_FLAG_INDIRECT (0x2000) //не используется +#define BF_LDR_FLAG_FIRST (0x4000) +#define BF_LDR_FLAG_FINAL (0x8000) + +#define L502_BF_WAIT_LOAD_RDY_TOUT 500 + +#define LDR_BUFF_SIZE 4096 + +#define BF_CHECK_ADDR(addr) (((addr) < 0xFFA0C000) && ((addr)>= 0xFFA0000)) || \ + (((addr) < 0xFF908000) && ((addr) >=0xFF900000)) || \ + (((addr) < 0xFF808000) && ((addr) >=0xFF800000)) || \ + (((addr) < 0x2000000)) ? 0 : X502_ERR_BF_INVALID_ADDR + +#define BF_CHECK_ADDR_SIZE(addr, size) BF_CHECK_ADDR(addr) ? X502_ERR_BF_INVALID_ADDR : \ + BF_CHECK_ADDR(addr+size*4-1) ? X502_ERR_BF_INVALID_ADDR : 0 + + +typedef struct st_bf_ldr_pkt { + uint8_t res; + uint8_t dma_mode; + uint16_t flags; + uint32_t addr; + uint32_t size; + uint32_t arg; +} t_bf_ldr_pkt; + +/* Разбираем заголовок блока LDR-формата из буфера размером BF_LDR_HDR_SIZE + и сохраняем параметры в структуре pkt */ +static int32_t f_parse_ldr_hdr(const uint8_t *hdr, t_bf_ldr_pkt *pkt) { + int32_t err = X502_ERR_OK; + uint32_t* pdw_buff = (uint32_t*)hdr; + uint8_t xor_ch = 0; + int i; + for (i=0; i < BF_LDR_HDR_SIZE; i++) { + xor_ch ^= hdr[i]; + } + + if ((xor_ch!=0) || (hdr[BF_LDR_HDRPOS_SGN] != BF_LDR_HDRSGN)) { + err = X502_ERR_LDR_FILE_FORMAT; + } else { + pkt->res = 0; + pkt->dma_mode = pdw_buff[0]&0xF; + pkt->flags = pdw_buff[0]&0xFFF0; + pkt->addr = pdw_buff[1]; + pkt->size = pdw_buff[2]; + pkt->arg = pdw_buff[3]; + + if ((pkt->flags & BF_LDR_FLAG_INIT) && (pkt->flags & BF_LDR_FLAG_FILL)) { + err = X502_ERR_LDR_FILE_FORMAT; + } else if (pkt->flags & (BF_LDR_FLAG_CALLBACK | BF_LDR_FLAG_INDIRECT | BF_LDR_FLAG_INIT)) { + err = X502_ERR_LDR_FILE_UNSUP_FEATURE; + } else if ((pkt->flags & BF_LDR_FLAG_INIT) && (pkt->addr != 0xFFA00000)) { + err = X502_ERR_LDR_FILE_UNSUP_STARTUP_ADDR; + } + } + return err; +} + +static int32_t f_bf_wait_cmd_done(t_x502_hnd hnd) { + int32_t err = X502_ERR_OK; + t_ltimer tmr; + uint32_t status; + ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(X502_BF_REQ_TOUT)); + do { + err = l502_port_fpga_reg_read(hnd, L502_REGS_BF_STATUS, &status); + } while ((status & L502_REGBIT_BF_STATUS_BUSY_Msk) && + (err == X502_ERR_OK) && !ltimer_expired(&tmr)); + + if (!err && (status & L502_REGBIT_BF_STATUS_BUSY_Msk)) + err = X502_ERR_BF_REQ_TIMEOUT; + return err; +} + + + +int32_t l502_iface_bf_mem_block_rd(t_x502_hnd hnd, uint32_t addr, uint32_t *block, uint32_t size){ + uint32_t i; + int32_t err = f_bf_wait_cmd_done(hnd); + /* записываем переметры передачи - размер и адрес в памяти BlackFin */ + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_REQ_SIZE, size); + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_REQ_ADDR, addr); + /* даем команду на запис */ + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_CMD, L502_BF_CMD_READ); + + /* ждем, пока операция не будет завершена */ + if (err == X502_ERR_OK) + err = f_bf_wait_cmd_done(hnd); + + /* записываем блок данных в буфер ПЛИС */ + for (i=0; (i < size) && (err == X502_ERR_OK); i++) { + err = l502_port_fpga_reg_read(hnd, L502_REGS_BF_REQ_DATA+i, &block[i]); + } + return err; +} + +int32_t l502_iface_bf_mem_block_wr(t_x502_hnd hnd, uint32_t addr, const uint32_t *block, uint32_t size) { + uint32_t i; + int32_t err = f_bf_wait_cmd_done(hnd); + if (err == X502_ERR_OK) { + /* записываем блок данных в буфер ПЛИС */ + for (i=0; (i < size) && (err == X502_ERR_OK); i++) { + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_REQ_DATA+i, block[i]); + } + + /* записываем переметры передачи - размер и адрес в памяти BlackFin */ + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_REQ_SIZE, size); + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_REQ_ADDR, addr); + /* даем команду на запис */ + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_CMD, L502_BF_CMD_WRITE); + + /* ждем, пока операция не будет завершена */ + if (err == X502_ERR_OK) + err = f_bf_wait_cmd_done(hnd); + } + return err; +} + + +static int32_t f_bf_mem_wr(t_x502_hnd hnd, uint32_t addr, const uint32_t* regs, uint32_t size) { + int32_t err = X502_ERR_OK; + + /* данные записываем блоками по L502_BF_REQ_DATA_SIZE */ + while ((err == X502_ERR_OK) && size) { + int put_size = (size < hnd->iface_hnd->bf_mem_block_size) ? size : + hnd->iface_hnd->bf_mem_block_size; + err = hnd->iface_hnd->bf_mem_block_wr(hnd, addr, regs, put_size); + if (!err) { + size -= put_size; + regs += put_size; + addr += put_size*4; + } + } + return err; +} + +int32_t l502_iface_bf_firm_load(t_x502_hnd hnd, const char *filename) { + int32_t err = X502_ERR_OK; + FILE* ldr_file=fopen(filename, "rb"); + if (ldr_file==NULL) { + err = X502_ERR_LDR_FILE_OPEN; + } else { + int32_t next_err = X502_ERR_OK; + uint32_t *ldr_buff = NULL; + ldr_buff = malloc(LDR_BUFF_SIZE); + if (ldr_buff == NULL) + err = X502_ERR_MEMORY_ALLOC; + + if (err == X502_ERR_OK) { + int rd_size = 0; + int stop = 0; + uint32_t reg; + uint8_t hdr[BF_LDR_HDR_SIZE]; + t_ltimer tmr; + + //uint32_t* pdw = (uint32_t*)ldr_buff; + t_bf_ldr_pkt pkt, pkt_next; + uint32_t bf_val = 0; + memset(&pkt_next, 0, sizeof(pkt_next)); + + l502_port_fpga_reg_read(hnd, L502_REGS_BF_CTL, &bf_val); + l502_port_fpga_reg_write(hnd, L502_REGS_BF_CTL, L502_REGBIT_BF_CTL_DSP_MODE_Msk + | (bf_val & 0xF00)); //set rst + SLEEP_MS(1); + l502_port_fpga_reg_write(hnd, L502_REGS_BF_CTL, L502_REGBIT_BF_CTL_DSP_MODE_Msk | + L502_REGBIT_BF_CTL_BF_RESET_Msk | (bf_val & 0xF00)); //release rst + + ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(L502_BF_WAIT_LOAD_RDY_TOUT)); + do { + l502_port_fpga_reg_read(hnd, L502_REGS_BF_CTL, ®); + if ((reg & L502_REGBIT_BF_CTL_HOST_WAIT_Msk) && ltimer_expired(&tmr)) + err = X502_ERR_BF_LOAD_RDY_TOUT; + } while ((err == X502_ERR_OK) && (reg & L502_REGBIT_BF_CTL_HOST_WAIT_Msk)); + + if (err == X502_ERR_OK) { + err = fread(hdr, 1, BF_LDR_HDR_SIZE, ldr_file) == BF_LDR_HDR_SIZE ? + f_parse_ldr_hdr(hdr, &pkt) : X502_ERR_LDR_FILE_READ; + } + + while ((err == X502_ERR_OK) && !stop) { + if (next_err != X502_ERR_OK) { + err = next_err; + } else if (((pkt.flags & BF_LDR_FLAG_FILL) == 0) && (pkt.size != 0)) { + int r_size = (pkt.size > LDR_BUFF_SIZE) ? LDR_BUFF_SIZE : pkt.size; + + rd_size = (int)fread(ldr_buff, 1, r_size, ldr_file); + if (rd_size!=r_size) + err = X502_ERR_LDR_FILE_READ; + } + if (err == X502_ERR_OK) { + if (pkt.size > LDR_BUFF_SIZE) { + pkt_next = pkt; + pkt_next.addr += LDR_BUFF_SIZE; + pkt_next.size -= LDR_BUFF_SIZE; + pkt.size = LDR_BUFF_SIZE; + } else { + next_err = fread(hdr, 1, BF_LDR_HDR_SIZE, ldr_file) == BF_LDR_HDR_SIZE ? + f_parse_ldr_hdr(hdr, &pkt_next) : X502_ERR_LDR_FILE_READ; + if (next_err != X502_ERR_OK) { + pkt_next.size = 0; + } + } + + if (pkt.size!=0) { + uint32_t size = ((pkt.size+31)/(32))*8; + if (pkt.flags & BF_LDR_FLAG_FILL) { + uint32_t i; + for (i=0; i < size; i++) + ldr_buff[i] = pkt.arg; + } + + if ((pkt.flags & BF_LDR_FLAG_FINAL) + || ((pkt_next.flags & BF_LDR_FLAG_FINAL) && (pkt_next.size==0))) { + uint32_t buf_pos = 0; + err = BF_CHECK_ADDR_SIZE(pkt.addr, size); + + if ((err == X502_ERR_OK) && (size > 8)) { + err = f_bf_mem_wr(hnd, pkt.addr, ldr_buff, size-8); + pkt.addr+=4*(size-8); + buf_pos = size-8; + size = 8; + } + + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_CMD, L502_BF_CMD_HIRQ); + if (err == X502_ERR_OK) + err = f_bf_mem_wr(hnd, pkt.addr, &ldr_buff[buf_pos], size); + stop=1; + + if (err == X502_ERR_OK) { + err = l502_port_fpga_reg_write(hnd, L502_REGS_BF_CTL, L502_REGBIT_BF_CTL_DSP_MODE_Msk | + L502_REGBIT_BF_CTL_BF_RESET_Msk); + } + } else if (!(pkt.flags & BF_LDR_FLAG_IGNORE)) { + err = BF_CHECK_ADDR_SIZE(pkt.addr, size); + if (!err) + err = f_bf_mem_wr(hnd, pkt.addr, ldr_buff, size); + } + } + pkt = pkt_next; + } + } + } + free(ldr_buff); + + fclose(ldr_file); + } + return err; +} diff --git a/x502api-1.1.34/devs/l502/l502api_compat.c b/x502api-1.1.34/devs/l502/l502api_compat.c new file mode 100644 index 0000000..6b7946c --- /dev/null +++ b/x502api-1.1.34/devs/l502/l502api_compat.c @@ -0,0 +1,285 @@ +#include "l502api_compat.h" +#include "x502api_private.h" + +LPCIE_EXPORT(t_l502_hnd) L502_Create(void) { + return X502_Create(); +} + +LPCIE_EXPORT(int32_t) L502_Free(t_l502_hnd hnd) { + return X502_Free(hnd); +} + +LPCIE_EXPORT(int32_t) L502_Close(t_l502_hnd hnd) { + return X502_Close(hnd); +} + +LPCIE_EXPORT(int32_t) L502_GetDevInfo(t_l502_hnd hnd, t_l502_info* info) { + return X502_GetDevInfo(hnd, (t_x502_info*)info); +} + +LPCIE_EXPORT(int32_t) L502_Configure(t_l502_hnd hnd, uint32_t flags) { + return X502_Configure(hnd, flags); +} + +LPCIE_EXPORT(int32_t) L502_SetLChannel(t_l502_hnd hnd, uint32_t lch, uint32_t phy_ch, + uint32_t mode, uint32_t range, uint32_t avg) { + return X502_SetLChannel(hnd, lch, phy_ch, mode, range, avg); +} + +LPCIE_EXPORT(int32_t) L502_SetLChannelCount(t_l502_hnd hnd, uint32_t lch_cnt) { + return X502_SetLChannelCount(hnd, lch_cnt); +} + +LPCIE_EXPORT(int32_t) L502_GetLChannelCount(t_l502_hnd hnd, uint32_t* lch_cnt) { + return X502_GetLChannelCount(hnd, lch_cnt); +} + +LPCIE_EXPORT(int32_t) L502_SetAdcFreqDivider(t_l502_hnd hnd, uint32_t adc_freq_div) { + return X502_SetAdcFreqDivider(hnd, adc_freq_div); +} + +LPCIE_EXPORT(int32_t) L502_SetAdcInterframeDelay(t_l502_hnd hnd, uint32_t delay) { + return X502_SetAdcInterframeDelay(hnd, delay); +} + +LPCIE_EXPORT(int32_t) L502_SetDinFreqDivider(t_l502_hnd hnd, uint32_t din_freq_div) { + return X502_SetDinFreqDivider(hnd, din_freq_div); +} + +LPCIE_EXPORT(int32_t) L502_SetAdcFreq(t_l502_hnd hnd, double *f_acq, double *f_frame) { + return X502_SetAdcFreq(hnd, f_acq, f_frame); +} + +LPCIE_EXPORT(int32_t) L502_SetDinFreq(t_l502_hnd hnd, double *f_din) { + return X502_SetDinFreq(hnd, f_din); +} + + +LPCIE_EXPORT(int32_t) L502_GetAdcFreq(t_l502_hnd hnd, double *f_acq, double *f_frame) { + return X502_GetAdcFreq(hnd, f_acq, f_frame); +} + +LPCIE_EXPORT(int32_t) L502_SetRefFreq(t_l502_hnd hnd, uint32_t freq) { + return X502_SetRefFreq(hnd, freq); +} + +LPCIE_EXPORT(int32_t) L502_SetSyncMode(t_l502_hnd hnd, uint32_t sync_mode) { + return X502_SetSyncMode(hnd, sync_mode); +} + +LPCIE_EXPORT(int32_t) L502_SetSyncStartMode(t_l502_hnd hnd, uint32_t sync_start_mode) { + return X502_SetSyncStartMode(hnd, sync_start_mode); +} + +LPCIE_EXPORT(int32_t) L502_SetMode(t_l502_hnd hnd, uint32_t mode) { + return X502_SetMode(hnd, mode); +} + +LPCIE_EXPORT(int32_t) L502_GetMode(t_l502_hnd hnd, uint32_t* mode) { + return X502_GetMode(hnd, mode); +} + +LPCIE_EXPORT(int32_t) L502_SetAdcCoef(t_l502_hnd hnd, uint32_t range, double k, double offs) { + return X502_SetAdcCoef(hnd, range, k, offs); +} + +LPCIE_EXPORT(int32_t) L502_GetAdcCoef(t_l502_hnd hnd, uint32_t range, double* k, double* offs) { + return X502_GetAdcCoef(hnd, range, k, offs); +} + +LPCIE_EXPORT(int32_t) L502_SetDacCoef(t_l502_hnd hnd, uint32_t ch, double k, double offs) { + return X502_SetDacCoef(hnd, ch, k, offs); +} + +LPCIE_EXPORT(int32_t) L502_GetDacCoef(t_l502_hnd hnd, uint32_t ch, double* k, double* offs) { + return X502_GetDacCoef(hnd, ch, k, offs); +} + +LPCIE_EXPORT(int32_t) L502_AsyncOutDac(t_l502_hnd hnd, uint32_t ch, double data, uint32_t flags) { + return X502_AsyncOutDac(hnd, ch, data, flags); +} + +LPCIE_EXPORT(int32_t) L502_AsyncOutDig(t_l502_hnd hnd, uint32_t val, uint32_t msk) { + return X502_AsyncOutDig(hnd, val, msk); +} + + +LPCIE_EXPORT(int32_t) L502_AsyncInDig(t_l502_hnd hnd, uint32_t* din) { + return X502_AsyncInDig(hnd, din); +} + + +LPCIE_EXPORT(int32_t) L502_AsyncGetAdcFrame(t_l502_hnd hnd, uint32_t flags, + uint32_t tout, double* data) { + return X502_AsyncGetAdcFrame(hnd, flags, tout, data); +} + +LPCIE_EXPORT(int32_t) L502_StreamsEnable(t_l502_hnd hnd, uint32_t streams) { + return X502_StreamsEnable(hnd, streams); +} + +LPCIE_EXPORT(int32_t) L502_StreamsDisable(t_l502_hnd hnd, uint32_t streams) { + return X502_StreamsDisable(hnd, streams); +} + +LPCIE_EXPORT(int32_t) L502_StreamsStart(t_l502_hnd hnd) { + return X502_StreamsStart(hnd); +} + +LPCIE_EXPORT(int32_t) L502_StreamsStop(t_l502_hnd hnd) { + return X502_StreamsStop(hnd); +} + +LPCIE_EXPORT(int32_t) L502_IsRunning(t_l502_hnd hnd) { + return X502_IsRunning(hnd); +} + +LPCIE_EXPORT(int32_t) L502_Recv(t_l502_hnd hnd, uint32_t* buf, uint32_t size, uint32_t tout) { + return X502_Recv(hnd, buf, size, tout); +} + +LPCIE_EXPORT(int32_t) L502_Send(t_l502_hnd hnd, const uint32_t* buf, uint32_t size, uint32_t tout) { + return X502_Send(hnd, buf, size, tout); +} + +LPCIE_EXPORT(int32_t) L502_ProcessAdcData(t_l502_hnd hnd, const uint32_t* src, double *dest, + uint32_t *size, uint32_t flags) { + return X502_ProcessAdcData(hnd, src, dest, size, flags); +} + +LPCIE_EXPORT(int32_t) L502_ProcessData(t_l502_hnd hnd, const uint32_t* src, uint32_t size, + uint32_t flags, double *adc_data, uint32_t *adc_data_size, + uint32_t *din_data, uint32_t *din_data_size) { + return X502_ProcessData(hnd, src, size, flags, adc_data, adc_data_size, + din_data, din_data_size); +} + + +LPCIE_EXPORT(int32_t) L502_ProcessDataWithUserExt(t_l502_hnd hnd, const uint32_t* src, uint32_t size, + uint32_t flags, double *adc_data, + uint32_t *adc_data_size, uint32_t *din_data, + uint32_t *din_data_size, + uint32_t *usr_data, uint32_t *usr_data_size) { + return X502_ProcessDataWithUserExt(hnd, src, size, flags, adc_data, adc_data_size, + din_data, din_data_size, usr_data, usr_data_size); +} + +LPCIE_EXPORT(int32_t) L502_PrepareData(t_l502_hnd hnd, const double* dac1, const double* dac2, + const uint32_t* digout, uint32_t size, int32_t flags, + uint32_t* out_buf) { + return X502_PrepareData(hnd, dac1, dac2, digout, size, flags, out_buf); +} + +LPCIE_EXPORT(int32_t) L502_GetRecvReadyCount(t_l502_hnd hnd, uint32_t *rdy_cnt) { + return X502_GetRecvReadyCount(hnd, rdy_cnt); +} + +LPCIE_EXPORT(int32_t) L502_GetSendReadyCount(t_l502_hnd hnd, uint32_t *rdy_cnt) { + return X502_GetSendReadyCount(hnd, rdy_cnt); +} + +LPCIE_EXPORT(int32_t) L502_GetNextExpectedLchNum(t_l502_hnd hnd, uint32_t *lch) { + return X502_GetNextExpectedLchNum(hnd, lch); +} + +LPCIE_EXPORT(int32_t) L502_PreloadStart(t_l502_hnd hnd) { + return X502_PreloadStart(hnd); +} + +LPCIE_EXPORT(int32_t) L502_OutCycleLoadStart(t_l502_hnd hnd, uint32_t size) { + return X502_OutCycleLoadStart(hnd, size); +} + + +LPCIE_EXPORT(int32_t) L502_OutCycleSetup(t_l502_hnd hnd, uint32_t flags) { + return X502_OutCycleSetup(hnd, flags); +} + +LPCIE_EXPORT(int32_t) L502_OutCycleStop(t_l502_hnd hnd, uint32_t flags) { + return X502_OutCycleStop(hnd, flags); +} + +LPCIE_EXPORT(int32_t) L502_SetDmaBufSize(t_l502_hnd hnd, uint32_t dma_ch, uint32_t size) { + return X502_SetStreamBufSize(hnd, dma_ch, size); +} + +LPCIE_EXPORT(int32_t) L502_SetDmaIrqStep(t_l502_hnd hnd, uint32_t dma_ch, uint32_t step) { + return X502_SetStreamStep(hnd, dma_ch, step); +} + +LPCIE_EXPORT(int32_t) L502_BfLoadFirmware(t_l502_hnd hnd, const char* filename) { + return X502_BfLoadFirmware(hnd, filename); +} + +LPCIE_EXPORT(int32_t) L502_BfCheckFirmwareIsLoaded(t_l502_hnd hnd, uint32_t *version) { + return X502_BfCheckFirmwareIsLoaded(hnd, version); +} + +LPCIE_EXPORT(int32_t) L502_BfMemRead(t_l502_hnd hnd, uint32_t addr, uint32_t* regs, + uint32_t size) { + return X502_BfMemRead(hnd, addr, regs, size); +} + +LPCIE_EXPORT(int32_t) L502_BfMemWrite(t_l502_hnd hnd, uint32_t addr, + const uint32_t* regs, uint32_t size) { + return X502_BfMemWrite(hnd, addr, regs, size); +} + +LPCIE_EXPORT(int32_t) L502_BfExecCmd(t_l502_hnd hnd, uint16_t cmd_code, uint32_t par, + const uint32_t* snd_data, uint32_t snd_size, + uint32_t* rcv_data, uint32_t rcv_size, uint32_t tout, uint32_t* recvd_size) { + return X502_BfExecCmd(hnd, cmd_code, par, snd_data, snd_size, rcv_data, rcv_size, + tout, recvd_size); +} + +LPCIE_EXPORT(int32_t) L502_FlashRead(t_l502_hnd hnd, uint32_t addr, uint8_t* data, + uint32_t size) { + return X502_FlashRead(hnd, addr, data, size); +} + +LPCIE_EXPORT(int32_t) L502_FlashWrite(t_l502_hnd hnd, uint32_t addr, + const uint8_t* data, uint32_t size) { + return X502_FlashWrite(hnd, addr, data, size); +} + +LPCIE_EXPORT(int32_t) L502_FlashErase(t_l502_hnd hnd, uint32_t addr, uint32_t size) { + return X502_FlashErase(hnd, addr, size); +} + +LPCIE_EXPORT(int32_t) L502_FlashWriteEnable(t_l502_hnd hnd) { + return X502_FlashWriteEnable(hnd); +} + +LPCIE_EXPORT(int32_t) L502_FlashWriteDisable(t_l502_hnd hnd) { + return X502_FlashWriteDisable(hnd); +} + +LPCIE_EXPORT(uint32_t) L502_GetDllVersion(void) { + return X502_GetLibraryVersion(); +} + + +LPCIE_EXPORT(const char*) L502_GetErrorString(int32_t err) { + return X502_GetErrorString(err); +} + +LPCIE_EXPORT(int32_t) L502_LedBlink(t_l502_hnd hnd) { + return X502_LedBlink(hnd); +} + +LPCIE_EXPORT(int32_t) L502_SetDigInPullup(t_l502_hnd hnd, uint32_t pullups) { + return X502_SetDigInPullup(hnd, pullups); +} + + +LPCIE_EXPORT(int32_t) L502_FpgaRegWrite(t_l502_hnd hnd, uint32_t reg, uint32_t val) { + return X502_FpgaRegWrite(hnd, reg, val); +} + +LPCIE_EXPORT(int32_t) L502_FpgaRegRead(t_l502_hnd hnd, uint32_t reg, uint32_t *val) { + return X502_FpgaRegRead(hnd, reg, val); +} + +LPCIE_EXPORT(int32_t) L502_ReloadDevInfo(t_l502_hnd hnd) { + return X502_ReloadDevInfo(hnd, 0); +} diff --git a/x502api-1.1.34/devs/l502/l502api_compat.h b/x502api-1.1.34/devs/l502/l502api_compat.h new file mode 100644 index 0000000..65cd765 --- /dev/null +++ b/x502api-1.1.34/devs/l502/l502api_compat.h @@ -0,0 +1,1834 @@ +/***************************************************************************//** + @file l502api_compat.h + Файл содержит определения типов и функций для L502, которые оставлены + для совместимости с версией библиотеки 1.0.x (до включения поддержки E502). + Все эти определения имеют аналог в x502api.h, а функции вызывают напрямую + аналогичные функции X502_xxx из библиотеки x502api + + @note Не включены недокументированные функцие, такие как L502_OpenByListItem, + LPCIE_GetDevInfoList и LPCIE_FreeDevInfoList + @author Borisov Alexey + ******************************************************************************/ + +#ifndef L502API_COMPAT_H +#define L502API_COMPAT_H + + +#include "x502api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LPCIE_EXPORT(type) X502_EXPORT(type) + +/***************************************************************************//** + @addtogroup const_list Константы и перечисления. + @{ + *****************************************************************************/ + +/** Максимальное количество логических каналов в таблице*/ +#define L502_LTABLE_MAX_CH_CNT 256 +/** Количество диапазонов для измерения напряжений */ +#define L502_ADC_RANGE_CNT 6 + +/** Максимальное значение для аппаратного усреднения по логическому каналу */ +#define L502_LCH_AVG_SIZE_MAX 128 +/** Максимальное значения делителя частоты АЦП */ +#define L502_ADC_FREQ_DIV_MAX (1024*1024) +/** Максимальное значение делителя частоты синхронного цифрового ввода */ +#define L502_DIN_FREQ_DIV_MAX (1024*1024) + +/** Максимальное значение межкадровой задержки для АЦП */ +#define L502_ADC_INTERFRAME_DELAY_MAX (0x1FFFFF) + +/** Таймаут по умолчанию для выполнения команды к BlackFin*/ +#define L502_BF_CMD_DEFAULT_TOUT 500 + +/** Код АЦП, соответствующий максимальному значению шкалы */ +#define L502_ADC_SCALE_CODE_MAX 6000000 +/** Код ЦАП, соответствующий максимальному значению шкалы */ +#define L502_DAC_SCALE_CODE_MAX 30000 + +/** Максимальное количество символов в строке с названием устройства */ +#define L502_DEVNAME_SIZE 32 +/** Максимальное количество символов в строке с серийным номером */ +#define L502_SERIAL_SIZE 32 + +/** Максимально возможное значение внешней опорной частоты */ +#define L502_EXT_REF_FREQ_MAX 2000000 + + +/** Размер пользовательской области Flash-памяти */ +#define L502_FLASH_USER_SIZE 0x100000 + +/** Стандартный таймаут на выполнение запроса к BlackFin в мс */ +#define L502_BF_REQ_TOUT 500 + + +/** Диапазон ЦАП в вольтах */ +#define L502_DAC_RANGE 5. + +/** Количество каналов ЦАП */ +#define L502_DAC_CH_CNT 2 + + +/** слово в потоке, означающее, что произошло переполнение */ +#define L502_STREAM_IN_MSG_OVERFLOW 0x01010000 + + +/** Коды ошибок библиотеки */ +typedef enum { + /** Функция выполнена без ошибок */ + L502_ERR_OK = 0, + /** В функцию передан недействительный описатель модуля */ + L502_ERR_INVALID_HANDLE = -1, + /** Ошибка выделения памяти */ + L502_ERR_MEMORY_ALLOC = -2, + /** Попытка открыть уже открытое устройство */ + L502_ERR_ALREADY_OPENED = -3, + /** Устройство с заданными параметрами не найдено в системе */ + L502_ERR_DEVICE_NOT_FOUND = -4, + /** Доступ к устройству запрещен (Как правило из-за того, что устройство + уже открыто в другой программе) */ + L502_ERR_DEVICE_ACCESS_DENIED = -5, + /** Ошибка открытия устройства */ + L502_ERR_DEVICE_OPEN = -6, + /** В функцию передан недействительный указатель */ + L502_ERR_INVALID_POINTER = -7, + /** Функция не может быть выполнена при запущенном потоке сбора данных */ + L502_ERR_STREAM_IS_RUNNING = -8, + /** Ошибка чтения данных синхронного ввода */ + L502_ERR_RECV = -9, + /** Ошибка записи данных для синхронного вывода */ + L502_ERR_SEND = -10, + /** Произошло переполнение внутреннего буфера для потока синхронного ввода */ + L502_ERR_STREAM_OVERFLOW = -11, + /** Неизвестное сообщение в потоке синхронного ввода */ + L502_ERR_UNSUP_STREAM_MSG = -12, + /** Ошибка создания системного мьютекса */ + L502_ERR_MUTEX_CREATE = -13, + /** Неверный описатель мьютекса */ + L502_ERR_MUTEX_INVALID_HANDLE = -14, + /** Истекло время ожидания освобождения мьютекса */ + L502_ERR_MUTEX_LOCK_TOUT = -15, + /** Ошибка освобождения мьютекса */ + L502_ERR_MUTEX_RELEASE = -16, + /** Недостаточно системных ресурсов */ + L502_ERR_INSUFFICIENT_SYSTEM_RESOURCES= -17, + /** Данная возможность еще не реализована */ + L502_ERR_NOT_IMPLEMENTED = -18, + /** Недостаточный размер массива */ + L502_ERR_INSUFFICIENT_ARRAY_SIZE = -19, + /** Ошибка чтения регистра FPGA */ + L502_ERR_FPGA_REG_READ = -20, + /** Ошибка записи регистра FPGA */ + L502_ERR_FPGA_REG_WRITE = -21, + /** Сбор данных уже остановлен */ + L502_ERR_STREAM_IS_NOT_RUNNING = -22, + /** Задан неверный размер логической таблицы */ + L502_ERR_INVALID_LTABLE_SIZE = -102, + /** Задан неверный номер логического канала */ + L502_ERR_INVALID_LCH_NUMBER = -103, + /** Неверно задано значение диапазона АЦП */ + L502_ERR_INVALID_LCH_RANGE = -104, + /** Неверно задан режим измерения для логического канала */ + L502_ERR_INVALID_LCH_MODE = -105, + /** Неверно задан номер физического канала при настройке логического */ + L502_ERR_INVALID_LCH_PHY_NUMBER = -106, + /** Неверно задан размер усреднения для логического канала */ + L502_ERR_INVALID_LCH_AVG_SIZE = -107, + /** Неверно задан делитель частоты сбора данных АЦП */ + L502_ERR_INVALID_ADC_FREQ_DIV = -108, + /** Неверно задан делитель частоты синхронного ввода цифровых линий */ + L502_ERR_INVALID_DIN_FREQ_DIV = -108, + /** Неверно задан режим работы модуля L502 */ + L502_ERR_INVALID_MODE = -109, + /** Неверный номер канала ЦАП */ + L502_ERR_INVALID_DAC_CHANNEL = -110, + /** Неверный код выбора опорной частоты синхронизации */ + L502_ERR_INVALID_REF_FREQ = -111, + /** Неверно задано значение межкадровой задержки */ + L502_ERR_INVALID_INTERFRAME_DELAY = -112, + /** Неверно задан режим синхронизации */ + L502_ERR_INVALID_SYNC_MODE = -113, + /** Неверно задан номер канала DMA */ + L502_ERR_INVALID_DMA_CH = -114, + + /** Ошибка захвата опорной частоты синхронизации */ + L502_ERR_REF_FREQ_NOT_LOCKED = -131, + /** Управляющий запрос к драйверу завершен с ошибкой */ + L502_ERR_IOCTL_FAILD = -132, + /** Истек таймаут ожидания завершения выполнения управляющего запроса к драйверу */ + L502_ERR_IOCTL_TIMEOUT = -133, + /** Ошибка получения информации о устройстве от драйвера */ + L502_ERR_GET_INFO = -134, + /** За время ожидания не было считано новое слово с цифровых линий */ + L502_ERR_DIG_IN_NOT_RDY = -135, + /** Принято недостаточно слов от модуля */ + L502_ERR_RECV_INSUFFICIENT_WORDS = -136, + /** Попытка выполнить операцию, требующую наличие ЦАП, при его отсутствии */ + L502_ERR_DAC_NOT_PRESENT = -137, + /** Неверный номер канала в обрабатываемом потоке синхронного ввода */ + L502_ERR_PROC_INVALID_CH_NUM = -140, + /** Неверный код диапазона в обрабатываемом потоке синхронного ввода */ + L502_ERR_PROC_INVALID_CH_RANGE = -141, + /** Задан неверный адрес во Flash-памяти */ + L502_ERR_FLASH_INVALID_ADDR = -142, + /** Задан неверный размер блока данных при работе с Flash-памятью */ + L502_ERR_FLASH_INVALID_SIZE = -143, + /** Истек таймаут ожидания завершения записи во Flash-память */ + L502_ERR_FLASH_WRITE_TOUT = -144, + /** Истек таймаут ожидания завершения стирания блока Flash-памяти */ + L502_ERR_FLASH_ERASE_TOUT = -145, + /** Заданная область для стирания Flash-памяти нарушает границу блока в 4 Кбайт */ + L502_ERR_FLASH_SECTOR_BOUNDARY = -146, + /** Не удалось открыть файл прошивки BlackFin */ + L502_ERR_LDR_FILE_OPEN = -180, + /** Ошибка чтения из фала прошивки BlackFin */ + L502_ERR_LDR_FILE_READ = -181, + /** Неверный формат файла прошивки BlackFin */ + L502_ERR_LDR_FILE_FORMAT = -182, + /** Используются возможность LDR-файла, недоступные при записи прошивки + BlackFin по HDMA */ + L502_ERR_LDR_FILE_UNSUP_FEATURE = -183, + /** Неверный стартовый адрес программы в прошивке BlackFin */ + L502_ERR_LDR_FILE_UNSUP_STARTUP_ADDR = -184, + /** Истек таймаут выполнения запроса на чтения/запись памяти BlackFin */ + L502_ERR_BF_REQ_TIMEOUT = -185, + /** Команда для BlackFin все еще находится в процессе обработки */ + L502_ERR_BF_CMD_IN_PROGRESS = -186, + /** Истекло время выполнения управляющей команды процессором BlackFin */ + L502_ERR_BF_CMD_TIMEOUT = -187, + /** Возвращено недостаточно данных в ответ на команду к BlackFin */ + L502_ERR_BF_CMD_RETURN_INSUF_DATA = -188, + /** Истек таймаут ожидания готовности процессора BlackFin к записи прошивки */ + L502_ERR_BF_LOAD_RDY_TOUT = -189, + /** Попытка выполнить операцию для которой нужен сигнальный процессор при + отсутствии сигнального процессора в модуле */ + L502_ERR_BF_NOT_PRESENT = -190, + /** Неверный адрес памяти BlackFin при записи или чтении по HDMA */ + L502_ERR_BF_INVALID_ADDR = -191, + /** Неверный размер данных, передаваемых с управляющей командой в BlackFin */ + L502_ERR_BF_INVALID_CMD_DATA_SIZE = -192 +} t_lpcie_errs; + +/** Флаги, управляющие поиском присутствующих модулей */ +typedef enum { + /** Признак, что нужно вернуть серийные номера только тех устройств, + которые еще не открыты */ + L502_GETDEVS_FLAGS_ONLY_NOT_OPENED = X502_GETDEVS_FLAGS_ONLY_NOT_OPENED +} t_l502_getdevs_flags; + + + +/** @brief Флаги для управления цифровыми выходами. + + Флаги управления цифровыми выходами. Могут быть объединены через логическое + “ИЛИ” со значениями цифровых выходов при асинхронном выводе с помощью + L502_AsyncOutDig() или переданы в L502_PrepareData() при синхронном выводе.*/ +typedef enum { + L502_DIGOUT_WORD_DIS_H = X502_DIGOUT_WORD_DIS_H, /**< Запрещение (перевод в третье состояние) + старшей половины цифровых выходов */ + L502_DIGOUT_WORD_DIS_L = X502_DIGOUT_WORD_DIS_L /**< Запрещение младшей половины + цифровых выходов */ +} t_l502_digout_word_flags; + + +/** Константы для выбора опорной частоты */ +typedef enum { + L502_REF_FREQ_2000KHZ = X502_REF_FREQ_2000KHZ, /**< Частота 2МГц */ + L502_REF_FREQ_1500KHZ = X502_REF_FREQ_1500KHZ /**< Частота 1.5МГц */ +} t_l502_ref_freq; + + +/** Диапазоны измерения для канала АЦП */ +typedef enum { + L502_ADC_RANGE_10 = X502_ADC_RANGE_10, /**< Диапазон +/-10V */ + L502_ADC_RANGE_5 = X502_ADC_RANGE_5, /**< Диапазон +/-5V */ + L502_ADC_RANGE_2 = X502_ADC_RANGE_2, /**< Диапазон +/-2V */ + L502_ADC_RANGE_1 = X502_ADC_RANGE_1, /**< Диапазон +/-1V */ + L502_ADC_RANGE_05 = X502_ADC_RANGE_05, /**< Диапазон +/-0.5V */ + L502_ADC_RANGE_02 = X502_ADC_RANGE_02 /**< Диапазон +/-0.2V */ +} t_l502_adc_range; + +/** Режим измерения для логического канала */ +typedef enum { + L502_LCH_MODE_COMM = X502_LCH_MODE_COMM, /**< Измерение напряжения относительно общей земли */ + L502_LCH_MODE_DIFF = X502_LCH_MODE_DIFF, /**< Дифференциальное измерение напряжения */ + L502_LCH_MODE_ZERO = X502_LCH_MODE_ZERO /**< Измерение собственного нуля */ +} t_l502_lch_mode; + +/** @brief Режимы синхронизации. + + Режимы задания источника частоты синхронизации и признака начала + синхронного ввода-вывода */ +typedef enum { + L502_SYNC_INTERNAL = 0, /**< Внутренний сигнал */ + L502_SYNC_EXTERNAL_MASTER = 1, /**< От внешнего мастера по разъему синхронизации */ + L502_SYNC_DI_SYN1_RISE = 2, /**< По фронту сигнала DI_SYN1 */ + L502_SYNC_DI_SYN2_RISE = 3, /**< По фронту сигнала DI_SYN2 */ + L502_SYNC_DI_SYN1_FALL = 6, /**< По спаду сигнала DI_SYN1 */ + L502_SYNC_DI_SYN2_FALL = 7 /**< По спаду сигнала DI_SYN2 */ +} t_l502_sync_mode; + +/** Флаги, управляющие обработкой принятых данных */ +typedef enum { + /** Признак, что нужно преобразовать значения АЦП в вольты */ + L502_PROC_FLAGS_VOLT = X502_PROC_FLAGS_VOLT, + /** Признак, что не нужно проверять совпадение номеров каналов + в принятых данных с каналами из логической таблицы. + Может использоваться при нестандартной прошивке BlackFin + при передаче в ПК не всех данных. */ + L502_PROC_FLAGS_DONT_CHECK_CH = X502_PROC_FLAGS_DONT_CHECK_CH +} t_l502_proc_flags; + + +/** Флаги для обозначения синхронных потоков данных */ +typedef enum { + L502_STREAM_ADC = X502_STREAM_ADC, /**< Поток данных от АЦП */ + L502_STREAM_DIN = X502_STREAM_DIN, /**< Поток данных с цифровых входов */ + L502_STREAM_DAC1 = X502_STREAM_DAC1, /**< Поток данных первого канала ЦАП */ + L502_STREAM_DAC2 = X502_STREAM_DAC2, /**< Поток данных второго канала ЦАП */ + L502_STREAM_DOUT = X502_STREAM_DOUT, /**< Поток данных на цифровые выводы */ + /** Объединение всех флагов, обозначающих потоки данных на ввод */ + L502_STREAM_ALL_IN = X502_STREAM_ALL_IN, + /** Объединение всех флагов, обозначающих потоки данных на вывод */ + L502_STREAM_ALL_OUT = X502_STREAM_ALL_OUT +} t_l502_streams; + +/** Константы, определяющие тип передаваемого отсчета из ПК в модуль */ +typedef enum { + L502_STREAM_OUT_WORD_TYPE_DOUT = X502_STREAM_OUT_WORD_TYPE_DOUT, /**< Цифровой вывод */ + L502_STREAM_OUT_WORD_TYPE_DAC1 = X502_STREAM_OUT_WORD_TYPE_DAC1, /**< Код для 1-го канала ЦАП */ + L502_STREAM_OUT_WORD_TYPE_DAC2 = X502_STREAM_OUT_WORD_TYPE_DAC2 /**< Код для 2-го канала ЦАП */ +} t_l502_stream_out_wrd_type; + +/** Режим работы модуля L502 */ +typedef enum { + L502_MODE_FPGA = X502_MODE_FPGA, /**< Все потоки данных передаются через ПЛИС минуя + сигнальный процессор BlackFin */ + L502_MODE_DSP = X502_MODE_DSP, /**< Все потоки данных передаются через сигнальный + процессор, который должен быть загружен + прошивкой для обработки этих потоков */ + L502_MODE_DEBUG = X502_MODE_DEBUG /**< Отладочный режим */ +} t_l502_mode; + +/** @brief Номера каналов ЦАП. + + Номер каналов ЦАП для указания в L502_AsyncOutDac() */ +typedef enum { + L502_DAC_CH1 = X502_DAC_CH1, /**< Первый канал ЦАП */ + L502_DAC_CH2 = X502_DAC_CH2 /**< Второй канал ЦАП */ +} t_l502_dac_ch; + +/** @brief Флаги, используемые при выводе данных на ЦАП. + + Флаги, комбинацию которых можно передать в L502_AsyncOutDac() или + L502_PrepareData(), чтобы определить действия, которые должны выполнить + эти функции с переданным значением перед выводом их на ЦАП */ +typedef enum { + /** Указывает, что значение задано в Вольтах и при выводе его нужно + перевести его в коды ЦАП. Если флаг не указан, то считается, что значение + изначально в кодах */ + L502_DAC_FLAGS_VOLT = X502_DAC_FLAGS_VOLT, + /** Указывает, что нужно применить калибровочные коэффициенты перед + выводом значения на ЦАП. */ + L502_DAC_FLAGS_CALIBR = X502_DAC_FLAGS_CALIBR +} t_l502_dacout_flags; + + +/** Номера каналов DMA */ +typedef enum { + L502_DMA_CH_IN = X502_STREAM_CH_IN, /**< Общий канал DMA на ввод */ + L502_DMA_CH_OUT = X502_STREAM_CH_OUT /**< Общий канал DMA на вывод */ +} t_l502_dma_ch; + + +/** @brief Цифровые линии, на которых можно включить подтягивающие резисторы + + Флаги, определяющие на каких цифровых входах должны быть включены + подтягивающие резисторы */ +typedef enum { + L502_PULLUPS_DI_H = X502_PULLUPS_DI_H, /**< Старшая половина цифровых входов */ + L502_PULLUPS_DI_L = X502_PULLUPS_DI_L, /**< Младшая половина цифровых входов */ + L502_PULLUPS_DI_SYN1 = X502_PULLUPS_DI_SYN1, /**< Линия SYN1 */ + L502_PULLUPS_DI_SYN2 = X502_PULLUPS_DI_SYN2 /**< Линия SYN2 */ +} t_l502_pullups; + + +/** Флаги, определяющие наличие опций в модуле */ +typedef enum { + /** Признак наличия двухканального канального ЦАП */ + L502_DEVFLAGS_DAC_PRESENT = X502_DEVFLAGS_DAC_PRESENT, + /** Признак наличия гальваноразвязки */ + L502_DEVFLAGS_GAL_PRESENT = X502_DEVFLAGS_GAL_PRESENT, + /** Признак наличия сигнального процессора BlackFin */ + L502_DEVFLAGS_BF_PRESENT = X502_DEVFLAGS_BF_PRESENT, + /** Признак, что во Flash-памяти присутствует информация о модуле */ + L502_DEVFLAGS_FLASH_DATA_VALID = X502_DEVFLAGS_FLASH_DATA_VALID, + /** Признак, что во Flash-памяти присутствуют действительные калибровочные + коэффициенты АЦП */ + L502_DEVFLAGS_FLASH_ADC_CALIBR_VALID = X502_DEVFLAGS_FLASH_ADC_CALIBR_VALID, + /** Признак, что во Flash-памяти присутствуют действительные калибровочные + коэффициенты ЦАП */ + L502_DEVFLAGS_FLASH_DAC_CALIBR_VALID = X502_DEVFLAGS_FLASH_DAC_CALIBR_VALID +} t_l502_dev_flags; + + +/** @brief Флаги для режима циклического вывода + + Данные флаги могут быть переданы в L502_OutCycleSetup() и L502_OutCycleStop() */ +typedef enum { + /** Флаг указывает, что останов или смена сигнала могут произойти без ожидания + конца цикла предыдущего сигнала. Это позволяет выполнить переключение + быстрее (однако все равно может быть поставлено на передачу до 256 КСемплов, + которые должны будут быть переданы), но точка смены или останова + может быть в любом месте периода */ + L502_OUT_CYCLE_FLAGS_FORCE = X502_OUT_CYCLE_FLAGS_FORCE +} t_l502_out_cycle_flags; + +/** @} */ + +/***************************************************************************//** + @addtogroup type_list Типы данных. + @{ + *****************************************************************************/ + +/** @brief Описатель модуля. + + Непрозрачный указатель на структуру, + содержащую информацию о настройках модуля и текущем соединении с ним. + Пользовательской программе не доступны поля структуры напрямую, а только + через функции библиотеки. + Функции управления модулем принимают описатель модуля своим первым параметром. + Описатель модуля создается с помощью L502_Create() и в конце работы + освобождается с помощью L502_Free(). */ +typedef t_x502_hnd t_l502_hnd; + + + +/** @brief Списко серийный номеров + + Тип определяет массив серийных номеров для количестава модулей, определяемого + на этапе работы программы. */ +typedef t_x502_serial_list t_l502_serial_list; + + +/** @brief Калибровочные коэффициенты диапазона. + + Структура содержит калибровочные значения смещения нуля и коэффициента + шкалы для одного диапазона АЦП или ЦАП.Результирующее значение АЦП + вычисляется как (val-offs)*k, где val - некалиброванное значение */ +typedef struct { + double offs; /**< смещение нуля */ + double k; /**< коэффициент шкалы */ +} t_l502_cbr_coef; + + +/** @brief Калибровочные коэффициенты модуля. + + Структура, содержащая все калибровочные коэффициенты, которые + используются модулем L502 */ +typedef struct { + /** Калибровочные коэффициенты АЦП */ + t_l502_cbr_coef adc[L502_ADC_RANGE_CNT]; + uint32_t res1[64]; /**< Резерв */ + /** Калибровочные коэффициенты ЦАП */ + t_l502_cbr_coef dac[L502_DAC_CH_CNT]; + uint32_t res2[20]; /**< Резерв */ +} t_l502_cbr; + +/** @brief Информация о модуле L502. + + Структура, содержащая постоянную информация о модуле L502, которая как правило + не изменяется после открытия */ +typedef struct { + char name[L502_DEVNAME_SIZE]; /**< Название устройства ("L502") */ + char serial[L502_SERIAL_SIZE]; /**< Серийный номер */ + uint32_t devflags; /**< Флаги из #t_l502_dev_flags, описывающие наличие + в модуле определенных опций */ + uint16_t fpga_ver; /**< Версия ПЛИС (старший байт - мажорная, младший - минорная) */ + uint8_t plda_ver; /**< Версия ПЛИС, управляющего аналоговой частью */ + uint8_t board_rev; /**< Ревизия платы */ + uint8_t res[120]; /**< Резерв */ + t_l502_cbr cbr; /**< Заводские калибровочные коэффициенты (из Flash-памяти) */ +} t_l502_info; + +/** @} */ + + +/** @addtogroup func_list Функции. + @{ **/ + +/***************************************************************************//** + @addtogroup func_hnd Функции для создания и освобождения описателя модуля. + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Создание описателя модуля. + + Создание описателя модуля, для последующей работы с модулем L502. + В случае успешного выделения памяти инициализирует поля описателя + значениями по-умолчанию. + @return NULL в случае ошибки, иначе - описатель модуля +*******************************************************************************/ +LPCIE_EXPORT(t_l502_hnd) L502_Create(void); + +/***************************************************************************//** + @brief Освобождение описателя модуля. + + Освобождение памяти, выделенной под описатель модуля с помощью L502_Create(). + После этого описатель уже использовать нельзя, независимо от возвращенного + значения! + @param[in] hnd Описатель устройства + @return Код ошибки +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_Free(t_l502_hnd hnd); +/** @} */ + + +/***************************************************************************//** + @addtogroup func_open Функции для открытия и получения информации о модуле. + @{ +*******************************************************************************/ + + +/***************************************************************************//** + @brief Закрытие соединения с модулем. + + Функция разрывает соединение с модулем L502, если оно было ранее установлено + (в противном случае ничего не делает). + Описатель модуля не освобождается. + Память под описатель модуля должна быть освобождена вызовом L502_Free(). + @param[in] hnd Описатель модуля. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_Close(t_l502_hnd hnd); + + +/***************************************************************************//** + @brief Получение информации о модуле. + + Получение информации о модуле L502, с которым установлена связь. + @param[in] hnd Описатель модуля. + @param[out] info Информация о модуле (смотри описание типа #t_l502_info). + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetDevInfo(t_l502_hnd hnd, t_l502_info* info); + + + +/** @} */ + +/***************************************************************************//** + @addtogroup func_config Функции для изменения настроек модуля + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Передача установленных настроек в модуль. + + Функция выполняет запись текущих настроек (которые были установлены + с помощью функций L502_SetXXX) в модуль. + Должна вызываться перед запуском потока данных. + @param[in] hnd Описатель модуля. + @param[in] flags Флаги (резерв - должно быть равно 0). + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_Configure(t_l502_hnd hnd, uint32_t flags); + +/***************************************************************************//** + @brief Установка параметров логического канала. + + Функция устанавливает параметры заданного логического канала в логической + таблице АЦП. + @param[in] hnd Описатель модуля. + @param[in] lch Номер логического канала. + (от 0 до #L502_LTABLE_MAX_CH_CNT-1) + @param[in] phy_ch Номер физического канала АЦП, начиная с 0 + (0-15 для дифференциального режима, + 0-31 для режима с общей землей) + @param[in] mode Режим измерения канал АЦП (значение типа #t_l502_lch_mode) + @param[in] range Диапазон измерения канала (значение типа #t_l502_adc_range) + @param[in] avg Коэффициент усреднения по каналу. Нулевое значение + соответствует значению коэффициента, определенного + библиотекой. Для явного задания коэффициента усреднения + нужно перед значение от 1 (отсутствие усреднения) до + #L502_LCH_AVG_SIZE_MAX. + В случае если значение усреднения превышает делитель + частоты, то это значение будет скорректировано + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetLChannel(t_l502_hnd hnd, uint32_t lch, uint32_t phy_ch, + uint32_t mode, uint32_t range, uint32_t avg); + +/***************************************************************************//** + @brief Установка количества логических каналов. + + Функция устанавливает количество логических каналов в логической таблице АЦП. + @param[in] hnd Описатель модуля + @param[in] lch_cnt Количество логических каналов + (от 1 до #L502_LTABLE_MAX_CH_CNT) + @return Код ошибки +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetLChannelCount(t_l502_hnd hnd, uint32_t lch_cnt); + + +/***************************************************************************//** + @brief Получение количества логических каналов. + + Функция возвращает установленное ранее с помощью L502_SetLChannelCount() + количество логических каналов в управляющей таблице АЦП. + @param[in] hnd Описатель модуля + @param[out] lch_cnt Количество логических каналов + @return Код ошибки +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetLChannelCount(t_l502_hnd hnd, uint32_t* lch_cnt); + +/***************************************************************************//** + @brief Установка делителя частоты сбора для АЦП. + + Частота сбора АЦП получается как результат деления опорной частоты + синхронизации (как в случае внешней, так и внутренней) на делитель, + устанавливаемый этой функцией. + + Альтернативой этой функции служит L502_SetAdcFreq(), которая рассчитывает + этот делитель на основании переданной требуемой частоты сбора АЦП. + + @param[in] hnd Описатель модуля. + @param[in] adc_freq_div Делитель частоты АЦП (от 1 до #L502_ADC_FREQ_DIV_MAX). + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetAdcFreqDivider(t_l502_hnd hnd, uint32_t adc_freq_div); + +/***************************************************************************//** + @brief Установка значения межкадровой задержки для АЦП. + + Функция устанавливает межкадровую задержку для АЦП, то есть количество + периодов опорной частоты синхронизации, которое будет пропущено после + проведения измерения последнего канала логической таблицы до проведения + измерения, соответствующего первому логическому каналу следующего кадра. + + Альтернативой может являться функция L502_SetAdcFreq(), которая рассчитывает + значение межкадровой задержки по заданным параметрам частоты сбора и частоты + следования кадров (частоты сбора на логический канал). + + @param[in] hnd Описатель модуля. + @param[in] delay Значение межкадровой задержки (от 0 до + #L502_ADC_INTERFRAME_DELAY_MAX) + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetAdcInterframeDelay(t_l502_hnd hnd, uint32_t delay); + +/***************************************************************************//** + @brief Установка делителя частоты синхронного ввода с цифровых линий. + + Частота синхронного ввода данных с цифровых входов получается как результат + деления опорной частоты синхронизации на делитель, устанавливаемый этой + функцией. + + Альтернативой этой функции служит L502_SetDinFreq(), которая рассчитывает + этот делитель на основании переданной требуемой частоты синхронного ввода + с цифровых линий. + + @param[in] hnd Описатель модуля. + @param[in] din_freq_div Делитель частоты АЦП (от 1 до #L502_DIN_FREQ_DIV_MAX). + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetDinFreqDivider(t_l502_hnd hnd, uint32_t din_freq_div); + + +/***************************************************************************//** + @brief Установка частоты сбора АЦП. + + Функция подбирает делитель частоты АЦП так, чтобы полученная частота сбора + была наиболее близка к указанной в параметре f_acq. Функция возвращает + в этом же параметре реальную частоту, которая была установлена. + + Так же функция может подобрать значение межкадровой задержки так, чтобы + частота следования кадров (частота сбора на логический канал) была наиболее + близка к указанному значению. + Для этого следует передать требуемое значение в переменной f_frame (в ней + также по завершению будет возвращено значение установленной частоты). + Если в качестве f_frame передан нулевой указатель, то будет установлена + нулевая межкадровая задержка. + + Если необходимо изменить значение опорной частоты, то данная функция должна + быть вызвана после L502_SetRefFreq(), в противном случае полученные делители + будут давать неверное значение частоты. + + Если устанавливается частота кадров, то функция должна вызываться после + того, как было заданно нужное количество логических каналов в управляющей + таблице с помощью L502_SetLChannelCount(). + + При использовании внешней опорной частоты синхронизации эта функция будет + давать верный результат, только если эта внешняя частота соответствует + значению, установленному с помощью L502_SetRefFreq(). + + + @param[in] hnd Описатель модуля. + @param[in,out] f_acq На входе принимает требуемое значения частоты сбора + АЦП в Герцах. На выходе возвращает реально + установленное значение частоты. + @param[in,out] f_frame На входе принимает требуемое значение частоты сбора + кадров (частоты сбора на логический канал) АЦП + в Герцах. На выходе возвращает реально + установленное значение. Если передан нулевой + указатель, то устанавливает максимальную частоту + сбора кадров (нулевую межкадровую задержку). + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetAdcFreq(t_l502_hnd hnd, double *f_acq, double *f_frame); + + + + + + +/***************************************************************************//** + @brief Установка частоты синхронного ввода с цифровых входов. + + Функция подбирает делитель частоты ввода значений с цифровых входов так, чтобы + полученная частота ввода была наиболее близка к указанной. Функция возвращает + в этом же параметре реальную частоту, которая была установлена. + + Если необходимо изменить значение опорной частоты синхронизации, то данная + функция должна быть вызвана после L502_SetRefFreq(), в противном случае + полученный делитель будет давать неверное значение частоты. + + При использовании внешней опорной частоты синхронизации эта функция будет + давать верный результат, только если эта внешняя частота соответствует + значению, установленному с помощью L502_SetRefFreq(). + + @param[in] hnd Описатель модуля. + @param[in,out] f_din На входе принимает требуемое значения частоты ввода + с цифровых входов в Герцах. На выходе возвращает + реально установленное значение частоты. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetDinFreq(t_l502_hnd hnd, double *f_din); + + +/***************************************************************************//** + @brief Получить текущие значения частот сбора АЦП + + Функция возвращает ткущие установленные для модуля значения частоты сбора + и частоты кадров АЦП (частоты на логический канал) в Герцах, которые были + установлены до этого с помощью L502_SetAdcFreq() или с помощью функций + L502_SetAdcFreqDivider()/L502_SetAdcInterframeDelay(). + + @param[in] hnd Описатель модуля. + @param[out] f_acq Если не NULL, то на выходе возвращается текущее + значение частоты сбора АЦП. + @param[out] f_frame Если не NULL, то на выходе возвращается текущее + значение частоты кадров АЦП. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetAdcFreq(t_l502_hnd hnd, double *f_acq, double *f_frame); + + + +/***************************************************************************//** + @brief Установка значения опорной частоты синхронизации. + + Функция задает значение опорной частоты синхронизации, от которой получаются + все частоты синхронного ввода/вывода посредством деления на определенный + делитель. + + При использовании внутренней опорной частоты доступны два значения: + 2МГц и 1.5Мгц (2МГц является значением по-умолчанию), для задания которых + можно использовать константы из #t_l502_ref_freq. + + При использовании внешней опорной частоты для того, чтобы можно было + правильно установить нужную частоту сбора функциями + L502_SetAdcFreq()/L502_SetDinFreq() и правильно были установлены значения + по-умолчанию для размера буфера DMA и шага прерываний, нужно с помощью этой + функции задать реально подаваемое значение внешней опорной частоты в Герцах. + + @param[in] hnd Описатель модуля. + @param[in] freq Значение из #t_l502_ref_freq, которое задает + выбранную опорную частоту. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetRefFreq(t_l502_hnd hnd, uint32_t freq); + +/***************************************************************************//** + @brief Установка режима генерации частоты синхронизации. + + Функция устанавливает кто будет генератором опорной частоты синхронизации - + сам модуль или будет использоваться внешний сигнал. + + В режиме #L502_SYNC_INTERNAL модуль сам будет генерировать для себя + частоту синхронизации с частотой, заданной L502_SetRefFreq(). + При этом запуск генерации будет осуществлен по вызову L502_StreamsStart() + или по условию, заданому в L502_SetSyncStartMode(), а останов + по L502_StreamsStop(). + + В остальных режимах сбор будет осуществляться по внешнему сигналу + синхронизации. + + @param[in] hnd Описатель модуля. + @param[in] sync_mode Значение из #t_l502_sync_mode, определяющее кто + будет источником частоты синхронизации. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetSyncMode(t_l502_hnd hnd, uint32_t sync_mode); + +/***************************************************************************//** + @brief Установка режима запуска частоты синхронизации. + + Функция устанавливает условие запуска синхронного ввода/вывода данных. + + Если с помощью L502_SetSyncMode() установлен режим синхронизации + #L502_SYNC_INTERNAL, то по заданному данной функцией условию модуль начнет + генерировать частоту синхронизации, в противном случае по заданному условию + модуль начнет использовать внешне заданную частоту синхронизации + (т.е. до выполнения условия сигнал синхронизации на заданном входе будет + игнорироваться). + + Режимы задания условия запуска синхронизации имеют те же значения, + что и режимы задания самой частоты (см. тип #t_l502_sync_mode). + В случае #L502_SYNC_INTERNAL запуск осуществляется при выполнении функции + L502_StreamsStart(), в противном случае - после выполнения + L502_StreamsStart() модуль начинает ожидать заданного данной функцией условия. + Т.е. даже при задании внешних источников синхронизации, все равно необходимо + вызывать L502_StreamsStart(). + + @param[in] hnd Описатель модуля. + @param[in] sync_start_mode Значение из #t_l502_sync_mode, определяющее + условие запуска частоты синхронизации. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetSyncStartMode(t_l502_hnd hnd, uint32_t sync_start_mode); + + +/***************************************************************************//** + @brief Установить режим работы модуля. + + Функция устанавливает режим работы модуля, который определяет будет ли + потоки данных обрабатывать ПЛИС или сигнальный процессор BlackFin. + При включении питания модулем всегда управляет ПЛИС. + После загрузки прошивки с помощью L502_BfLoadFirmware() модуль переходит + в режим управления сигнальным процессором. + + Данная функция может использоваться для ручной установки режима, + например, для возврата в режим управления ПЛИС или для переключения в режим + управления сигнальным процессором, если прошивка уже была загружена + (например, через JTAG интерфейс при отладке). + + @param[in] hnd Описатель модуля. + @param[in] mode Режим работы модуля из #t_l502_mode. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetMode(t_l502_hnd hnd, uint32_t mode); +/***************************************************************************//** + @brief Получение текущего режима работы модуля. + + Функция возвращает текущий режим работы модуля. + @param[in] hnd Описатель модуля. + @param[out] mode В данном параметре возвращается текущий режим + работы модуля (из #t_l502_mode). + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetMode(t_l502_hnd hnd, uint32_t* mode); +/***************************************************************************//** + @brief Установить коэффициенты для калибровки значений АЦП. + + Функция записывает в ПЛИС коэффициенты для калибровки значений АЦП. + При открытии модуля, библиотека считывает калибровочные коэффициенты из + защищенной области Flash-памяти модуля и записывает их в ПЛИС для + выполнения калибровки на лету. + + Результирующее значение АЦП вычисляется по формуле (val+offs)*k, где val - + некалиброванное значение. + + Данная функция позволяет изменить используемые коэффициенты в то время, пока + не запущен синхронный сбор данных. При этом изменяются только текущие + коэффициенты, а заводские калибровочные коэффициенты из Flash-памяти + сохраняют свое значение и при следующем открытии будут восстановлены. + + @param[in] hnd Описатель модуля. + @param[in] range Диапазон АЦП (из #t_l502_adc_range). + @param[in] k Устанавливаемое значение коэффициента шкалы. + @param[in] offs Устанавливаемое значение смещения нуля. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetAdcCoef(t_l502_hnd hnd, uint32_t range, double k, double offs); + +/***************************************************************************//** + @brief Получение текущих калибровочных коэффициентов АЦП. + + Функция возвращает текущие калибровочные коэффициенты для заданного диапазона + измерения АЦП. Эти коэффициенты могут отличаться от заводских значений, + сохраненных во Flash-памяти модуля, например, если пользователь использовал + L502_SetAdcCoef() для установки своих коэффициентов. + + @param[in] hnd Описатель модуля. + @param[in] range Диапазон АЦП (из #t_l502_adc_range). + @param[in] k В данной переменной возвращается текущий коэффициент + шкалы. + @param[in] offs В данной переменной возвращается текущее смещение нуля. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetAdcCoef(t_l502_hnd hnd, uint32_t range, double* k, double* offs); + + + +/***************************************************************************//** + @brief Установить коэффициенты для калибровки значений ЦАП. + + Функция устанавливает калибровочные коэффициенты для заданного канала АЦП, + которые будут использоваться функциями l502api для калибровки выводимых + значений ЦАП, если указан фалаг #L502_DAC_FLAGS_CALIBR. + + Откалиброванное значение ЦАП в кодах получается как + (val+offs)*k, где val - некалиброванное значение (в кодах). + + При открытии модуля, библиотека считывает калибровочные коэффициенты из + защищенной области Flash-памяти модуля и использует их. + + Данная функция нужна только если пользователь хочет использовать свои + коэффициенты. При этом она не изменяет значения во Flash-памяти, т.е. + при следующем открытии модуля коэффициенты будут снова восстановлены из + Flash-памяти. + + @param[in] hnd Описатель модуля. + @param[in] ch Канал ЦАП (из #t_l502_dac_ch). + @param[in] k Устанавливаемое значение коэффициента шкалы. + @param[in] offs Устанавливаемое значение смещения нуля. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetDacCoef(t_l502_hnd hnd, uint32_t ch, double k, double offs); + + +/***************************************************************************//** + @brief Получение текущих калибровочных коэффициентов ЦАП. + + Функция возвращает текущие калибровочные коэффициенты для заданного канала ЦАП. + Эти коэффициенты могут отличаться от заводских значений, + сохраненных во Flash-памяти модуля, например, если пользователь использовал + L502_SetDacCoef() для установки своих коэффициентов. + + @param[in] hnd Описатель модуля. + @param[in] ch Канал ЦАП (из #t_l502_dac_ch). + @param[in] k В данной переменной возвращается текущий коэффициент + шкалы. + @param[in] offs В данной переменной возвращается текущее смещение нуля. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetDacCoef(t_l502_hnd hnd, uint32_t ch, double* k, double* offs); + + + +/** @} */ + + +/***************************************************************************//** + @addtogroup func_async Функции асинхронного ввода-вывода + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Асинхронный вывод данных на канал ЦАП. + + Функция выводит указанное значение на указанный канал ЦАП. Значение может + быть задано как в кодах, так и в Вольтах, и к нему могут быть применены + калибровочные коэффициенты (определяется флагами). + + Функция может вызываться либо когда синхронный сбор не запущен, либо при + запущенном сборе данных, если синхронный сбор по этому каналу ЦАП + не разрешен. + + @param[in] hnd Описатель модуля. + @param[in] ch Номер канала ЦАП (из #t_l502_dac_ch). + @param[in] data Выводимое значение на ЦАП (в кодах или вольтах) + @param[in] flags Флаги из #t_l502_dacout_flags. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_AsyncOutDac(t_l502_hnd hnd, uint32_t ch, double data, uint32_t flags); + +/***************************************************************************//** + @brief Асинхронный вывод данных на цифровые выходы. + + Функция выводит указанное значение на цифровые выходы модуля. + Формат значения аналогичен L502_PrepareData() - в младших 16 битах + указывается выводимое значение, а в старшие - флаги (с помощью которых можно + перевести одну из половин в третье состояние). + + Функция может вызываться либо когда синхронный сбор не запущен, либо при + запущенном сборе данных, если синхронный сбор по цифровым линиям не разрешен. + + Можно использовать маску, чтобы вывести только на часть выводов, оставив + остальные неизменными, однако следует учесть, что после открытия связи с + модулем необходимо сперва сделать вывод на все линии, после чего уже + можно использовать маску при последующих вызовах. + + @param[in] hnd Описатель модуля. + @param[in] val Младшая половина - выводимое значение, старшая - + флаги из #t_l502_digout_word_flags. + @param[in] msk Маска - указанные в маске биты не будут изменяться + с предыдущего выведенного состояния (распространяется + и на старшую половину val). + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_AsyncOutDig(t_l502_hnd hnd, uint32_t val, uint32_t msk); + + +/***************************************************************************//** + @brief Асинхронный ввод значений с цифровых входов. + + Функция считывает текущее значение цифровых входов. + При этом синхронный сбор цифровых входов не должен быть запущен (не разрешен + поток #L502_STREAM_DIN). + + Так как модуль L502 не поддерживает аппаратно асинхронный ввод, то если на + момент вызова этой функции не запущен синхронный ввод/вывод с помощью + L502_StreamsStart(), то данная функция на время выполнения запускает + синхронный сбор и останавливает его как только будут получено одно новое + значение цифровых входов. + + @param[in] hnd Описатель модуля. + @param[out] din При успешном выполнении в этой переменной + возвращается текущее состояние цифровых входов. + Действительны младшие 18 бит, старшие 14 - резерв. + Резервные биты могут быть использованы в последующих + версиях, не следует считать, что они всегда будут + равны нулю! + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_AsyncInDig(t_l502_hnd hnd, uint32_t* din); + + + + + + + + + +/***************************************************************************//** + @brief Асинхронный ввод одного кадра АЦП. + + Функия производит однократный ввод кадра в соответствии с заранее + установленной логической таблицей. Частота сбора АЦП соответствует частоте + установленной с помощью L502_SetAdcFreq(). Частота следования кадров + значения не имеет. Сам кадр вводится синхронно, но при последовательном + вызове L502_AsyncGetAdcFrame() для измерения нескольких кадров задержка + между этими кадрами не определена. + + Функция так же выполняет обработку принятых данных АЦП, аналогично + L502_ProcessAdcData(), и принимает набор флагов, аналогичный + L502_ProcessAdcData(). + + Для работы этой функции не должен быть разрешен синхронный ввод АЦП + и цифровых линий. + + Так как аппаратно асинхронный ввод в плате отсутствует, то эта функция + в случае не запущенного потока запускает его внутри себя, принимает один + кадр данных и после этого останавливает синхронный сбор. + + @param[in] hnd Описатель модуля. + @param[in] flags Флаги из t_l502_proc_flags + @param[in] tout Таймаут на выполнение функции в мс + @param[out] data Массив, в котором в случае успеха будут возвращены + отсчеты кадра АЦП. Должен быть размером, достаточным + для хранения отсчетов типа double в количестве, + равном количеству установленных логических каналов + в управляющей таблице АЦП. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_AsyncGetAdcFrame(t_l502_hnd hnd, uint32_t flags, + uint32_t tout, double* data); + +/** @} */ + + + + +/***************************************************************************//** + @addtogroup func_streams Функции для работы с синхронным потоковым вводом-выводом + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Разрешение синхронных потоков на ввод/вывод. + + Функция разрешает прием/передачу для указанных потоков + Не указанные потоки сохраняют свое разрешенное или запрещенное состояние. + Может вызываться как до L502_Configure(), так и после. + Разрешенные потоки устанавливаются как правило до вызова L502_StreamsStart(). + + При желании в некоторых ситуациях можно изменять состав разрешенных потоков + во время запущенного сбора данных, однако если эти потоки сильно различаются + в частоте, то рассчитанные библиотекой значения буфера и шага прерывания + могут не подходить для изменившихся значений (см. @ref sect_sync_mode_buf) + @param[in] hnd Описатель модуля. + @param[in] streams Набор флагов #t_l502_streams, указывающих, какие потоки + должны быть разрешены. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_StreamsEnable(t_l502_hnd hnd, uint32_t streams); +/****************************************************************************//** + @brief Запрещение синхронных потоков на ввод/вывод. + + Функция запрещает передачу синхронных данных для указанных потоков. + Не указанные потоки сохраняют свое разрешенное или запрещенное состояние. + Функция, противоположная по смыслу L502_StreamsEnable(). + @param[in] hnd Описатель модуля. + @param[in] streams Набор флагов #t_l502_streams, указывающих, какие потоки + должны быть запрещены. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_StreamsDisable(t_l502_hnd hnd, uint32_t streams); + +/***************************************************************************//** + @brief Запуск синхронных потоков ввода/вывода. + + Функция запуска синхронных потоков данных. Все синхронные потоки тактируются + от общей опорной частоты. Если был установлен внутренний старт синхронизации, + то синхронизация потоков начнется при выполнении данной функции, в противном + случае по данной функции модуль перейдет в состояние ожидания внешнего + признака начальной синхронизации. + + Также функция осуществляет инициализацию канала DMA на ввод данных из платы, + если был разрешен поток АЦП или синхронного ввода цифровых линий, + и инициалзицую канала DMA на вывод, если был разрешен хотя бы один поток на + вывод, но не была вызвана функция L502_PreloadStart() (однако в этом случае + начало вывода не совпадет с началом ввода). + + @param[in] hnd Описатель модуля. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_StreamsStart(t_l502_hnd hnd); + +/***************************************************************************//** + @brief Останов синхронных потоков ввода/вывода. + + Функция останова синхронных потоков ввода/вывода данных. После выполнению + этой функции модуль завершает генерацию опорной частоты синхронизации (или + использовать внешную частоту синхронизации) и останавливает синхронную + передачу данных. + @param[in] hnd Описатель модуля. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_StreamsStop(t_l502_hnd hnd); + + +/***************************************************************************//** + @brief Проверка, запущен ли синхронный ввод/вывод. + + Функция проверяет запущен ли синхронный ввод вывод с помощью L502_StreamsStart() + или какой-либо внутренней логикой в прошивки BlackFin. + Если сбор данных не запущен, то функция возвращает ошибку + #L502_ERR_STREAM_IS_NOT_RUNNING, если запущен, то нулевой код ошибки + + @param[in] hnd Описатель модуля. + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_IsRunning(t_l502_hnd hnd); + +/***************************************************************************//** + @brief Чтение данных АЦП и цивровых входов из модуля. + + Функция считывает данные от модуля, которые были переданы по DMA в буфер + драйвера. Функция принимает отсчеты в специальном индексном формате, + в котором содержится информация, что это за данные (значения цифровых входов + или отсчеты АЦП) и дополнительная информация для АЦП (номер канала, режим). + Для разбора полученных отсчетов используется функция L502_ProcessData(). + + Если в буфере драйвера сейчас находится меньше отсчетов, чем было запрошено, + то функция будет ожидать пока придет заданноеколичество данных или + пока не истечет указанный таймаут. В последнем случае функция возвратит + столько отсчетов, сколько было в буфере при истечении таймаута. + + Количество готовых для чтения отсчетов в буфере драйвера можно при желании + узнать с помощью функции L502_GetRecvReadyCount(). + + До вызовов L502_Recv() синхронный поток сбора данных должен быть уже запущен + с помощью L502_StreamsStart(). + + @param[in] hnd Описатель модуля. + @param[out] buf Буфер, в которые будут сохранены отсчеты. + @param[in] size Количество считываемых отсчетов (32-битных слов). + @param[in] tout Таймаут на прием данных в мс. + @return Если < 0 - код ошибки. + Если >= 0 - количество считанных слов. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_Recv(t_l502_hnd hnd, uint32_t* buf, uint32_t size, uint32_t tout); + + +/***************************************************************************//** + @brief Передача потоковых данных ЦАП и цифровых выходов в модуль. + + Функция записывает данные на передачу во внутренний буфер драйвера, после + чего эти данные будут по DMA переданы в модуль. + Данные должны быть в специальном формате, который определяет, что это за + данные (цифровые выходы, канал ЦАП1 или канал ЦАП2). Подготовить данные + в нужном формате можно с помощью L502_PrepareData(). + + Если буфер драйвера на передачу заполнен, то функция будут ждать пока + он не освободится или пока не истечет указанный таймаут. + Количество свободного места в буфере можно при желании узнать с помощью + функции L502_GetSendReadyCount(). + + Возвращение означает что данные переданы в буфер драйвера, а не то что они + уже дошли до модуля и выведены. + + Перед вызовом этой функции должна быть запущена предзагрузка данных на + вывод с помощью L502_PreloadStart(). + + @param[in] hnd Описатель модуля. + @param[in] buf Буфер со словами, которые необходимо передать модулю + @param[in] size Количество передаваемых отсчетов (32-битных слов). + @param[in] tout Таймаут на передачу (в буфер драйвера) данных в мс. + @return Если < 0 - код ошибки. + Если >= 0 - количество записанных слов. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_Send(t_l502_hnd hnd, const uint32_t* buf, uint32_t size, uint32_t tout); + + + +/***************************************************************************//** + @brief Обработка принятых отсчетов АЦП от модуля. + + Функция выполняет обработку отсчетов АЦП, прочитанных с помощью L502_Recv(). + Функция проверяет служебную информацию из входного массива и переводит отсчеты + АЦП либо в коды, либо в вольты (если указан флаг #L502_PROC_FLAGS_VOLT). + + Функция используется, когда не запущен синхронный ввод с цифровых линий и + отсчеты АЦП являются единственными приходящими от модуля данными (если + в принятом потоке будут другие данные - то они будут отброшены). + + Если запущен синхронный ввод с цифровых линий, то следует использовать + L502_ProcessData(), которая выделяет данные с цифровых линий в отдельный + массив. + + @param[in] hnd Описатель модуля. + @param[in] src Входной массив отсчетов, принятых с помощью L502_Recv(). + @param[out] dest Массив, в который будут сохранены преобразованные данные + от АЦП. + @param[in,out] size На входе - количество слов в массиве src, на выходе - + количество сохраненных преобразованных значений в + массиве dest + @param[in] flags Набор флагов из #t_l502_proc_flags + @return Код ошибки. + ****************************************************************************/ +LPCIE_EXPORT(int32_t) L502_ProcessAdcData(t_l502_hnd hnd, const uint32_t* src, double *dest, + uint32_t *size, uint32_t flags); + + +/***************************************************************************//** + @brief Обработка принятых от модуля данных. + + Функция выполняет обработку данных, прочитанных с помощью L502_Recv(). + Функция проверяет служебную информацию из входного массива, разбивает данные + на два массива - данные от АЦП, которые переводятся в тип double, и данные + от синхронного цифрового ввода. + + Данные от АЦП так же могут быть переведены в вольты. При этом данные АЦП + приходят от модуля уже откалиброванными с помощью калибровочных коэффициентов, + так как калибровка выполняется аппаратно. Если данные АЦП не переводятся + в Вольты и при этом не были изменены заводские калибровочные коэффициенты, + то возвращенное значение равное #L502_ADC_SCALE_CODE_MAX соответствует + напряжению равному максимальному для используемого диапазона. + + Кроме того, функция разбирает сообщения, передаваемые в потоке данных + (например, сообщение о переполнении буфера). + @param[in] hnd Описатель модуля. + @param[in] src Входной массив отсчетов, принятый с помощью L502_Recv(). + @param[in] size Количество отсчетов (32-битных слов) в массиве src. + @param[in] flags Набор флагов из #t_l502_proc_flags, управляющих + поведением функции. Может быть указано несколько флагов + через логическое "ИЛИ". + @param[out] adc_data Массив, в который будут сохранены данные от АЦП, + преобразованные в соответствии с указанными флагами. + Может быть NULL, если не нужно сохранять данные от АЦП + (тогда adc_data_size должен быть тоже NULL, или в + переменной передан размер 0). + @param[in,out] adc_data_size На входе в данном параметре передается резмер + буфера adc_data. Если данных от АЦП во входном массиве + будет больше adc_data_size, то в adc_data будет + сохранено только первые adc_data_size отсчетов. + На выходе при успешном завершении функции в данную + переменную записывается количество сохранных отсчетов + АЦП. + Указатель может быть равен NULL, если adc_data = NULL + @param[out] din_data Массив, в который будут сохранены отчеты с синхронного + цифрового ввода. Младшие 18 битов соответствуют + состояниям линий, старшие 14 - резерв. + Может быть NULL, если не нужно сохранять данные от + синхронного ввода. + @param[in,out] din_data_size Аналогично параметру adc_data_size в этом + параметре передается размер буфера din_data в отсчетах, + а на выходе сохраняется количество реально сохраненных + отсчетов цифрового ввода. Может быть NULL, если + din_data = NULL. + @return Код ошибки. + ****************************************************************************/ +LPCIE_EXPORT(int32_t) L502_ProcessData(t_l502_hnd hnd, const uint32_t* src, uint32_t size, + uint32_t flags, double *adc_data, uint32_t *adc_data_size, + uint32_t *din_data, uint32_t *din_data_size); + +/***************************************************************************//** + @brief Обработка принятых от модуля данных с пользовательскими данными. + + Функция аналогична L502_ProcessData(), но позволяет также выделить + пользовательские данные из потока. Пользовательскими данными считаются все + отсчеты, которые не являются данными АЦП, данными цифрового ввода + или сообщениями. + Пользовательские данные складываются без изменений в массив usr_data + (если он не равен нулю). + Данная функция предназначена в первую очередь для программистов, которые + будут использовать модифицированную прошивку сигнального процессора BlackFin. + @param[in] hnd Описатель модуля. + @param[in] src Входной массив отсчетов, принятый с помощью + L502_Recv(). + @param[in] size Количество отсчетов (32-битных слов) в массиве src. + @param[in] flags Набор флагов из #t_l502_proc_flags. + @param[out] adc_data Массив, в который будут сохранены данные от АЦП + (см. L502_ProcessData()). + @param[in,out] adc_data_size см. L502_ProcessData() + @param[out] din_data Массив, в который будут сохранены отчеты с + синхронного цифрового ввода. См. L502_ProcessData(). + @param[in,out] din_data_size см. L502_ProcessData(). + @param[out] usr_data Массив, в который будут сохранены пользовательские + данные без изменения их формата. + @param[in,out] usr_data_size В этом параметре передается размер буфера usr_data + а на выходе сохраняется количество реально сохраненных + отсчетов пользовательских данных. + Может быть NULL только если usr_data = NULL. + @return Код ошибки. + ****************************************************************************/ +LPCIE_EXPORT(int32_t) L502_ProcessDataWithUserExt(t_l502_hnd hnd, const uint32_t* src, uint32_t size, + uint32_t flags, double *adc_data, + uint32_t *adc_data_size, uint32_t *din_data, + uint32_t *din_data_size, + uint32_t *usr_data, uint32_t *usr_data_size); + + + +/***************************************************************************//** + @brief Подготовка данных для вывода в модуль. + + Функция принимает данные из трех массивов - данные на цифровые выходы, + отсчеты первого и второго канала ЦАП. В качестве массива может быть передан + нулевой указатель, если данные из этого источника не требуются. + Все используемые массивы должны быть одинакового размера и функция их + равномерно перемешивает в общий поток, преобразуя в нужный для модуля формат. + + Выходной массив должен будет содержать n*size отсчетов, где n - количество + используемых входных массивов (от 1 до 3). + + Значения цифровых выходов представляют собой 32-битные слова, младшие 16-бит + которых определяют значения выводов, а старшие - флаги из + #t_l502_digout_word_flags, которые могут использоваться в частности для + перевода одной (или обеих) из половин выводов в третье состояние. + + В качестве значений ЦАП могут использоваться как коды, так и Вольты, + в зависимости от переданных флагов, и к выводимым значениям могут быть + применены калибровочные коэффициенты. + Если используются коды ЦАП с включенной калибровкой, то код + #L502_DAC_SCALE_CODE_MAX определяет код, соответствующий +5V. + + @param[in] hnd Описатель модуля. + @param[in] dac1 Входной массив отсчетов первого канала ЦАП или + NULL, если не используется. + @param[in] dac2 Входной массив отсчетов второго канала ЦАП или + NULL, если не используется. + @param[in] digout Входной массив со значениями цифровых выводов + или NULL, если не используется. + @param[in] size Размер каждого из используемых входных массивов. + @param[in] flags Флаги, управляющие работой функции, из + #t_l502_dacout_flags. + @param[out] out_buf Выходной массив, в который будут сохранены + сформированные отсчеты. Должен быть размера + n*size (n - количество используемых входных + массивов) + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_PrepareData(t_l502_hnd hnd, const double* dac1, const double* dac2, + const uint32_t* digout, uint32_t size, int32_t flags, + uint32_t* out_buf); + +/***************************************************************************//** + @brief Получить количество отсчетов в буфере потока на ввод. + + Функция возвращает количество отсчетов, которые были приняты из модуля + во внутренний буфер в драйвере и готовы для считывания с помощью + L502_Recv(). + То есть если в L502_Recv() передать значение, которое вернула данная функция, + то L502_Recv() вернет это количество данных без ожидания (так как они уже + в буфере). + @param[in] hnd Описатель модуля. + @param[out] rdy_cnt Количество готовых к приему отсчетов. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetRecvReadyCount(t_l502_hnd hnd, uint32_t *rdy_cnt); + + +/***************************************************************************//** + @brief Получить размер свободного места в буфере потока на вывод. + + Функция возвращает количество отсчетов, соответствующее свободному месту + в буфере драйвера на передачу в модуль. + Это количество отсчетов может быть передано с помощью L502_Send() без + ожидания. + @param[in] hnd Описатель модуля. + @param[out] rdy_cnt Количество свободных отсчетов в буфере + на передачу. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetSendReadyCount(t_l502_hnd hnd, uint32_t *rdy_cnt); + + +/***************************************************************************//** + @brief Получить номер следующего ожидаемого логического канала АЦП для + обработки. + + Функция возвращает номер логического канала АЦП, который должен быть + обработан первым при следующем вызове L502_ProcessData()/ + L502_ProcessAdcData() в случае, если поток данных непрерывен. + + По сути, это номер логического канала, слудущий за логическим каналом + последнего обработанного до этого отсчета АЦП. + Может быть использовано при обработке блоков данных не кратных целому + количеству кадров. + Если перед L502_ProcessData() вызывать данную функцию, то она вернет номер + логического канала, соответствющий первому отсчету АЦП, + обработаному последующим вызовом L502_ProcessData(). + + Например, если установлено 7 логических каналов, а в L502_ProcessData() + передано для обработки кратное 7 количество отсчетов, то последующий вызов + L502_GetNextExpectedLchNum() вернет номер канала равный 0 (так как обработано + целое число кадров и ожидается снова начало кадра). + Если в L502_ProcessData() передан массив с 7*n + 5 отсчетами АЦП, то следующим + ожидаемым каналом будет логический канал с номером 5 (обработаны каналы + 0,1,2,3,4 из неполного кадра). + + @param[in] hnd Описатель модуля. + @param[out] lch Номер логического канала (начиная с нуля). + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_GetNextExpectedLchNum(t_l502_hnd hnd, uint32_t *lch); + + +/***************************************************************************//** + @brief Начало подготовки вывода синхронных данных + + Функция должна вызываться перед началом предзагрузки потоковых синхронных + данных на вывод. Для начала выдачи синхронных данных одновременно с началом + синхронного ввода, к моменту начала сбора часть данных должна быть уже + загружена в модуль до вызова L502_StreamsStart(). + + Данная функция инициализирует DMA канал на передачу данных на вывод. + После вызова этой функции можно загрузить часть данных на вывод + с помощью L502_Send(). + + @param[in] hnd Описатель модуля. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_PreloadStart(t_l502_hnd hnd); + + + +/***************************************************************************//** + @brief Начало загрузки циклического сигнала на вывод + + По вызову этой функции в драйвере выделяется место под циклический буфер на + вывод. Должна вызываться перед загрузкой циклических данных с помощью + L502_Send(). + + Для успешного выполнения в драйвере должен быть свободный буфер (используется + двойная буферизация) - т.е. функция не может быть вызвана сразу после предыдущего + L502_OutCycleSetup(). Кроме того не должен был быть использован потоковый + вывод. + + @param[in] hnd Описатель модуля. + @param[in] size Количество отсчетов в выводимом циклическом сигнале + суммарно для всех используемых каналов вывода. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_OutCycleLoadStart(t_l502_hnd hnd, uint32_t size); + +/***************************************************************************//** + @brief Установка ранее загруженного циклического сигнала на вывод + + По вызову этой функции ранее загруженный циклический буфер становится активным. + Если синхронный ввод-вывод запущен (через L502_StreamsStart()), то по этой + функции сигнал будет выдаваться на выход, иначе выдача начнется при запуске + синхронного ввода-вывода. + + Если до этого уже выводился циклический сигнал, то смена на новый произойдет + в конце цикла предыдущего сигнала, если не указан флаг + #L502_OUT_CYCLE_FLAGS_FORCE. + + Данная функция должна быть вызвана только после вызова L502_OutCycleLoadStart() + и загрузки указанного в ней количества отсчетов в буфер! + + @param[in] hnd Описатель модуля. + @param[in] flags Флаги из #t_l502_out_cycle_flags. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_OutCycleSetup(t_l502_hnd hnd, uint32_t flags); + + +/***************************************************************************//** + @brief Останов вывода циклического сигнала + + По вызову этой функции прекращается выдача ранее установленного циклического + сигнала с помощью L502_OutCycleSetup(). Остановка осуществляется по + после выдачи последнего семпла в периоде (но функция возвращает управление + сразу, делая только запрос на останов), что позволяет знать какие значения + останутся на выходах. + + При вызове же L502_StreamsStop() (или при запрещении всех потоков + на вывод через L502_StreamsDisable()) останов всех потоков происходит сразу + и точную точка не извествна. + + @param[in] hnd Описатель модуля. + @param[in] flags Флаги из #t_l502_out_cycle_flags. + @return Код ошибки. + ***************************************************************************/ +LPCIE_EXPORT(int32_t) L502_OutCycleStop(t_l502_hnd hnd, uint32_t flags); + + + + + +/***************************************************************************//** + @brief Установка размера буфера в драйвере для синхронного ввода или вывода. + + Функция устанавливает размер буфера в драйвере, который используется + для временного хранения данных на прием или на передачу. + Предназначена для случаев, когда пользователя по каким-либо причинам не + удовлетворяет рассчитываемое библиотекой значение по-умолчанию + + @param[in] hnd Описатель модуля. + @param[in] dma_ch Определяет, устанавливается размер буфера на ввод + или на вывод (значение из #t_l502_dma_ch). + @param[in] size Размер буфера в 32-битных отсчетах + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetDmaBufSize(t_l502_hnd hnd, uint32_t dma_ch, uint32_t size); + +/***************************************************************************//** + @brief Установка шага прерывания при передаче потока по DMA. + + Функция устанавливает шаг для генерации прерываний при передаче синхронного + потока данных на ввод или на вывод. + После передачи указанного количества отсчетов по DMA между буфером + драйвера и модулем, модуль генерирует прерывание и драйвер обновляет счетчик + переданных данных. + Даннай функция предназначена для пользователей, которых не устроит + автоматически рассчитываемое библиотекой значение. + + @param[in] hnd Описатель модуля. + @param[in] dma_ch Определяет, шаг прерывания устанавливается на ввод + или на вывод (значение из #t_l502_dma_ch). + @param[in] step Шаг прерывания в 32-битных отсчетах + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetDmaIrqStep(t_l502_hnd hnd, uint32_t dma_ch, uint32_t step); + +/** @} */ + + + + +/***************************************************************************//** + @addtogroup func_dsp Функции для работы с сигнальным процессором + @{ +*******************************************************************************/ +/***************************************************************************//** + @brief Загрузка прошивки сигнального процессора BlackFin. + + Функция загружает прошивку сигнального процессора из указанного файла в + процессор и запускает ее, проверяет правильность загрузки путем получения + версии прошивки (через специальную команду). + Прошивка должна быть в бинарном формате LDR. + @param[in] hnd Описатель модуля. + @param[in] filename Имя файла с загружаемой прошивкой. + @return Код ошибки. + *****************************************************************************/ +LPCIE_EXPORT(int32_t) L502_BfLoadFirmware(t_l502_hnd hnd, const char* filename); + + + +/***************************************************************************//** + @brief Проверка, загружена ли прошивка BlackFIn. + + Функция передает команды процессору BlackFin для получения версии прошивки и + ее состояния. Успешное выполнение команд свидетельствует о том, что в + BlackFin загружена действительная прошивка. + Кроме того прошивке передается информация о модуле (наличие опций, версия + ПЛИС и т.д.) для внутреннего использования. + В случае успеха модуль переводится в режим DSP. + + Данная функция может служить для проверки была ли загружена прошивка раньше + (чтобы не загружать повторно) или для проверки была ли она загружена через + JTAG-интерфейс. + + @param[in] hnd Описатель модуля. + @param[out] version Если указатель не нулевой, то в данной переменной + возвращается версия прошивки BlackFin в случае + успешной проверки. + @return Код ошибки. + *****************************************************************************/ +LPCIE_EXPORT(int32_t) L502_BfCheckFirmwareIsLoaded(t_l502_hnd hnd, uint32_t *version); + +/***************************************************************************//** + @brief Чтение блока данных из памяти сигнального процессора. + + Функция считывает блок данных напрямую из памяти процессора. Может быть + прочитаны данные, как из внутренней памяти (L1), так и из внешней SDRAM. + Для выполнения этой функции в BlackFin должна быть загружена его прошивка. + + Функция предназначена в первую очередь для пользователей, пишущих свою + программу для сигнального процессора. + + @param[in] hnd Описатель модуля. + @param[in] addr Адрес памяти, начиная с которого будет считан блок + данных. + @param[out] regs Массив, в который будут сохранено прочитанное + содержимое памяти. + @param[in] size Количество считываемых 32-битных слов. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_BfMemRead(t_l502_hnd hnd, uint32_t addr, uint32_t* regs, + uint32_t size); + +/***************************************************************************//** + @brief Запись блока данных в память сигнального процессора. + + Функция записывает блок данных напрямую в памяти процессора BlackFin. Блок + данных должен быть всегда кратен 8 32-битным словам (32 байтам). + Запись может осуществляться как во внутреннюю память (L1), так и во внешнюю SDRAM. + Для выполнения этой функции в BlackFin должна быть загружена его прошивка. + + Функция предназначена в первую очередь для пользователей, пишущих свою + программу для сигнального процессора. + + @note Следует быть осторожным, т.к. запись в область данных, используемую + программой может привести к ее неработоспособности. + + @param[in] hnd Описатель модуля. + @param[in] addr Адрес памяти, начиная с которого будет записан блок + данных. + @param[out] regs Массив с данными для записи в сигнальный процессор. + @param[in] size Количество записываемых данных в 32-битных словах + (должно быть кратно 8). + @return Код ошибки. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_BfMemWrite(t_l502_hnd hnd, uint32_t addr, + const uint32_t* regs, uint32_t size); + + +/***************************************************************************//** + @brief Передача управляющей команды сигнальному процессору. + + Функция предназначена для передачи пользовательских управляющих команд + процессору для пользователей, пишущих свою прошивку BlackFin. + + Управление работой сигнального процессора штатным образом осуществляется + через управляющие команды, которые записываются в специальную область + памяти сигнального процессора. Сигнальный процессор обрабатывает команду + и по завершению записывает в эту же область результат. + + Команды делаться на стандартные, которые используются библиотекой l502api и + пользовательские, которые может пользователь определять по своему усмотрению. + Пользовательские команды начинаются с кода L502_BF_CMD_CODE_USER (0x8000). + + @param[in] hnd Описатель модуля. + @param[in] cmd_code Код команды - определяет, что за команда выполняется. + @param[in] par Параметр, передаваемый с командой (значение зависит + от кода команды). + @param[in] snd_data Опциональные данные, передаваемые вместе с командой. + Если данные не передаются, то должен передаваться + нулевой указатель и snd_size = 0. + @param[in] snd_size Количество 32-битных слов, передаваемых в snd_data + @param[out] rcv_data Массив, в который будут переданы данные, возвращенные + процессором по завершению команды. Если данные не + должны возвращаться, то должен передаваться нулевой + указатель, а rcv_size = 0. + @param[in] rcv_size Количество 32-битных слов, которое ожидается, что + вернет процессор по выполнению команды. Массив + rcv_data должен быть рассчитан на данное количество + слов. + @param[in] tout Таймаут в течении которого будет ожидаться, когда + процессор завершит выполнение команды. Функция + возвратит управление либо по завершению команды, + либо по таймауту. + @param[out] recvd_size Если не является нулевым указателем, то в эту + переменную будет сохранено количество 32-битных слов, + которое реально вернул процессор после выполнения + команды (процессор имеет право вернуть меньше данных, + чем запрашивалось в rcv_size). + @return Код ошибки. Если процессор выполнил команду с ненулевым + кодом завершения, то этот код и будет возвращен + функцией. +*******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_BfExecCmd(t_l502_hnd hnd, uint16_t cmd_code, uint32_t par, + const uint32_t* snd_data, uint32_t snd_size, + uint32_t* rcv_data, uint32_t rcv_size, uint32_t tout, uint32_t* recvd_size); +/** @} */ + + + +/***************************************************************************//** + @addtogroup func_flash Функции для работы с Flash-памятью модуля + @{ +*******************************************************************************/ + +/***************************************************************************//** + @brief Чтение блока данных из Flash-памяти. + + Функция считывает массив данных из Flash-памяти модуля в массив, переданный + пользователем. Для считывания не нужно специальное разрешение - оно доступно + всегда. + @param[in] hnd Описатель модуля. + @param[in] addr Адрес начала блока. + @param[out] data Массив, куда будут сохранены считанные данные + (должен быть не меньше size байт). + @param[in] size Количество байт для чтения. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_FlashRead(t_l502_hnd hnd, uint32_t addr, uint8_t* data, + uint32_t size); +/***************************************************************************//** + @brief Запись блока данных во Flash-память модуля. + + Функция записывает переданный массив данных во Flash-память модуля. + Эта область должна быть предварительно стерта с помощью L502_FlashErase() и + до начала изменения должна быть вызвана функция L502_FlashWriteEnable(), + чтобы разрешить любое изменение содержимого Flash-памяти. + Пользователю для записи доступны только первые #L502_FLASH_USER_SIZE байт + Flash-памяти. + @param[in] hnd Описатель модуля. + @param[in] addr Адрес начала блока. + @param[in] data Массив c записываемыми данными (должен быть не меньше + size байт). + @param[in] size Количество байт для записи. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_FlashWrite(t_l502_hnd hnd, uint32_t addr, + const uint8_t* data, uint32_t size); +/***************************************************************************//** + @brief Стирание блока во Flash-памяти. + + Функция стирает блок во Flash-памяти модуля (все ячейки будут читаться как + 0xFF). Адрес и размер должны быть кратны 4096 байт! + Перед вызовом этой функции запись должна быть разрешена запись + в пользовательскую область с помощью L502_FlashWriteEnable(). + @param[in] hnd Описатель модуля. + @param[in] addr Адрес начала блока (должен быть кратен 4K). + @param[in] size Количество байт для стирания (кратно 4K). + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_FlashErase(t_l502_hnd hnd, uint32_t addr, uint32_t size); +/***************************************************************************//** + @brief Разрешение записи в пользовательскую область Flash-памяти. + + Функция разрешает запись в пользовательскую область Flash-памяти (первые + #L502_FLASH_USER_SIZE байт). Должна быть вызвана до того, как + можно будет использовать L502_FlashErase() и L502_FlashWrite() для изменения + содержимого пользовательской области памяти. После завершения изменения + следует вызвать L502_FlashWriteDisable(). + @param[in] hnd Описатель модуля. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_FlashWriteEnable(t_l502_hnd hnd); +/***************************************************************************//** + @brief Запрет записи в пользовательскую область Flash-памяти. + + Функция запрещает запись в пользовательскую область Flash-памяти модуля + (первые #L502_FLASH_USER_SIZE байт). Должна быть вызвана после того, как + нужные данные в пользовательской области были изменены с помощью + L502_FlashErase() и L502_FlashWrite(), чтобы защитить пользовательскую + область от случайной изменения в дальнейшем. + @param[in] hnd Описатель модуля. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_FlashWriteDisable(t_l502_hnd hnd); + +/** @} */ + +/***************************************************************************//** + @addtogroup func_misc Дополнительные вспомогательные функции. + @{ +*******************************************************************************/ +/**************************************************************************//** + @brief Получить версию библиотеки. + + Функция возвращает версию библиотеки l502api.dll. + Версия возвращается в виде 32-битного числа. + Строковое представление возвращенной версии - четыре числа, + старшее соответствует старшему байту, младшее - младшему. + + Старший байт - можорная версия, второй по старшинству байт - минорная, + третий - ревизия, четвертый - номер сборки (не используется - всегда 0) + + @return 32-битное число, представляющее собой версию библиотеки + *****************************************************************************/ +LPCIE_EXPORT(uint32_t) L502_GetDllVersion(void); + + + +/***************************************************************************//** + @brief Получение строки об ошибке. + + Функция возвращает строку, соответствующую переданному коду ошибки. + В настроящее время возвращается всегда русская версия строки (возможно в + будущем будет возможность сменить язык глобальной функцией). + + @note Следует учесть, что в ОС Windows строка возвращается в + стандартной для Windows кодировке CP1251, в то время как в Linux + используется кодировка UTF-8. + @param[in] err Код ошибки, для которого нужно вернуть строку. + @return Указатель на строку, соответствующую коду ошибки + ******************************************************************************/ +LPCIE_EXPORT(const char*) L502_GetErrorString(int32_t err); + + +/***************************************************************************//** + @brief Моргание светодиодом на передней панели. + + При вызове этой функции, если не запущен синхронный ввод/вывод, происходит + кратковременное затухание красного цвета светодиода на передней панели. + Может быть использована для визуальной идентификации модуля после его + открытия. + + При запущенном синхронном вводе/выводе светодиод на передней панели всегда + горит зеленым цветом и данная функция не влияет на его состояние. + + @param[in] hnd Описатель модуля. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_LedBlink(t_l502_hnd hnd); + +/***************************************************************************//** + @brief Установка подтягивающих резисторов на входных линиях. + + Функция может использоваться для включения подтягивающих резисторов на + цифровых входах. Отдельно можно задавать включены или отключены подтяжки + на младшей половине цифровых линий, старшей половине, на линии SYN1 и на + линии SYN2. На не указанных линиях подтягивающие резисторы будут отключены, + если они были включены до этого. + При включении питания все подтягивающие резисторы отключены. + + @param[in] hnd Описатель модуля. + @param[in] pullups Флаги (из #t_l502_pullups), определяющие, на каких + линиях включены подтягивающие резисторы. + @return Код ошибки. + ******************************************************************************/ +LPCIE_EXPORT(int32_t) L502_SetDigInPullup(t_l502_hnd hnd, uint32_t pullups); + +/** @} */ +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/x502api-1.1.34/devs/l502/l502api_eeprom.c b/x502api-1.1.34/devs/l502/l502api_eeprom.c new file mode 100644 index 0000000..bbc05fe --- /dev/null +++ b/x502api-1.1.34/devs/l502/l502api_eeprom.c @@ -0,0 +1,171 @@ +#include "l502api_private.h" +#include "ltimer.h" +#include "l502_fpga_regs.h" + +#define L502_EEPROM_BIG_SECTOR_SIZE (64*1024) +#define L502_EEPROM_SMALL_SECTOR_SIZE (4*1024) + +#define L502_FLASH_WRITE_TOUT 500 +#define L502_FLASH_ERASE_TOUT 500 + +/** биты регистра статуса */ +typedef enum { + SST25_STATUS_BUSY = 0x01, + SST25_STATUS_WEL = 0x02, + SST25_STATUS_BP0 = 0x04, + SST25_STATUS_BP1 = 0x08, + SST25_STATUS_BP2 = 0x10, + SST25_STATUS_BP3 = 0x20, + SST25_STATUS_AAI = 0x40, + SST25_STATUS_BPL = 0x80 +} t_sst25_status_bits; + +static uint8_t f_prot_bits[] = { + SST25_STATUS_BP2 | SST25_STATUS_BP1 | SST25_STATUS_BP0, + SST25_STATUS_BP2 | SST25_STATUS_BP0, + SST25_STATUS_BP2, + SST25_STATUS_BP0, + 0 +}; + + + +static LINLINE int32_t f_eeprom_rd_status(t_x502_hnd hnd, uint8_t* stat) { + uint32_t val; + int32_t err = l502_port_fpga_reg_read(hnd, L502_REGS_EEPROM_RD_STATUS, &val); + if (err == X502_ERR_OK) + *stat = (val>>24)&0xFF; + return err; +} + +static LINLINE int32_t f_eeprom_wr_status(t_x502_hnd hnd, uint8_t stat) { + int32_t err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_STATUS_EN, 1); + if (err == X502_ERR_OK) { + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_STATUS, stat); + } + return err; +} + + +static int32_t f_eeprom_wr_byte(t_x502_hnd hnd, uint32_t addr, uint8_t val) { + int32_t err = X502_ERR_OK; + t_ltimer tmr; + uint8_t stat = SST25_STATUS_BUSY; + + /* разрешаем запись в EEPROM */ + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_EN, 1); + if (err == X502_ERR_OK) { + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_BYTE, ((addr & 0xFFFFFF) << 8) | val); + if (err != X502_ERR_OK) + l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_DIS, 1); + } + + if (err == X502_ERR_OK) + ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(L502_FLASH_WRITE_TOUT)); + + /* Ожидаем завершения записи */ + while ((err == X502_ERR_OK) && (stat & SST25_STATUS_BUSY) && !ltimer_expired(&tmr)) { + err = f_eeprom_rd_status(hnd, &stat); + } + + if ((err == X502_ERR_OK) && (stat & SST25_STATUS_BUSY)) { + err = X502_ERR_FLASH_WRITE_TOUT; + } + return err; +} + + + + + +int32_t l502_iface_flash_rd(t_x502_hnd hnd, uint32_t addr, uint8_t* data, uint32_t size) { + int32_t err = X502_ERR_OK; + uint32_t val; + + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_SET_RD_ADDR, (addr & 0xFFFFFF) << 8); + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_read(hnd, L502_REGS_EEPROM_RD_DWORD, &val); + if (err == X502_ERR_OK) { + unsigned int i; + for (i=0; (i < sizeof(val)) && size; i++, size--) { + *data++ = val & 0xFF; + val >>= 8; + } + } + return err; +} + +int32_t l502_iface_flash_wr(t_x502_hnd hnd, uint32_t addr, const uint8_t* data, uint32_t size) { + uint32_t i; + int32_t err = X502_ERR_OK; + for (i=0; (i < size) && (err == X502_ERR_OK); i++) { + err = f_eeprom_wr_byte(hnd, addr+i, data[i]); + } + return err; +} + +int32_t l502_iface_flash_erase(t_x502_hnd hnd, uint32_t addr, uint32_t size) { + int32_t err = X502_ERR_OK; + if (((addr & (L502_EEPROM_SMALL_SECTOR_SIZE-1)) || + (size & (L502_EEPROM_SMALL_SECTOR_SIZE-1)))) { + err = X502_ERR_FLASH_SECTOR_BOUNDARY; + } + + while((size != 0) && (err == X502_ERR_OK)) { + uint32_t er_size; + /* разрешаем запись в EEPROM */ + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_EN, 1); + if (err == X502_ERR_OK) { + uint8_t stat = SST25_STATUS_BUSY; + t_ltimer tmr; + /* проверяем - можем ли стереть целиком большой сектор или + придется писать в мелкий */ + if ((size >= L502_EEPROM_BIG_SECTOR_SIZE) && + !(size & (L502_EEPROM_BIG_SECTOR_SIZE-1))) { + er_size = L502_EEPROM_BIG_SECTOR_SIZE; + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_ERASE_64K, addr<<8); + } else { + er_size = L502_EEPROM_SMALL_SECTOR_SIZE; + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_ERASE_4K, addr<<8); + } + + if (err == X502_ERR_OK) + ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(L502_FLASH_ERASE_TOUT)); + + /* ожидаем завершения стирания */ + while ((err == X502_ERR_OK) && (stat & SST25_STATUS_BUSY) && !ltimer_expired(&tmr)) { + err = f_eeprom_rd_status(hnd, &stat); + } + + if ((err == X502_ERR_OK) && (stat & SST25_STATUS_BUSY)) { + err = X502_ERR_FLASH_ERASE_TOUT; + } + + /* запрещаем запись, если произошла ошибка. при успешном стирании + запись будут запрещена атоматически */ + if (err != X502_ERR_OK) + l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_EN, 0); + + if (err == X502_ERR_OK) { + addr += er_size; + size -= er_size; + } + } + } + return err; +} + +int32_t l502_iface_flash_set_prot(t_x502_hnd hnd, uint32_t prot, const uint8_t* prot_data, uint32_t size) { + int32_t err = X502_ERR_OK; + uint16_t prot_code = 0; + if (size == 2) { + prot_code = ((uint16_t)prot_data[1] << 8) | prot_data[0]; + } + + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_STATUS_EN, 1); + if ((err == X502_ERR_OK) && (prot_code != 0)) + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_HARD_WR_STATUS_EN, prot_code); + if (err == X502_ERR_OK) + err = l502_port_fpga_reg_write(hnd, L502_REGS_EEPROM_WR_STATUS, f_prot_bits[prot]); + return err; +} diff --git a/x502api-1.1.34/devs/l502/l502api_private.h b/x502api-1.1.34/devs/l502/l502api_private.h new file mode 100644 index 0000000..2529423 --- /dev/null +++ b/x502api-1.1.34/devs/l502/l502api_private.h @@ -0,0 +1,54 @@ +#ifndef L502API_PRIVATE_H +#define L502API_PRIVATE_H + +#include "x502api_private.h" +#include "lpcie_ioctls.h" + +#define L502_DEVICE_NAME "L502" + + +typedef struct { +#ifdef _WIN32 + HANDLE file; +#else + int file; +#endif +} t_pci_iface_data; + +#define L502_PCI_IFACE_FILE(hnd) (((t_pci_iface_data*)hnd->iface_data)->file) + +int32_t l502_port_fpga_reg_write(t_x502_hnd hnd, uint32_t reg, uint32_t val); +int32_t l502_port_fpga_reg_read(t_x502_hnd hnd, uint32_t reg, uint32_t *val); +int32_t l502_port_open(t_x502_hnd hnd, const t_x502_devrec *devrec); +int32_t l502_port_free_iface_data(void *intptr); +int32_t l502_port_close(t_x502_hnd hnd); +int32_t l502_port_stream_start(t_x502_hnd hnd, uint32_t ch, uint32_t single); +int32_t l502_port_stream_stop(t_x502_hnd hnd, uint32_t ch, uint32_t flags); +int32_t l502_port_stream_free(t_x502_hnd hnd, uint32_t ch, uint32_t flags); +int32_t l502_port_stream_read(t_x502_hnd hnd, uint32_t *buff, uint32_t size, uint32_t timeout); +int32_t l502_port_stream_write(t_x502_hnd hnd, const uint32_t *buff, uint32_t size, + uint32_t timeout); +int32_t l502_port_stream_set_params(t_x502_hnd hnd, t_lpcie_stream_ch_params *par); +int32_t l502_port_stream_rdy_size(t_x502_hnd hnd, uint32_t ch, uint32_t *rdy_size); +int32_t l502_port_renew_info(t_x502_hnd hnd); +int32_t l502_port_get_drv_ver(t_x502_hnd hnd, uint32_t *ver); +int32_t l502_port_cycle_load_start(t_x502_hnd hnd, uint32_t ch, uint32_t size, uint32_t min_irq_step); +int32_t l502_port_cycle_setup(t_x502_hnd hnd, uint32_t ch, uint32_t evt); +int32_t l502_port_cycle_stop(t_x502_hnd hnd, uint32_t ch, uint32_t evt); +int32_t l502_port_cycle_check_setup(t_x502_hnd hnd, uint32_t ch, uint32_t *done); + + + +int32_t l502_iface_flash_rd(t_x502_hnd hnd, uint32_t addr, uint8_t* data, uint32_t size); +int32_t l502_iface_flash_wr(t_x502_hnd hnd, uint32_t addr, const uint8_t* data, uint32_t size); +int32_t l502_iface_flash_erase(t_x502_hnd hnd, uint32_t addr, uint32_t size); +int32_t l502_iface_flash_set_prot(t_x502_hnd hnd, uint32_t prot, const uint8_t* prot_data, uint32_t size); + +int32_t l502_iface_bf_mem_block_rd(t_x502_hnd hnd, uint32_t addr, uint32_t *block, uint32_t size); +int32_t l502_iface_bf_mem_block_wr(t_x502_hnd hnd, uint32_t addr, const uint32_t *block, uint32_t size); +int32_t l502_iface_bf_firm_load(t_x502_hnd hnd, const char *filename); + +int32_t l502_devlist_gen(t_x502_devrec *info, void *iface_data); + +#endif // L502API_PRIVATE_H + diff --git a/x502api-1.1.34/devs/l502/linux/l502_ioctls.h b/x502api-1.1.34/devs/l502/linux/l502_ioctls.h new file mode 100644 index 0000000..51148b4 --- /dev/null +++ b/x502api-1.1.34/devs/l502/linux/l502_ioctls.h @@ -0,0 +1,78 @@ +#ifndef LPCIE_IOCTLS_H +#define LPCIE_IOCTLS_H + +#include "linux/ioctl.h" +#include "linux/types.h" + + +#define L502_BF_IO_SIZE 8 + +#define LPCIE_DEVNAME_SIZE 32 +#define LPCIE_SERIAL_SIZE 32 +#define LPCIE_SOFTVER_SIZE 32 +#define LPCIE_REVISION_SIZE 16 +#define LPCIE_MODIFICATION_SIZE 16 +#define LPCIE_SPECINFO_SIZE 64 + +/** Информация о модуле */ +typedef struct +{ + char Name[LPCIE_DEVNAME_SIZE]; /**< название модуля ("L-502") */ + char Serial[LPCIE_SERIAL_SIZE]; /**< серийный номер изделия */ + char SoftVer[LPCIE_SOFTVER_SIZE]; /**< версия ПО контроллера */ + char Revision[LPCIE_REVISION_SIZE]; /**< ревизия платы */ + char Modification[LPCIE_MODIFICATION_SIZE]; /**< опции */ + char SpecInfo[LPCIE_SPECINFO_SIZE]; /**< резервная информация */ +} t_lpcie_devinfo; + +typedef struct +{ + __u32 addr; + __u32 val; +} t_lpcie_mem_rw; + +typedef struct +{ + __u32 page_size; /** размер каждой страницы */ + __u32 irq_step; /** количество отсчетов, после которого генерится прерывание */ + __u16 pages_cnt; /** количество страниц */ + __u16 packet_size; /** размер пакета по PCI-Express */ +} t_lpcie_dma_params; + +typedef struct +{ + uint32_t addr; + uint32_t reserv; + uint32_t buf[L502_BF_IO_SIZE]; +} t_l502_bf_rw; + +#define LPCIE_IO_MAGIC 'L' + +#define LPCIE_IOCTL_GET_DEVINFO _IOR(LPCIE_IO_MAGIC, 0x80, t_lpcie_devinfo) +#define LPCIE_IOCTL_TIMER_START _IOW(LPCIE_IO_MAGIC, 0x81, __u32) +#define LPCIE_IOCTL_TIMER_STOP _IO(LPCIE_IO_MAGIC, 0x82) +#define LPCIE_IOCTL_READ_CFG _IOWR(LPCIE_IO_MAGIC, 0x83, __u32) +#define LPCIE_IOCTL_MEM_FPGA_RD _IOWR(LPCIE_IO_MAGIC, 0x86, t_lpcie_mem_rw) +#define LPCIE_IOCTL_MEM_FPGA_WR _IOW(LPCIE_IO_MAGIC, 0x87, t_lpcie_mem_rw) +#define LPCIE_IOCTL_MEM_BLOCK_RD _IOWR(LPCIE_IO_MAGIC, 0x86, t_lpcie_mem_rw) + +#define LPCIE_IOCTL_ADC_START _IO(LPCIE_IO_MAGIC, 0x90) +#define LPCIE_IOCTL_ADC_STOP _IO(LPCIE_IO_MAGIC, 0x91) +#define LPCIE_IOCTL_DAC_START _IO(LPCIE_IO_MAGIC, 0x92) +#define LPCIE_IOCTL_DAC_STOP _IO(LPCIE_IO_MAGIC, 0x93) +#define LPCIE_IOCTL_SET_ADC_DMA_PAR _IOW(LPCIE_IO_MAGIC, 0x94, t_lpcie_dma_params) +#define LPCIE_IOCTL_SET_DAC_DMA_PAR _IOW(LPCIE_IO_MAGIC, 0x95, t_lpcie_dma_params) +#define LPCIE_IOCTL_DAC_RST_BUFS _IO(LPCIE_IO_MAGIC, 0x96) + +#define LPCIE_IOCTL_ADC_GET_RDY _IOR(LPCIE_IO_MAGIC, 0x97, __u32) +#define LPCIE_IOCTL_DAC_GET_FREE _IOR(LPCIE_IO_MAGIC, 0x98, __u32) + +#define LPCIE_IOCTL_DAC_SET_CYCLE_BUF _IOW(LPCIE_IO_MAGIC, 0x99, __u32) +#define LPCIE_IOCTL_DAC_SET_CYCLE_DATA _IOW(LPCIE_IO_MAGIC, 0x9A, __u32) + + +#define LPCIE_IOCTL_BF_RD _IOR(LPCIE_IO_MAGIC, 0xA0, t_l502_bf_rw) +#define LPCIE_IOCTL_BF_WR _IOW(LPCIE_IO_MAGIC, 0xA0, t_l502_bf_rw) +#define LPCIE_IOCTL_BF_HOST_IRQ _IO(LPCIE_IO_MAGIC, 0xA1) + +#endif // LPCIE_IOCTLS_H diff --git a/x502api-1.1.34/devs/l502/linux/l502_spec.c b/x502api-1.1.34/devs/l502/linux/l502_spec.c new file mode 100644 index 0000000..61ab6b4 --- /dev/null +++ b/x502api-1.1.34/devs/l502/linux/l502_spec.c @@ -0,0 +1,369 @@ +#include "../l502api_private.h" +#include "../lpcie_ioctls.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#define LPCIE_CLASS_DIR "/sys/class/lpcie" + + +static int32_t f_ioctl(int fd, unsigned long int req, void* val) { + return ioctl(fd, req, val) ? X502_ERR_IOCTL_FAILD : X502_ERR_OK; +} + + + + +int32_t l502_port_fpga_reg_write(t_x502_hnd hnd, uint32_t reg, uint32_t val) { + t_lpcie_mem_rw mem_wr = {reg,val}; + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_MEMFPGA_WR, &mem_wr); +} + + +int32_t l502_port_fpga_reg_read(t_x502_hnd hnd, uint32_t reg, uint32_t *val) { + t_lpcie_mem_rw mem_wr = {reg,0}; + int32_t err = f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_MEMFPGA_RD, &mem_wr); + if ((err == X502_ERR_OK) && (val != NULL)) + *val = mem_wr.val; + return err; +} + +int32_t l502_port_stream_set_params(t_x502_hnd hnd, t_lpcie_stream_ch_params *par) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_SET_PARAMS, par); +} + +int32_t l502_port_stream_start(t_x502_hnd hnd, uint32_t ch, uint32_t single) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), single ? LPCIE_IOCTL_STREAM_START_SINGLE : + LPCIE_IOCTL_STREAM_START, &ch); +} + +int32_t l502_port_stream_stop(t_x502_hnd hnd, uint32_t ch, uint32_t flags) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_STOP, &ch); +} + +int32_t l502_port_stream_free(t_x502_hnd hnd, uint32_t ch, uint32_t flags) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_FREE, &ch); +} + + +int32_t l502_port_stream_rdy_size(t_x502_hnd hnd, uint32_t ch, uint32_t *rdy_size) { + t_lpcie_get_rdy_par rdy_par = {ch,0}; + int32_t err = f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_GET_RDY_SIZE, &rdy_par); + if ((err == X502_ERR_OK) && (rdy_size != NULL)) + *rdy_size = rdy_par.rdy_size; + return err; +} + +int32_t l502_port_cycle_load_start(t_x502_hnd hnd, uint32_t ch, uint32_t size, uint32_t min_irq_step) { + t_lpcie_cycle_set_par par; + memset(&par, 0, sizeof(par)); + par.ch = ch; + par.size = size; + par.irq_step = min_irq_step; + return f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_LOAD, &par); +} + +int32_t l502_port_cycle_setup(t_x502_hnd hnd, uint32_t ch, uint32_t evt) { + t_lpcie_cycle_evt_par par; + memset(&par, 0, sizeof(par)); + par.ch = ch; + par.evt = evt; + return f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_SWITCH, &par); +} + +int32_t l502_port_cycle_stop(t_x502_hnd hnd, uint32_t ch, uint32_t evt) { + t_lpcie_cycle_evt_par par; + memset(&par, 0, sizeof(par)); + par.ch = ch; + par.evt = evt; + return f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_STOP, &par); +} + +int32_t l502_port_cycle_check_setup(t_x502_hnd hnd, uint32_t ch, uint32_t *done) { + t_lpcie_cycle_check_setup_par par; + int32_t err; + + memset(&par, 0, sizeof(par)); + par.ch = ch; + err = f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_CHECK_SETUP, &par); + if (err == X502_ERR_OK) + *done = par.done; + return err; +} + +int32_t l502_port_renew_info(t_x502_hnd hnd) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_RELOAD_DEVINFO, NULL); +} + +int32_t l502_port_get_drv_ver(t_x502_hnd hnd, uint32_t *ver) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_GET_DRV_VERSION, ver); +} + + +int32_t l502_port_stream_read(t_x502_hnd hnd, uint32_t *buff, uint32_t size, uint32_t timeout) { + struct timeval tmval = {timeout/1000, (timeout%1000)*1000}; + uint32_t rcv_size = 0; + int out = 0; + int32_t err = X502_ERR_OK; + while (!out && (err == X502_ERR_OK) && (rcv_size < size)) { + fd_set rd_set; + int sel = 0; + + FD_ZERO(&rd_set); + FD_SET(L502_PCI_IFACE_FILE(hnd), &rd_set); + sel = select(L502_PCI_IFACE_FILE(hnd)+1, &rd_set, NULL, NULL, &tmval); + if ((sel > 0) && (FD_ISSET(L502_PCI_IFACE_FILE(hnd), &rd_set))) { + ssize_t rd = read(L502_PCI_IFACE_FILE(hnd), &buff[rcv_size], (size-rcv_size)*4); + if (rd>0) { + rcv_size += rd/4; + } else if (rd == 0) { + out = 1; + } else { + err = X502_ERR_RECV; + } + } else if (sel==0) { + out = 1; + } else { + err = X502_ERR_RECV; + } + } + return err != X502_ERR_OK ? err : (int32_t)rcv_size; +} + + +int32_t l502_port_stream_write(t_x502_hnd hnd, const uint32_t *buff, uint32_t size, + uint32_t timeout) { + struct timeval tmval = {timeout/1000, (timeout%1000)*1000}; + uint32_t snd_size = 0; + int32_t err = X502_ERR_OK; + int out = 0; + + while (!out && (err == X502_ERR_OK) && (snd_size < size)) { + fd_set wr_set; + int sel = 0; + + FD_ZERO(&wr_set); + FD_SET(L502_PCI_IFACE_FILE(hnd), &wr_set); + sel = select(L502_PCI_IFACE_FILE(hnd)+1, NULL, &wr_set, NULL, &tmval); + if ((sel > 0) && (FD_ISSET(L502_PCI_IFACE_FILE(hnd), &wr_set))) { + ssize_t wr = write(L502_PCI_IFACE_FILE(hnd), &buff[snd_size], (size-snd_size)*4); + if (wr > 0) { + snd_size += wr/4; + } else if (wr==0) { + out = 1; + } else { + err = X502_ERR_SEND; + } + } else if (sel==0) { + out = 1; + } else { + err = X502_ERR_SEND; + } + } + return err != X502_ERR_OK ? err : (int32_t)snd_size; +} + + +static int32_t f_get_file_par(const char *path, char *filebuf, + const char *file, char *res, uint32_t req_size, + uint32_t *read_size) { + int f; + int32_t err = X502_ERR_OK; + strcpy(filebuf, path); + strcat(filebuf, file); + + f = open (filebuf, O_RDONLY); + if (f == -1) { + err = X502_ERR_GET_INFO; + } else { + ssize_t rd = read(f, res, req_size); + if (rd < 0) { + err = X502_ERR_GET_INFO; + } else { + if (read_size!=NULL) + *read_size = rd; + } + close(f); + } + return err; +} + + +static void f_del_eol(char *str) { + for ( ; *str; str++) { + if ((*str=='\n') || (*str=='\r')) + *str = '\0'; + } +} + +static int32_t f_fill_devlist(const char *devname, t_x502_devrec *info) { + int32_t err = 0; + + int path_len = strlen(devname) + strlen(LPCIE_CLASS_DIR) + 2; + char *filename = malloc(path_len + 21); + char *path = malloc(path_len); + + if ((filename != NULL) && (path != NULL)) { + sprintf(path, LPCIE_CLASS_DIR "/%s", devname); + + err = f_get_file_par(path, filename, "/name", info->devname, + sizeof(info->devname), NULL); + if (err == X502_ERR_OK) + f_del_eol(info->devname); + + if (err == X502_ERR_OK) { + /* получаем серийный номер устройства */ + err = f_get_file_par(path, filename, "/sn", info->serial, + sizeof(info->serial), NULL); + if (err == X502_ERR_OK) + f_del_eol(info->serial); + } + + /* получаем информацию, открыто ли устройство */ + if (err == X502_ERR_OK) { + char val = '0'; + err = f_get_file_par(path, filename, "/opened", &val, 1, NULL); + if ((err == X502_ERR_OK) && (val!='0')) + info->flags |= X502_DEVFLAGS_DEVREC_OPENED; + } + + /* получаем информацию, присутствует ли BlackFin */ + if (err == X502_ERR_OK) { + char val = '0'; + if ((f_get_file_par(path, filename, "/bf", &val, 1, NULL) == X502_ERR_OK) + && (val != '0')) { + info->flags |= X502_DEVFLAGS_BF_PRESENT; + } + + if ((f_get_file_par(path, filename, "/dac", &val, 1, NULL) == X502_ERR_OK) + && (val != '0')) { + info->flags |= X502_DEVFLAGS_DAC_PRESENT; + } + + if ((f_get_file_par(path, filename, "/gal", &val, 1, NULL) == X502_ERR_OK) + && (val != '0')) { + info->flags |= X502_DEVFLAGS_GAL_PRESENT; + } + } + + if (err == X502_ERR_OK) { + char *devname_cpy = malloc(strlen(devname)+1); + if (devname_cpy!=NULL) { + strcpy(devname_cpy, devname); + err = l502_devlist_gen(info, devname_cpy); + if (err != X502_ERR_OK) { + free(devname_cpy); + } + } else { + err = X502_ERR_MEMORY_ALLOC; + } + } + } else { + err = X502_ERR_MEMORY_ALLOC; + } + + free(filename); + free(path); + return err; +} + + +int32_t l502_port_free_iface_data(void *intptr) { + free(intptr); + return X502_ERR_OK; +} + +int32_t l502_port_open(t_x502_hnd hnd, const t_x502_devrec *devinfo) { + const char *devname = (const char *)devinfo->internal->iface_data; + int32_t err = X502_ERR_OK; + int path_len = strlen(devname)+6; + char *path = malloc(path_len); + + if (path != NULL) { + int file; + snprintf(path, path_len, "/dev/%s", devname); + file = open(path, O_RDWR); + if (file != -1) { + hnd->iface_data = malloc(sizeof(t_pci_iface_data)); + if (hnd->iface_data == NULL) { + err = X502_ERR_MEMORY_ALLOC; + close(file); + } else { + L502_PCI_IFACE_FILE(hnd) = file; + } + } else { + /** @todo Разобрать коды ошибок */ + err = X502_ERR_DEVICE_OPEN; + } + free(path); + } else { + err = X502_ERR_MEMORY_ALLOC; + } + return err; +} + +int32_t l502_port_close(t_x502_hnd hnd) { + if (hnd->iface_data !=NULL) { + close(L502_PCI_IFACE_FILE(hnd)); + free(hnd->iface_data); + hnd->iface_data = NULL; + } + return X502_ERR_OK; +} + + + +X502_EXPORT(int32_t) L502_GetDevRecordsList(t_x502_devrec *list, uint32_t size, + uint32_t flags, uint32_t *devcnt) { + uint32_t curcnt = 0; + int32_t err = X502_ERR_OK; + /* все устройства, поддерживаемые драйвером LPCIE, создают папку + в директории класса устройств lpcie*/ + DIR *class_dir = opendir(LPCIE_CLASS_DIR); + if (class_dir!=NULL) { + struct dirent *dev_ent; + /* читаем все записи в директории класса */ + while ((dev_ent=readdir(class_dir))!=NULL) { + /* проверяем, что имя начинается с lpcie */ + if (!memcmp(dev_ent->d_name, "lpcie", sizeof("lpcie")-1)) { + t_x502_devrec info; + int info_used = 0; + X502_DevRecordInit(&info); + + /* получаем информацию о устройстве из служебных файлов, + * предоставляемых драйвером */ + if (f_fill_devlist(dev_ent->d_name, &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); + } + } + closedir(class_dir); + } + + if (devcnt != NULL) + *devcnt = curcnt; + + return err != X502_ERR_OK ? err : curcnt > size ? (int32_t)size : (int32_t)curcnt ; +} diff --git a/x502api-1.1.34/devs/l502/lpcie_ioctls.h b/x502api-1.1.34/devs/l502/lpcie_ioctls.h new file mode 100644 index 0000000..5faeaad --- /dev/null +++ b/x502api-1.1.34/devs/l502/lpcie_ioctls.h @@ -0,0 +1,144 @@ +/***************************************************************************//** + * @file lpcie_ioctls.h + * Файл содержит определения управляющих запросов драйвера lpcie и типов данных, + * используемых для передачи параметров управляющих запросов + * @author Borisov Alexey + * @date 23.05.2012 + * ****************************************************************************/ +#ifndef LPCIE_IOCTLS_H +#define LPCIE_IOCTLS_H + +/** Проверка по версии дарайвера, поддерживается ли запрос LPCIE_IOCTL_CYCLE_CHECK_SETUP */ +#define LPCIE_IOCTL_SUPPORT_CYCLE_CHECK_SETUP(ver) (ver >= 0x01000900) + +/** Варианты событий, по которым должно произойти переключение + циклического сигнала */ +typedef enum { + LPCIE_CYCLE_SW_EVT_IMMIDIATLY = 1, /**< сразу по получению команды */ + LPCIE_CYCLE_SW_EVT_END_OF_CYCLE = 2 /**< по завершению текущего цикла */ +} t_lpcie_cycle_sw_evt; + + +/** параметры для записи значения регистра */ +typedef struct { + uint32_t addr; /** Адрес регистра */ + uint32_t val; /** Значение регистра */ +} t_lpcie_mem_rw; + +/** настройки канала DMA, передаваемые вместе с LPCIE_IOCTL_STREAM_SET_PARAMS */ +typedef struct { + uint32_t ch; /** канал DMA (ввод/вывод) */ + uint32_t res[2]; /** резерв */ + uint32_t buf_size; /** размер каждой страницы памяти в PC */ + uint32_t irq_step; /** через сколько переданных отсчетов будет + генерироваться прерывание */ + uint32_t res2[3]; +} t_lpcie_stream_ch_params; + +/** параметры для установки циклического сигнала */ +typedef struct { + uint32_t ch; /** канал DMA (доступно только на вывод) */ + uint32_t size; /** количество отсчетов в циклическом сигнале */ + uint32_t irq_step; /** шаг генерации прерываний */ + uint32_t res; /** резерв */ +} t_lpcie_cycle_set_par; + +/** параметры для остановки/смены циклического сигнала */ +typedef struct { + uint32_t ch; /** канал DMA (доступно только на вывод) */ + uint32_t evt; /** событие для смены сигнала из #t_lpcie_cycle_sw_evt */ + uint32_t res[2]; /** резерв */ +} t_lpcie_cycle_evt_par; + +/** параметры для запроса LPCIE_IOCTL_CYCLE_CHECK_SETUP */ +typedef struct { + uint32_t ch; /** канал DMA (доступно только на вывод) */ + uint32_t done; /** признак, завершена ли установка циклического сигнала */ +} t_lpcie_cycle_check_setup_par; + +/** параметры запроса для получения количества готовых для ввода или вывода + отсчетов */ +typedef struct { + uint32_t ch; /** канал DMA (ввод/вывод) */ + uint32_t rdy_size; /** Количество отсчетов доступных на ввод или вывод */ +} t_lpcie_get_rdy_par; + + +#ifdef _WIN32 +#define LPCIE_IOCTL_GET_DRV_VERSION \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED , FILE_ANY_ACCESS) + + +#define LPCIE_IOCTL_MEMFPGA_RD \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x810, METHOD_BUFFERED , FILE_ANY_ACCESS) + +#define LPCIE_IOCTL_MEMFPGA_WR \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x811, METHOD_BUFFERED , FILE_ANY_ACCESS) + +#define LPCIE_IOCTL_POWER_DONW \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x820, METHOD_BUFFERED , FILE_ANY_ACCESS) +#define LPCIE_IOCTL_RELOAD_DEVINFO \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x821, METHOD_BUFFERED , FILE_ANY_ACCESS) + + + +#define LPCIE_IOCTL_STREAM_SET_PARAMS \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x840, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_STREAM_GET_PARAMS \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x841, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_STREAM_GET_RDY_SIZE \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x842, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define LPCIE_IOCTL_STREAM_START \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x844, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_STREAM_STOP \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x845, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_STREAM_FREE \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x846, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_STREAM_START_SINGLE \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x847, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_CYCLE_LOAD \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x850, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_CYCLE_SWITCH \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x851, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_CYCLE_STOP \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x852, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define LPCIE_IOCTL_CYCLE_CHECK_SETUP \ + CTL_CODE(FILE_DEVICE_UNKNOWN, 0x853, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#else + +#define LPCIE_IO_MAGIC 'L' + +#define LPCIE_IOCTL_GET_DRV_VERSION _IOR(LPCIE_IO_MAGIC, 0x800, uint32_t) + +#define LPCIE_IOCTL_MEMFPGA_RD _IOWR(LPCIE_IO_MAGIC, 0x810, t_lpcie_mem_rw) +#define LPCIE_IOCTL_MEMFPGA_WR _IOW(LPCIE_IO_MAGIC, 0x811, t_lpcie_mem_rw) + +#define LPCIE_IOCTL_POWER_DONW _IO(LPCIE_IO_MAGIC, 0x820) +#define LPCIE_IOCTL_RELOAD_DEVINFO _IO(LPCIE_IO_MAGIC, 0x821) + + +#define LPCIE_IOCTL_STREAM_SET_PARAMS _IOW(LPCIE_IO_MAGIC, 0x840, t_lpcie_stream_ch_params) +#define LPCIE_IOCTL_STREAM_GET_PARAMS _IOWR(LPCIE_IO_MAGIC, 0x841, t_lpcie_stream_ch_params) +#define LPCIE_IOCTL_STREAM_GET_RDY_SIZE _IOWR(LPCIE_IO_MAGIC, 0x842, t_lpcie_get_rdy_par) + + +#define LPCIE_IOCTL_STREAM_START _IOW(LPCIE_IO_MAGIC, 0x844, uint32_t) +#define LPCIE_IOCTL_STREAM_STOP _IOW(LPCIE_IO_MAGIC, 0x845, uint32_t) +#define LPCIE_IOCTL_STREAM_FREE _IOW(LPCIE_IO_MAGIC, 0x846, uint32_t) +#define LPCIE_IOCTL_STREAM_START_SINGLE _IOW(LPCIE_IO_MAGIC, 0x847, uint32_t) + +#define LPCIE_IOCTL_CYCLE_LOAD _IOW(LPCIE_IO_MAGIC, 0x850, t_lpcie_cycle_set_par) +#define LPCIE_IOCTL_CYCLE_SWITCH _IOW(LPCIE_IO_MAGIC, 0x851, t_lpcie_cycle_evt_par) +#define LPCIE_IOCTL_CYCLE_STOP _IOW(LPCIE_IO_MAGIC, 0x852, t_lpcie_cycle_evt_par) +#define LPCIE_IOCTL_CYCLE_CHECK_SETUP _IOWR(LPCIE_IO_MAGIC, 0x853, t_lpcie_cycle_check_setup_par) + +#endif + + + + + + +#endif // LPCIE_IOCTLS_H diff --git a/x502api-1.1.34/devs/l502/pas/l502api.pas b/x502api-1.1.34/devs/l502/pas/l502api.pas new file mode 100644 index 0000000..a94168b --- /dev/null +++ b/x502api-1.1.34/devs/l502/pas/l502api.pas @@ -0,0 +1,805 @@ +unit l502api; +interface +uses Windows, SysUtils, x502api; + + + + // + function L502_GetSerialList(out serials: array of string; flags: LongWord; out devcnt: LongWord) : LongInt; overload; + function L502_GetSerialList(out serials: array of string; flags: LongWord) : LongInt; overload; + // + function L502_Open(hnd: t_x502_hnd; serial: string): LongInt; stdcall; + + function L502_GetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord; out devcnt: LongWord) : LongInt; overload; + function L502_GetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord) : LongInt; overload; + + + +{ ----------- , ----------} +const + // + L502_LTABLE_MAX_CH_CNT = 256; + // + L502_ADC_RANGE_CNT = 6; + // + L502_LCH_AVG_SIZE_MAX = 128; + // + L502_ADC_FREQ_DIV_MAX = (1024*1024); + // + L502_DIN_FREQ_DIV_MAX = (1024*1024); + // + L502_ADC_INTERFRAME_DELAY_MAX = $1FFFFF; + // BlackFin + L502_BF_CMD_DEFAULT_TOUT = 500; + + // , + L502_ADC_SCALE_CODE_MAX = 6000000; + // , + L502_DAC_SCALE_CODE_MAX = 30000; + + // + L502_DEVNAME_SIZE = X502_DEVNAME_SIZE; + // + L502_SERIAL_SIZE = X502_SERIAL_SIZE; + + // Flash- + L502_FLASH_USER_SIZE = $100000; + // BlackFin + L502_BF_REQ_TOUT = 500; + // + L502_DAC_RANGE = 5; + // + L502_DAC_CH_CNT = 2; + + // , , + L502_STREAM_IN_MSG_OVERFLOW = $01010000; + + // + L502_EXT_REF_FREQ_MAX = 2000000; + + + { ----------------- ---------------------------} + // + L502_ERR_OK = 0; + // + L502_ERR_INVALID_HANDLE = -1; + // + L502_ERR_MEMORY_ALLOC = -2; + // + L502_ERR_ALREADY_OPENED = -3; + // + L502_ERR_DEVICE_NOT_FOUND = -4; + // ( - , ) + L502_ERR_DEVICE_ACCESS_DENIED = -5; + // + L502_ERR_DEVICE_OPEN = -6; + // + L502_ERR_INVALID_POINTER = -7; + // + L502_ERR_STREAM_IS_RUNNING = -8; + // + L502_ERR_RECV = -9; + // + L502_ERR_SEND = -10; + // + L502_ERR_STREAM_OVERFLOW = -11; + // + L502_ERR_UNSUP_STREAM_MSG = -12; + // + L502_ERR_MUTEX_CREATE = -13; + // + L502_ERR_MUTEX_INVALID_HANDLE = -14; + // + L502_ERR_MUTEX_LOCK_TOUT = -15; + // + L502_ERR_MUTEX_RELEASE = -16; + // + L502_ERR_INSUFFICIENT_SYSTEM_RESOURCES= -17; + // + L502_ERR_NOT_IMPLEMENTED = -18; + // + L502_ERR_INSUFFICIENT_ARRAY_SIZE = -19; + // FPGA + L502_ERR_FPGA_REG_READ = -20; + // FPGA + L502_ERR_FPGA_REG_WRITE = -21; + // + L502_ERR_STREAM_IS_NOT_RUNNING = -22; + // + L502_ERR_INVALID_LTABLE_SIZE = -102; + // + L502_ERR_INVALID_LCH_NUMBER = -103; + // + L502_ERR_INVALID_LCH_RANGE = -104; + // + L502_ERR_INVALID_LCH_MODE = -105; + // + L502_ERR_INVALID_LCH_PHY_NUMBER = -106; + // + L502_ERR_INVALID_LCH_AVG_SIZE = -107; + // + L502_ERR_INVALID_ADC_FREQ_DIV = -108; + // + L502_ERR_INVALID_DIN_FREQ_DIV = -108; + // L502 + L502_ERR_INVALID_MODE = -109; + // + L502_ERR_INVALID_DAC_CHANNEL = -110; + // + L502_ERR_INVALID_REF_FREQ = -111; + // + L502_ERR_INVALID_INTERFRAME_DELAY = -112; + // + L502_ERR_INVALID_SYNC_MODE = -113; + // DMA + L502_ERR_INVALID_DMA_CH = -114; + // + L502_ERR_REF_FREQ_NOT_LOCKED = -131; + // + L502_ERR_IOCTL_FAILD = -132; + // + L502_ERR_IOCTL_TIMEOUT = -133; + // + L502_ERR_GET_INFO = -134; + // + L502_ERR_DIG_IN_NOT_RDY = -135; + // + L502_ERR_RECV_INSUFFICIENT_WORDS = -136; + // , , + L502_ERR_DAC_NOT_PRESENT = -137; + // + L502_ERR_PROC_INVALID_CH_NUM = -140; + // + L502_ERR_PROC_INVALID_CH_RANGE = -141; + // Flash- + L502_ERR_FLASH_INVALID_ADDR = -142; + // Flash- + L502_ERR_FLASH_INVALID_SIZE = -143; + // Flash- + L502_ERR_FLASH_WRITE_TOUT = -144; + // Flash- + L502_ERR_FLASH_ERASE_TOUT = -145; + // Flash- 4 + L502_ERR_FLASH_SECTOR_BOUNDARY = -146; + // BlackFin + L502_ERR_LDR_FILE_OPEN = -180; + // BlackFin + L502_ERR_LDR_FILE_READ = -181; + // BlackFin + L502_ERR_LDR_FILE_FORMAT = -182; + // LDR-, BlackFin HDMA + L502_ERR_LDR_FILE_UNSUP_FEATURE = -183; + // BlackFin + L502_ERR_LDR_FILE_UNSUP_STARTUP_ADDR = -184; + // / BlackFin + L502_ERR_BF_REQ_TIMEOUT = -185; + // BlackFin + L502_ERR_BF_CMD_IN_PROGRESS = -186; + // BlackFin + L502_ERR_BF_CMD_TIMEOUT = -187; + // BlackFin + L502_ERR_BF_CMD_RETURN_INSUF_DATA = -188; + // BlackFin + L502_ERR_BF_LOAD_RDY_TOUT = -189; + // + // + L502_ERR_BF_NOT_PRESENT = -190; + // BlackFin HDMA + L502_ERR_BF_INVALID_ADDR = -191; + // , BlackFin + L502_ERR_BF_INVALID_CMD_DATA_SIZE = -192; + + + {-------- , ------} + // , , + // + L502_GETDEVS_FLAGS_ONLY_NOT_OPENED = X502_GETDEVS_FLAGS_ONLY_NOT_OPENED; + + + + {-------- . ---------------} + // ( ) + L502_DIGOUT_WORD_DIS_H = $00020000; + // + L502_DIGOUT_WORD_DIS_L = $00010000; + + + {-------- -------------------} + L502_REF_FREQ_2000KHZ = 2000000; // 2 + L502_REF_FREQ_1500KHZ = 1500000; // 1.5 + + {-------- ----------------------} + L502_ADC_RANGE_10 = 0; // +/-10V + L502_ADC_RANGE_5 = 1; // +/-5V + L502_ADC_RANGE_2 = 2; // +/-2V + L502_ADC_RANGE_1 = 3; // +/-1V + L502_ADC_RANGE_05 = 4; // +/-0.5V + L502_ADC_RANGE_02 = 5; // +/-0.2V + + + {-------- ------------------} + L502_LCH_MODE_COMM = 0; // + L502_LCH_MODE_DIFF = 1; // + L502_LCH_MODE_ZERO = 2; // + + + {-------- ------------------------------------} + L502_SYNC_INTERNAL = 0; // + L502_SYNC_EXTERNAL_MASTER = 1; // + L502_SYNC_DI_SYN1_RISE = 2; // DI_SYN1 + L502_SYNC_DI_SYN2_RISE = 3; // DI_SYN2 + L502_SYNC_DI_SYN1_FALL = 6; // DI_SYN1 + L502_SYNC_DI_SYN2_FALL = 7; // DI_SYN2 + + {-------- , -------------------} + L502_PROC_FLAGS_VOLT = 1; // , + // + + {-------- -----------------} + L502_STREAM_ADC = $01; // + L502_STREAM_DIN = $02; // + L502_STREAM_DAC1 = $10; // + L502_STREAM_DAC2 = $20; // + L502_STREAM_DOUT = $40; // + // , + L502_STREAM_ALL_IN = L502_STREAM_ADC or L502_STREAM_DIN; + // , + L502_STREAM_ALL_OUT = L502_STREAM_DAC1 or L502_STREAM_DAC2 or L502_STREAM_DOUT; + + + {--- , -----} + L502_STREAM_OUT_WORD_TYPE_DOUT = $0; // + L502_STREAM_OUT_WORD_TYPE_DAC1 = $40000000; // 1- + L502_STREAM_OUT_WORD_TYPE_DAC2 = $80000000; // 2- + + {------------------ L502 ------------------------------} + L502_MODE_FPGA = 0; // + // BlackFin + L502_MODE_DSP = 1; // + // , + // + L502_MODE_DEBUG = 2; // + + {------------------- -----------------------------------} + L502_DAC_CH1 = 0; // + L502_DAC_CH2 = 1; // + + {----------- , -----------------} + // , + // . , , + // + L502_DAC_FLAGS_VOLT = $0001; + // , + // . + L502_DAC_FLAGS_CALIBR = $0002; + + {------------------ DMA ------------------------------------} + L502_DMA_CH_IN = 0; // DMA + L502_DMA_CH_OUT = 1; // DMA + + {--- , ----} + L502_PULLUPS_DI_H = $01; // + L502_PULLUPS_DI_L = $02; // + L502_PULLUPS_DI_SYN1 = $04; // SYN1 + L502_PULLUPS_DI_SYN2 = $08; // SYN2 + + + + {--------------- , ---------------} + // + L502_DEVFLAGS_DAC_PRESENT = X502_DEVFLAGS_DAC_PRESENT; + // BlackFin + L502_DEVFLAGS_BF_PRESENT = X502_DEVFLAGS_BF_PRESENT; + // + L502_DEVFLAGS_GAL_PRESENT = X502_DEVFLAGS_GAL_PRESENT; + // , Flash- + L502_DEVFLAGS_FLASH_DATA_VALID = $00010000; + // , Flash- + // + L502_DEVFLAGS_FLASH_ADC_CALIBR_VALID = $00020000; + // , Flash- + // + L502_DEVFLAGS_FLASH_DAC_CALIBR_VALID = $00040000; + + {---------------- ----------------------} + // , + // . + L502_OUT_CYCLE_FLAGS_FORCE = $01; + + +type + // - + t_l502_hnd = t_x502_hnd; + + + { } + t_l502_cbr_coef = record + offs: Double; // + k : Double; // + end; + + + { . } + t_l502_cbr = record + // + adc: array[0..L502_ADC_RANGE_CNT-1] of t_l502_cbr_coef; + res1: array[0..63] of LongWord; + // + dac: array[0..L502_DAC_CH_CNT-1] of t_l502_cbr_coef; + res2: array[0..19] of LongWord; + end; + + { L502.} + t_l502_info = record + name: array[0..L502_DEVNAME_SIZE-1] of AnsiChar; // ("L502") + serial: array[0..L502_SERIAL_SIZE-1] of AnsiChar; // + devflags: LongWord; // + fpga_ver : Word; // ( - , - ) + plda_ver : Byte; // , + res : array[0..120] of Byte; // + cbr : t_l502_cbr; // ( Flash-) + end; + + function L502_Create(): t_l502_hnd; stdcall; + function L502_Free(hnd: t_l502_hnd): LongInt; stdcall; + + // + function L502_Close(hnd: t_l502_hnd): LongInt; stdcall; + // + function L502_GetDevInfo(hnd: t_l502_hnd; out info: t_l502_info) : LongInt; stdcall; + + // + function L502_Configure(hnd: t_l502_hnd; flags: LongWord): LongInt; stdcall; + // + function L502_SetLChannel(hnd: t_l502_hnd; lch, phy_ch, mode, range, avg: LongWord): LongInt; stdcall; + // + function L502_SetLChannelCount(hnd: t_l502_hnd; lch_cnt : LongWord): LongInt; stdcall; + // + function L502_GetLChannelCount(hnd: t_l502_hnd; out lch_cnt: LongWord): LongInt; stdcall; + // + function L502_SetAdcFreqDivider(hnd: t_l502_hnd; adc_freq_div : LongWord): LongInt; stdcall; + // + function L502_SetAdcInterframeDelay(hnd: t_l502_hnd; delay : LongWord): LongInt; stdcall; + // + function L502_SetDinFreqDivider(hnd: t_l502_hnd; din_freq_div: LongWord): LongInt; stdcall; + // + function L502_SetAdcFreq(hnd: t_l502_hnd; var f_acq, f_frame: Double): LongInt; stdcall; + // + function L502_SetDinFreq(hnd: t_l502_hnd; var f_din: Double): LongInt; stdcall; + // + function L502_GetAdcFreq(hnd: t_l502_hnd; out f_acq, f_frame: Double): LongInt; stdcall; + // . + function L502_SetRefFreq(hnd: t_l502_hnd; freq: LongWord): LongInt; stdcall; + // . + function L502_SetSyncMode(hnd: t_l502_hnd; sync_mode: LongWord): LongInt; stdcall; + // . + function L502_SetSyncStartMode(hnd: t_l502_hnd; sync_start_mode: LongWord): LongInt; stdcall; + // + function L502_SetMode(hnd: t_l502_hnd; mode: LongWord): LongInt; stdcall; + // . + function L502_GetMode(hnd: t_l502_hnd; out mode: LongWord): LongInt; stdcall; + // . + function L502_SetAdcCoef(hnd: t_l502_hnd; range: LongWord; k, offs: Double): LongInt; stdcall; + // . + function L502_GetAdcCoef(hnd: t_l502_hnd; range: LongWord; out k, offs: Double): LongInt; stdcall; + + + {----------------------- - ------------------} + // . + function L502_AsyncOutDac(hnd: t_l502_hnd; ch: LongWord; data: Double; flags: LongWord): LongInt; stdcall; + // . + function L502_AsyncOutDig(hnd: t_l502_hnd; val, msk: LongWord): LongInt; stdcall; + // . + function L502_AsyncInDig(hnd: t_l502_hnd; out din: LongWord): LongInt; stdcall; + // . + function L502_AsyncGetAdcFrame(hnd: t_l502_hnd; flags: LongWord; tout: LongWord; out data: array of Double): LongInt; stdcall; + + + {-------------- - ----} + // /. + function L502_StreamsEnable(hnd: t_l502_hnd; streams: LongWord): LongInt; stdcall; + // /. + function L502_StreamsDisable(hnd: t_l502_hnd; streams: LongWord): LongInt; stdcall; + // /. + function L502_StreamsStart(hnd: t_l502_hnd): LongInt; stdcall; + // /. + function L502_StreamsStop(hnd: t_l502_hnd): LongInt; stdcall; + // , + function L502_IsRunning(hnd: t_l502_hnd): LongInt; stdcall; + + + // . + function L502_Recv(hnd: t_l502_hnd; out buf : array of LongWord; size: LongWord; tout : LongWord): LongInt; stdcall; + // . + function L502_Send(hnd: t_l502_hnd; const buf : array of LongWord; size: LongWord; tout: LongWord): LongInt; stdcall; + // . + function L502_ProcessAdcData(hnd: t_l502_hnd; const src: array of LongWord; + out dest: array of Double; var size : LongWord; + flags : LongWord): LongInt; stdcall; + // . + function L502_ProcessData(hnd: t_l502_hnd; const src: array of LongWord; size: LongWord; + flags : LongWord; out adc_data: array of Double; var adc_data_size : LongWord; + out din_data: array of LongWord; var din_data_size: LongWord):LongInt; stdcall; + // . + function L502_ProcessDataWithUserExt(hnd: t_l502_hnd; const src: array of LongWord; size: LongWord; + flags : LongWord; out adc_data: array of Double; var adc_data_size : LongWord; + out din_data: array of LongWord; var din_data_size: LongWord; + out usr_data: array of LongWord; var usr_data_size: LongWord):LongInt; stdcall; + // . + function L502_PrepareData(hnd: t_l502_hnd; const dac1, dac2: array of Double; + const digout: array of LongWord; size, flags : LongWord; + out out_buf: array of LongWord):LongInt; stdcall; + + // . + function L502_GetRecvReadyCount(hnd: t_l502_hnd; out rdy_cnt: LongWord):LongInt; stdcall; + // . + function L502_GetSendReadyCount(hnd: t_l502_hnd; out rdy_cnt: LongWord):LongInt; stdcall; + // . + function L502_GetNextExpectedLchNum(hnd: t_l502_hnd; out lch: LongWord):LongInt; stdcall; + // + function L502_PreloadStart(hnd: t_l502_hnd): LongInt; stdcall; + + // + function L502_OutCycleLoadStart(hnd: t_l502_hnd; size: LongWord):LongInt; stdcall; + // + function L502_OutCycleSetup(hnd: t_l502_hnd; flags: LongWord):LongInt; stdcall; + // + function L502_OutCycleStop(hnd: t_l502_hnd; flags: LongWord):LongInt; stdcall; + + // . + function L502_SetDmaBufSize(hnd: t_l502_hnd; dma_ch, size: LongWord): LongInt; stdcall; + // DMA. + function L502_SetDmaIrqStep(hnd: t_l502_hnd; dma_ch, step: LongWord): LongInt; stdcall; + + {------------ -------------------} + function L502_BfCheckFirmwareIsLoaded(hnd: t_l502_hnd; out version: LongWord): LongInt; stdcall; + /// BlackFin. + function L502_BfLoadFirmware(hnd: t_l502_hnd; filename: string): LongInt; stdcall; + /// . + function L502_BfMemRead(hnd: t_l502_hnd; addr : LongWord; out regs: array of LongWord; + size: LongWord): LongInt; stdcall; + /// . + function L502_BfMemWrite(hnd: t_l502_hnd; addr : LongWord; + const regs: array of LongWord; size: LongWord): LongInt; stdcall; + /// . + function L502_BfExecCmd(hnd: t_l502_hnd; cmd_code : Word; par : LongWord; + const snd_data : array of LongWord; snd_size : LongWord; + out rcv_data : array of LongWord; rcv_size : LongWord; + tout: LongWord; out recvd_size: LongWord): LongInt; stdcall; + + {------------------- Flash- -------------} + /// Flash- . + function L502_FlashRead(hnd: t_l502_hnd; addr: LongWord; + out data: array of Byte; size: LongWord): LongInt; stdcall; + /// Flash- . + function L502_FlashWrite(hnd: t_l502_hnd; addr: LongWord; + const data: array of Byte; size: LongWord): LongInt; stdcall; + /// Flash-. + function L502_FlashErase(hnd: t_l502_hnd; addr: LongWord; size: LongWord): LongInt; stdcall; + /// Flash-. + function L502_FlashWriteEnable(hnd: LongWord): LongInt; stdcall; + /// Flash-. + function L502_FlashWriteDisable(hnd: t_l502_hnd): LongInt; stdcall; + + {----------------- ------------------} + // . + function L502_GetDllVersion() : LongWord; stdcall; + // + function L502_GetDriverVersion(hnd: t_l502_hnd; out ver: LongWord): LongInt; stdcall; + // + function L502_GetErrorString(err: LongInt) : string; stdcall; + // . + function L502_LedBlink(hnd: t_l502_hnd): LongInt; stdcall; + // . + function L502_SetDigInPullup(hnd: t_l502_hnd; pullups : LongWord): LongInt; stdcall; + + + + + + + + + +implementation + function L502_Create() : t_l502_hnd; stdcall; external 'l502api.dll'; + function L502_Free(hnd: t_l502_hnd) : LongInt; stdcall; external 'l502api.dll'; + function _get_serials( ser_arr: p_x502_serial_array; size:LongWord; + flags:LongWord; out devcnt: LongWord) : LongInt; + stdcall; external 'l502api.dll' name 'L502_GetSerialList'; + function _get_dev_records_list(out list; size:LongWord; + flags : LongWord; out devcnt: LongWord) : LongInt; + stdcall; external 'l502api.dll' name 'L502_GetDevRecordsList'; + + function _open(hnd: t_l502_hnd; serial: PAnsiChar) : LongInt; stdcall; external 'l502api.dll' name 'L502_Open'; + function L502_Close(hnd : t_l502_hnd) : LongInt; stdcall; external 'l502api.dll'; + function L502_GetDevInfo(hnd : t_l502_hnd; out info : t_l502_info) : LongInt; stdcall; external 'l502api.dll'; + + function L502_Configure(hnd: t_l502_hnd; flags: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetLChannel(hnd: t_l502_hnd; lch, phy_ch, mode, range, avg: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetLChannelCount(hnd: t_l502_hnd; lch_cnt : LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_GetLChannelCount(hnd: t_l502_hnd; out lch_cnt: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetAdcFreqDivider(hnd: t_l502_hnd; adc_freq_div : LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetAdcInterframeDelay(hnd: t_l502_hnd; delay : LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetDinFreqDivider(hnd: t_l502_hnd; din_freq_div: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetAdcFreq(hnd: t_l502_hnd; var f_acq, f_frame: Double): LongInt; stdcall; external 'l502api.dll'; + function L502_SetDinFreq(hnd: t_l502_hnd; var f_din: Double): LongInt; stdcall; external 'l502api.dll'; + function L502_GetAdcFreq(hnd: t_l502_hnd; out f_acq, f_frame: Double): LongInt; stdcall; external 'l502api.dll'; + function L502_SetRefFreq(hnd: t_l502_hnd; freq: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetSyncMode(hnd: t_l502_hnd; sync_mode: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetSyncStartMode(hnd: t_l502_hnd; sync_start_mode: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetMode(hnd: t_l502_hnd; mode: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_GetMode(hnd: t_l502_hnd; out mode: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetAdcCoef(hnd: t_l502_hnd; range: LongWord; k, offs: Double): LongInt; stdcall; external 'l502api.dll'; + function L502_GetAdcCoef(hnd: t_l502_hnd; range: LongWord; out k, offs: Double): LongInt; stdcall; external 'l502api.dll'; + + function L502_AsyncOutDac(hnd: t_l502_hnd; ch: LongWord; data: Double; flags: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_AsyncOutDig(hnd: t_l502_hnd; val, msk: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_AsyncInDig(hnd: t_l502_hnd; out din: LongWord): LongInt; stdcall; external 'l502api.dll'; + function _get_adc_frame(hnd: t_l502_hnd; flags: LongWord; tout: LongWord; out data): LongInt; stdcall; external 'l502api.dll' name 'L502_AsyncGetAdcFrame'; + + function L502_StreamsEnable(hnd: t_l502_hnd; streams: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_StreamsDisable(hnd: t_l502_hnd; streams: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_StreamsStart(hnd: t_l502_hnd): LongInt; stdcall; external 'l502api.dll'; + function L502_IsRunning(hnd: t_l502_hnd): LongInt; stdcall; external 'l502api.dll'; + function L502_StreamsStop(hnd: t_l502_hnd): LongInt; stdcall; external 'l502api.dll'; + function _recv(hnd: t_l502_hnd; out buf; size: LongWord; tout : LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_Recv'; + function _send(hnd: t_l502_hnd; const buf; size: LongWord; tout : LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_Send'; + function _process_adc_data(hnd: t_l502_hnd; const src; out dest; var size : LongWord; + flags : LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_ProcessAdcData'; + function _process_data(hnd: t_l502_hnd; const src; size: LongWord; + flags : LongWord; out adc_data; var adc_data_size : LongWord; + out din_data; var din_data_size: LongWord):LongInt; stdcall; external 'l502api.dll' name 'L502_ProcessData'; + + function _process_data_usr(hnd: t_l502_hnd; const src; size: LongWord; + flags : LongWord; out adc_data; var adc_data_size : LongWord; + out din_data; var din_data_size: LongWord; + out usr_data; var usr_data_size: LongWord):LongInt; stdcall; external 'l502api.dll' name 'L502_ProcessDataWithUserExt'; + function _prepare_data(hnd: t_l502_hnd; const dac1, dac2; const digout; size, flags : LongWord; + out out_buf):LongInt; stdcall; external 'l502api.dll' name 'L502_PrepareData'; + function L502_GetRecvReadyCount(hnd: t_l502_hnd; out rdy_cnt: LongWord):LongInt; stdcall; external 'l502api.dll'; + function L502_GetSendReadyCount(hnd: t_l502_hnd; out rdy_cnt: LongWord):LongInt; stdcall; external 'l502api.dll'; + function L502_GetNextExpectedLchNum(hnd: t_l502_hnd; out lch: LongWord):LongInt; stdcall; external 'l502api.dll'; + function L502_PreloadStart(hnd: t_l502_hnd): LongInt; stdcall; external 'l502api.dll'; + function L502_OutCycleLoadStart(hnd: t_l502_hnd; size: LongWord):LongInt; stdcall; external 'l502api.dll'; + function L502_OutCycleSetup(hnd: t_l502_hnd; flags: LongWord):LongInt; stdcall; external 'l502api.dll'; + function L502_OutCycleStop(hnd: t_l502_hnd; flags: LongWord):LongInt; stdcall; external 'l502api.dll'; + function L502_SetDmaBufSize(hnd: t_l502_hnd; dma_ch, size: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_SetDmaIrqStep(hnd: t_l502_hnd; dma_ch, step: LongWord): LongInt; stdcall; external 'l502api.dll'; + + function L502_BfCheckFirmwareIsLoaded(hnd: t_l502_hnd; out version: LongWord): LongInt; stdcall; external 'l502api.dll'; + function _bf_load_firm(hnd: t_l502_hnd; filename: PAnsiChar): LongInt; stdcall; external 'l502api.dll' name 'L502_BfLoadFirmware'; + function _bf_mem_read(hnd: t_l502_hnd; addr : LongWord; out regs; size: LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_BfMemRead'; + function _bf_mem_write(hnd: t_l502_hnd; addr : LongWord; const regs; size: LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_BfMemWrite'; + function _bf_exec_cmd(hnd: t_l502_hnd; cmd_code : Word; par : LongWord; + const snd_data; snd_size : LongWord; out rcv_data; rcv_size : LongWord; + tout: LongWord; out recved_size: LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_BfExecCmd'; + + function _flash_read(hnd: t_l502_hnd; addr: LongWord; out data; size: LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_FlashRead'; + function _flash_write(hnd: t_l502_hnd; addr: LongWord; const data; size: LongWord): LongInt; stdcall; external 'l502api.dll' name 'L502_FlashWrite'; + function L502_FlashErase(hnd: t_l502_hnd; addr: LongWord; size: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_FlashWriteEnable(hnd: LongWord): LongInt; stdcall; external 'l502api.dll'; + function L502_FlashWriteDisable(hnd: t_l502_hnd): LongInt; stdcall; external 'l502api.dll'; + + function L502_GetDllVersion() : LongWord; stdcall; external 'l502api.dll'; + function L502_GetDriverVersion(hnd: t_l502_hnd; out ver: LongWord): LongInt; stdcall; external 'l502api.dll'; + function _get_err_str(err : LongInt) : PAnsiChar; stdcall; external 'l502api.dll' name 'L502_GetErrorString'; + function L502_LedBlink(hnd: t_l502_hnd): LongInt; stdcall; external 'l502api.dll'; + function L502_SetDigInPullup(hnd: t_l502_hnd; pullups : LongWord): LongInt; stdcall; external 'l502api.dll'; + + + { + function L502_GetSerialList(out serials: t_l502_serial_list; flags: LongWord) : LongInt; overload; + var + ser_arr : p_l502_serial_array; + devcnt: LongWord; + res, i : LongInt; + begin + // + res := _get_serials(nil, 0, flags, devcnt); + if (res >= 0) and (devcnt>0) then + begin + // devcnt + ser_arr:=GetMemory(devcnt*L502_SERIAL_SIZE); + // + res:= _get_serials(ser_arr, devcnt, flags, PLongWord(nil)^); + if (res > 0) then + begin + // + SetLength(serials, res); + // + for i:=0 to res-1 do + serials[i] := string(ser_arr[i]); + end; + // , + FreeMemory(ser_arr); + end; + L502_GetSerialList:= res; + end; + } + + function L502_GetSerialList(out serials: array of string; flags: LongWord; out devcnt: LongWord) : LongInt; overload; + var + ser_arr : p_x502_serial_array; + res, i : LongInt; + begin + if (Length(serials) > 0) then + begin + ser_arr:=GetMemory(Length(serials)*X502_SERIAL_SIZE); + // + res := _get_serials(ser_arr, Length(serials), flags, devcnt); + if res >= 0 then + begin + // + for i:=0 to res-1 do + serials[i] := string(ser_arr[i]); + end; + // , + FreeMemory(ser_arr); + end + else + begin + res:= _get_serials(nil, 0, flags, devcnt); + end; + L502_GetSerialList:=res; + end; + + function L502_GetSerialList(out serials: array of string; flags: LongWord) : LongInt; overload; + begin + L502_GetSerialList:= L502_GetSerialList(serials, flags, PCardinal(nil)^); + end; + + function L502_GetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord; out devcnt: LongWord) : LongInt; overload; + begin + if (Length(list) > 0) then + begin + L502_GetDevRecordsList := _get_dev_records_list(list, Length(list), flags, devcnt); + end + else + begin + L502_GetDevRecordsList:= _get_dev_records_list(PCardinal(nil)^, 0, flags, devcnt); + end; + end; + function L502_GetDevRecordsList(out list: array of t_x502_devrec; flags : LongWord) : LongInt; overload; + begin + L502_GetDevRecordsList:= L502_GetDevRecordsList(list, flags, PCardinal(nil)^); + end; + + function L502_Open(hnd: t_l502_hnd; serial: string) : LongInt; + begin + L502_Open:=_open(hnd, PAnsiChar(AnsiString(serial))); + end; + + function L502_GetErrorString(err: LongInt) : string; + begin + L502_GetErrorString:= string(_get_err_str(err)); + end; + + function L502_AsyncGetAdcFrame(hnd: t_l502_hnd; flags: LongWord; tout: LongWord; out data: array of Double): LongInt; stdcall; + var err: LongInt; + lch_cnt: LongWord; + begin + err:= L502_GetLChannelCount(hnd, lch_cnt); + if err=L502_ERR_OK then + begin + if LongWord(Length(data)) < lch_cnt then + err:=L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + err:=_get_adc_frame(hnd,flags,tout,data); + end; + L502_AsyncGetAdcFrame:=err; + end; + + function L502_Recv(hnd: t_l502_hnd; out buf : array of LongWord; size: LongWord; tout : LongWord): LongInt; stdcall; + begin + if LongWord(Length(buf)) < size then + L502_Recv:=L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_Recv:=_recv(hnd, buf, size, tout); + end; + + function L502_Send(hnd: t_l502_hnd; const buf : array of LongWord; size: LongWord; tout: LongWord): LongInt; stdcall; + begin + if LongWord(Length(buf)) < size then + L502_Send:=L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_Send:=_send(hnd,buf,size,tout); + end; + + + function L502_ProcessAdcData(hnd: t_l502_hnd; const src: array of LongWord; + out dest: array of Double; var size : LongWord; + flags : LongWord): LongInt; stdcall; + begin + if (LongWord(Length(src)) < size) or (LongWord(Length(dest)) < size) then + L502_ProcessAdcData:=L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_ProcessAdcData:=_process_adc_data(hnd, src, dest, size, flags); + end; + + function L502_ProcessData(hnd: t_l502_hnd; const src: array of LongWord; size: LongWord; + flags : LongWord; out adc_data: array of Double; var adc_data_size : LongWord; + out din_data: array of LongWord; var din_data_size: LongWord):LongInt; stdcall; + begin + if (LongWord(Length(adc_data)) < adc_data_size) or (LongWord(Length(din_data)) < din_data_size) + or (LongWord(Length(src)) < size) then + L502_ProcessData:=L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_ProcessData:=_process_data(hnd, src, size, flags, adc_data, adc_data_size, din_data, din_data_size); + end; + + function L502_ProcessDataWithUserExt(hnd: t_l502_hnd; const src: array of LongWord; size: LongWord; + flags : LongWord; out adc_data: array of Double; var adc_data_size : LongWord; + out din_data: array of LongWord; var din_data_size: LongWord; + out usr_data: array of LongWord; var usr_data_size: LongWord):LongInt; stdcall; + begin + if (LongWord(Length(adc_data)) < adc_data_size) or (LongWord(Length(din_data)) < din_data_size) + or (LongWord(Length(src)) < size) or (LongWord(Length(usr_data)) < usr_data_size) then + L502_ProcessDataWithUserExt:=L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_ProcessDataWithUserExt:=_process_data_usr(hnd, src,size,flags,adc_data, + adc_data_size, din_data, din_data_size, usr_data, usr_data_size); + end; + + function L502_PrepareData(hnd: t_l502_hnd; const dac1, dac2: array of Double; + const digout: array of LongWord; size, flags : LongWord; + out out_buf: array of LongWord):LongInt; stdcall; + begin + L502_PrepareData:=_prepare_data(hnd, dac1, dac2, digout, size, flags, out_buf); + end; + + function L502_BfLoadFirmware(hnd: t_l502_hnd; filename: string): LongInt; stdcall; + begin + L502_BfLoadFirmware:=_bf_load_firm(hnd, PAnsiChar(AnsiString(filename))); + end; + + function L502_BfMemRead(hnd: t_l502_hnd; addr : LongWord; out regs: array of LongWord; + size: LongWord): LongInt; stdcall; + begin + if (LongWord(Length(regs)) < size) then + L502_BfMemRead := L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_BfMemRead:=_bf_mem_read(hnd, addr, regs, size); + end; + + function L502_BfMemWrite(hnd: t_l502_hnd; addr : LongWord; + const regs: array of LongWord; size: LongWord): LongInt; stdcall; + begin + if (LongWord(Length(regs)) < size) then + L502_BfMemWrite := L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_BfMemWrite:=_bf_mem_write(hnd, addr, regs, size); + end; + + function L502_BfExecCmd(hnd: t_l502_hnd; cmd_code : Word; par : LongWord; + const snd_data : array of LongWord; snd_size : LongWord; + out rcv_data : array of LongWord; rcv_size : LongWord; + tout: LongWord; out recvd_size: LongWord): LongInt; stdcall; + begin + if (LongWord(Length(snd_data)) < snd_size) or + (LongWord(Length(rcv_data)) < rcv_size) then + L502_BfExecCmd := L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_BfExecCmd:=_bf_exec_cmd(hnd, cmd_code, par, snd_data, snd_size, + rcv_data, rcv_size, tout, recvd_size); + end; + + function L502_FlashRead(hnd: t_l502_hnd; addr: LongWord; + out data: array of Byte; size: LongWord): LongInt; stdcall; + begin + if (LongWord(Length(data)) < size) then + L502_FlashRead := L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_FlashRead:=_bf_mem_write(hnd, addr, data, size); + end; + /// Flash- . + function L502_FlashWrite(hnd: t_l502_hnd; addr: LongWord; + const data: array of Byte; size: LongWord): LongInt; stdcall; + begin + if (LongWord(Length(data)) < size) then + L502_FlashWrite := L502_ERR_INSUFFICIENT_ARRAY_SIZE + else + L502_FlashWrite:=_bf_mem_write(hnd, addr, data, size); + end; +end. + diff --git a/x502api-1.1.34/devs/l502/win/l502_spec.c b/x502api-1.1.34/devs/l502/win/l502_spec.c new file mode 100644 index 0000000..2341ffd --- /dev/null +++ b/x502api-1.1.34/devs/l502/win/l502_spec.c @@ -0,0 +1,507 @@ +#include +#include +#include +#include +#include + +#include "../l502api_private.h" + +#include + + + +/* GUID интерфейса устройств lpcie для связи приложения с драйвером */ +DEFINE_GUID (GUID_LPCIE_INTERFACE, + 0x53869b9a, 0x7875, 0x4fd3, 0x9e, 0x04, 0xbe, 0xc8, 0x1a, 0x92, 0xf9, 0xa9); + +#define L502_IOCTL_TIMEOUT 500 + + +typedef struct { + int32_t size; + SP_DEVICE_INTERFACE_DETAIL_DATA *intf_detail; +} t_lpcie_devlst_intptr; + + +static int32_t f_ioctl(HANDLE hDevice, + uint32_t dwIoControlCode, // control code of operation to perform + void* lpInBuffer, // pointer to buffer to supply input data + uint32_t nInBufferSize, // size of input buffer in bytes + void* lpOutBuffer, // pointer to buffer to receive output data + uint32_t nOutBufferSize, // size of output buffer in bytes + uint32_t* rx_size, + uint32_t TimeOut) { // таймаут в мс + uint32_t RealBytesTransferred; + uint32_t BytesReturned; + OVERLAPPED Ov; + int32_t err = X502_ERR_OK; + uint32_t syserr = 0; + + + // инициализируем OVERLAPPED структуру + memset(&Ov, 0x0, sizeof(OVERLAPPED)); + // создаём событие для асинхронного запроса + Ov.hEvent = CreateEvent(NULL, FALSE , FALSE, NULL); + if(!Ov.hEvent) { + err = X502_ERR_MEMORY_ALLOC; + } else { + // посылаем требуемый запрос + if(!DeviceIoControl( hDevice, dwIoControlCode, + lpInBuffer, nInBufferSize, + lpOutBuffer, nOutBufferSize, + &BytesReturned, &Ov)) { + syserr = GetLastError(); + if(syserr != ERROR_IO_PENDING) { + CloseHandle(Ov.hEvent); + err = X502_ERR_IOCTL_FAILD; + GetOverlappedResult(hDevice, &Ov, &RealBytesTransferred, TRUE); + } + } + } + + if (err == X502_ERR_OK) { + // ждём окончания выполнения запроса + if (WaitForSingleObject(Ov.hEvent, TimeOut) == WAIT_TIMEOUT) { + CancelIo(hDevice); + CloseHandle(Ov.hEvent); + err = X502_ERR_IOCTL_TIMEOUT; + GetOverlappedResult(hDevice, &Ov, &RealBytesTransferred, TRUE); + } + } + // попробуем получить кол-во реально переданных байт данных + if (err == X502_ERR_OK) { + if(!GetOverlappedResult(hDevice, &Ov, &RealBytesTransferred, TRUE)) { + syserr = GetLastError(); + CancelIo(hDevice); + CloseHandle(Ov.hEvent); + err = X502_ERR_IOCTL_FAILD; + } else if(nOutBufferSize != RealBytesTransferred) { + CancelIo(hDevice); + CloseHandle(Ov.hEvent); + } else { + CloseHandle(Ov.hEvent); + } + } + + if ((err == X502_ERR_OK) && (rx_size != NULL)) { + *rx_size = RealBytesTransferred; + } + + if (err==X502_ERR_IOCTL_FAILD) { + if (syserr == ERROR_NO_SYSTEM_RESOURCES) + err = X502_ERR_INSUFFICIENT_SYSTEM_RESOURCES; + } + return err; +} + + +int32_t l502_port_fpga_reg_write(t_x502_hnd hnd, uint32_t reg, uint32_t val) { + t_lpcie_mem_rw mem_wr = {reg,val}; + return f_ioctl (L502_PCI_IFACE_FILE(hnd), + LPCIE_IOCTL_MEMFPGA_WR, &mem_wr, sizeof(mem_wr), + NULL, 0, NULL, L502_IOCTL_TIMEOUT) ? + X502_ERR_FPGA_REG_READ : 0; +} + + +int32_t l502_port_fpga_reg_read(t_x502_hnd hnd, uint32_t reg, uint32_t *val) { + uint32_t rd_val; + int32_t err = f_ioctl (L502_PCI_IFACE_FILE(hnd), + LPCIE_IOCTL_MEMFPGA_RD, ®, sizeof(reg), + &rd_val, sizeof(rd_val), NULL, L502_IOCTL_TIMEOUT); + if (err != X502_ERR_OK) { + err = X502_ERR_FPGA_REG_READ; + } else if (val != NULL) { + *val = rd_val; + } + return err; +} + +int32_t l502_port_stream_set_params(t_x502_hnd hnd, t_lpcie_stream_ch_params *par) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_SET_PARAMS, par, + sizeof(t_lpcie_stream_ch_params), NULL, 0, NULL, + L502_IOCTL_TIMEOUT); +} + +int32_t l502_port_stream_start(t_x502_hnd hnd, uint32_t ch, uint32_t single) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), single ? LPCIE_IOCTL_STREAM_START_SINGLE : + LPCIE_IOCTL_STREAM_START, + &ch, sizeof(ch), NULL, 0, NULL, + L502_IOCTL_TIMEOUT); +} + +int32_t l502_port_stream_stop(t_x502_hnd hnd, uint32_t ch, uint32_t flags) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_STOP, &ch, sizeof(ch), NULL, 0, NULL, + L502_IOCTL_TIMEOUT); +} + +int32_t l502_port_stream_free(t_x502_hnd hnd, uint32_t ch, uint32_t flags) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_FREE, &ch, sizeof(ch), NULL, 0, NULL, + L502_IOCTL_TIMEOUT); +} + + +int32_t l502_port_stream_rdy_size(t_x502_hnd hnd, uint32_t ch, uint32_t *rdy_size) { + uint32_t rd_val; + int32_t err = f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_STREAM_GET_RDY_SIZE, &ch, sizeof(ch), + &rd_val, sizeof(rd_val), NULL, L502_IOCTL_TIMEOUT); + if ((err == X502_ERR_OK) && (rdy_size != NULL)) + *rdy_size = rd_val; + return err; +} + + + +int32_t l502_port_cycle_load_start(t_x502_hnd hnd, uint32_t ch, uint32_t size, uint32_t min_irq_step) { + t_lpcie_cycle_set_par par; + memset(&par, 0, sizeof(par)); + par.ch = ch; + par.size = size; + par.irq_step = min_irq_step; + return f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_LOAD, &par, sizeof(par), NULL, 0, + NULL, L502_IOCTL_TIMEOUT); +} + +int32_t l502_port_cycle_setup(t_x502_hnd hnd, uint32_t ch, uint32_t evt) { + t_lpcie_cycle_evt_par par; + memset(&par, 0, sizeof(par)); + par.ch = ch; + par.evt = evt; + return f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_SWITCH, &par, sizeof(par), NULL, 0, + NULL, L502_IOCTL_TIMEOUT); +} + +int32_t l502_port_cycle_stop(t_x502_hnd hnd, uint32_t ch, uint32_t evt) { + t_lpcie_cycle_evt_par par; + memset(&par, 0, sizeof(par)); + par.ch = ch; + par.evt = evt; + return f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_STOP, &par, sizeof(par), NULL, 0, + NULL, L502_IOCTL_TIMEOUT); +} + +int32_t l502_port_cycle_check_setup(t_x502_hnd hnd, uint32_t ch, uint32_t *done) { + uint32_t rd_val; + int32_t err = f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_CYCLE_CHECK_SETUP, &ch, sizeof(ch), + &rd_val, sizeof(rd_val), NULL, L502_IOCTL_TIMEOUT); + if ((err == X502_ERR_OK) && (done != NULL)) + *done = rd_val; + return err; +} + +int32_t l502_port_renew_info(t_x502_hnd hnd) { + return f_ioctl(L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_RELOAD_DEVINFO, NULL, 0, NULL, 0, NULL, + L502_IOCTL_TIMEOUT); +} + +int32_t l502_port_get_drv_ver(t_x502_hnd hnd, uint32_t *ver) { + uint32_t rd_val; + int32_t err = f_ioctl (L502_PCI_IFACE_FILE(hnd), LPCIE_IOCTL_GET_DRV_VERSION, NULL, 0, + &rd_val, sizeof(rd_val), NULL, L502_IOCTL_TIMEOUT); + if ((err == X502_ERR_OK) && (ver != NULL)) + *ver = rd_val; + return err; +} + + +int32_t l502_port_stream_read(t_x502_hnd hnd, uint32_t* buff, uint32_t size, uint32_t timeout) { + int send_size = 4*size; + uint32_t NumberOfBytesRead = 0; + int32_t err = X502_ERR_OK; + OVERLAPPED Ov; + + // инициализируем OVERLAPPED структуру + memset(&Ov, 0, sizeof(OVERLAPPED)); + Ov.hEvent = CreateEvent(NULL, FALSE , FALSE, NULL); + if(!Ov.hEvent) { + err = X502_ERR_MEMORY_ALLOC; + } + + if (err == X502_ERR_OK) { + // посылаем асинхронный запрос на сбор необходимого кол-ва данных + if(!ReadFile(L502_PCI_IFACE_FILE(hnd), buff, send_size, NULL, &Ov)) { + if(GetLastError() != ERROR_IO_PENDING) { + CancelIo(L502_PCI_IFACE_FILE(hnd)); + CloseHandle(Ov.hEvent); + err = X502_ERR_RECV; + GetOverlappedResult(L502_PCI_IFACE_FILE(hnd), &Ov, &NumberOfBytesRead, TRUE); + } + } + } + // ждём окончания выполнения запроса + if (err == X502_ERR_OK) { + if (WaitForSingleObject(Ov.hEvent, timeout) == WAIT_TIMEOUT) { + CancelIo(L502_PCI_IFACE_FILE(hnd)); + CloseHandle(Ov.hEvent); + GetOverlappedResult(L502_PCI_IFACE_FILE(hnd), &Ov, &NumberOfBytesRead, TRUE); + } else if(!GetOverlappedResult(L502_PCI_IFACE_FILE(hnd), &Ov, &NumberOfBytesRead, TRUE)) { + CancelIo(L502_PCI_IFACE_FILE(hnd)); + CloseHandle(Ov.hEvent); + err = X502_ERR_RECV; + } else { + CloseHandle(Ov.hEvent); + } + } + return err != X502_ERR_OK ? err : NumberOfBytesRead/4; +} + + + +int32_t l502_port_stream_write(t_x502_hnd hnd, const uint32_t* buff, + uint32_t size, uint32_t timeout) { + int send_size = 4*size; + uint32_t NumberOfBytesRead = 0; + int32_t err = X502_ERR_OK; + OVERLAPPED Ov; + + // инициализируем OVERLAPPED структуру + memset(&Ov, 0, sizeof(OVERLAPPED)); + Ov.hEvent = CreateEvent(NULL, FALSE , FALSE, NULL); + if(!Ov.hEvent) { + err = X502_ERR_MEMORY_ALLOC; + } + if (err == X502_ERR_OK) { + // посылаем асинхронный запрос на сбор необходимого кол-ва данных + if(!WriteFile(L502_PCI_IFACE_FILE(hnd), buff, send_size, NULL, &Ov)) { + if(GetLastError() != ERROR_IO_PENDING) { + CancelIo(L502_PCI_IFACE_FILE(hnd)); + CloseHandle(Ov.hEvent); + err = X502_ERR_SEND; + GetOverlappedResult(L502_PCI_IFACE_FILE(hnd), &Ov, &NumberOfBytesRead, TRUE); + } + } + } + // ждём окончания выполнения запроса + if (err == X502_ERR_OK) { + if (WaitForSingleObject(Ov.hEvent, timeout) == WAIT_TIMEOUT) { + CancelIo(L502_PCI_IFACE_FILE(hnd)); + CloseHandle(Ov.hEvent); + GetOverlappedResult(L502_PCI_IFACE_FILE(hnd), &Ov, &NumberOfBytesRead, TRUE); + } else if(!GetOverlappedResult(L502_PCI_IFACE_FILE(hnd), &Ov, &NumberOfBytesRead, TRUE)) { + CancelIo(L502_PCI_IFACE_FILE(hnd)); + CloseHandle(Ov.hEvent); + err = X502_ERR_SEND; + } else { + CloseHandle(Ov.hEvent); + } + } + return err != X502_ERR_OK ? err : NumberOfBytesRead/4; +} + + + +static int32_t f_get_file_par(SP_DEVICE_INTERFACE_DETAIL_DATA *detail, TCHAR* filebuf, + const TCHAR* file, char* res, uint32_t req_size, + uint32_t* read_size) { + HANDLE ifile; + int32_t err = 0; + _tcscpy(filebuf, TEXT(detail->DevicePath)); + _tcscat(filebuf, file); + + ifile = CreateFile(filebuf, GENERIC_READ, + FILE_SHARE_READ, + NULL, OPEN_EXISTING, + 0, NULL); + if (ifile != INVALID_HANDLE_VALUE) { + DWORD read_cnt=0; + if (!ReadFile(ifile, res, req_size, &read_cnt, NULL)) { + err = X502_ERR_GET_INFO; + } else { + if (read_size!=NULL) + *read_size = read_cnt; + } + CloseHandle(ifile); + } else { + err = X502_ERR_GET_INFO; + } + return err; +} + + + + +static int f_fill_devlist(SP_DEVICE_INTERFACE_DETAIL_DATA *detail, + t_x502_devrec *info) { + int32_t err = X502_ERR_OK; + TCHAR *filename = malloc(sizeof(TCHAR)*21 + _tcslen(detail->DevicePath)); + + if (filename == NULL) { + err = X502_ERR_MEMORY_ALLOC; + } else { + err = f_get_file_par(detail, filename, TEXT("\\name"), info->devname, + sizeof(info->devname), NULL); + if (err == X502_ERR_OK) { + /* получаем серийный номер устройства */ + err = f_get_file_par(detail, filename, TEXT("\\sn"), info->serial, + sizeof(info->serial), NULL); + } + + /* получаем информацию, открыто ли устройство */ + if (err == X502_ERR_OK) { + char val = '0'; + f_get_file_par(detail, filename, TEXT("\\opened"), &val, 1, NULL); + if (!err && (val!='0')) + info->flags |= X502_DEVFLAGS_DEVREC_OPENED; + } + + /* получаем информацию, присутствует ли BlackFin */ + if (err == X502_ERR_OK) { + char val = '0'; + if ((f_get_file_par(detail, filename, TEXT("\\bf"), &val, 1, NULL)==0) + && (val != '0')) { + info->flags |= X502_DEVFLAGS_BF_PRESENT; + } + + if ((f_get_file_par(detail, filename, TEXT("\\dac"), &val, 1, NULL)==0) + && (val != '0')) { + info->flags |= X502_DEVFLAGS_DAC_PRESENT; + } + + if ((f_get_file_par(detail, filename, TEXT("\\gal"), &val, 1, NULL)==0) + && (val != '0')) { + info->flags |= X502_DEVFLAGS_GAL_PRESENT; + } + } + + if (err == X502_ERR_OK) { + err = l502_devlist_gen(info, detail); + } + free(filename); + } + return err; +} + +int32_t l502_port_free_iface_data(void *intptr) { + free(intptr); + return X502_ERR_OK; +} + + +int32_t l502_port_open(t_x502_hnd hnd, const t_x502_devrec *devinfo) { + SP_DEVICE_INTERFACE_DETAIL_DATA *detail = (SP_DEVICE_INTERFACE_DETAIL_DATA *)devinfo->internal->iface_data; + int32_t err = X502_ERR_OK; + HANDLE file = CreateFile(detail->DevicePath, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + if (file != INVALID_HANDLE_VALUE) { + hnd->iface_data = malloc(sizeof(t_pci_iface_data)); + if (hnd->iface_data == NULL) { + err = X502_ERR_MEMORY_ALLOC; + CloseHandle(file); + } else { + L502_PCI_IFACE_FILE(hnd) = file; + } + } else { + DWORD syserr = GetLastError(); + if (syserr == ERROR_ACCESS_DENIED) { + err = X502_ERR_DEVICE_ACCESS_DENIED; + } else if (syserr == ERROR_FILE_NOT_FOUND) { + err = X502_ERR_DEVICE_NOT_FOUND; + } else { + err = X502_ERR_DEVICE_OPEN; + } + } + return err; +} + +int32_t l502_port_close(t_x502_hnd hnd) { + if (hnd->iface_data !=NULL) { + CloseHandle(L502_PCI_IFACE_FILE(hnd)); + free(hnd->iface_data); + hnd->iface_data = NULL; + } + return X502_ERR_OK; +} + + + +X502_EXPORT(int32_t) L502_GetDevRecordsList(t_x502_devrec *list, uint32_t size, + uint32_t flags, uint32_t *devcnt) { + HDEVINFO infoSet; + SP_DEVINFO_DATA infoData; + DWORD index=0; + uint32_t curcnt=0; + int32_t err = X502_ERR_OK; + + + infoData.cbSize = sizeof(SP_DEVINFO_DATA); + + /* получаем список устройств с подержкой интерфейса lpcie */ + infoSet = SetupDiGetClassDevs(&GUID_LPCIE_INTERFACE,NULL, + NULL, + DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + + /* проходимся по всем устройствам из списка */ + while (SetupDiEnumDeviceInfo(infoSet, index, &infoData)) { + SP_DEVICE_INTERFACE_DATA intfData; + intfData.cbSize = sizeof(intfData); + /* получаем информацию о интерфейсе */ + if (SetupDiEnumDeviceInterfaces(infoSet, &infoData, &GUID_LPCIE_INTERFACE, + 0, &intfData)) { + DWORD req_size; + /* узнаем резмер детальной информации о интерфейсе (нужна для + получения имени устройства) */ + if (!SetupDiGetDeviceInterfaceDetail(infoSet, &intfData, NULL, + 0, &req_size, NULL) + && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { + SP_DEVICE_INTERFACE_DETAIL_DATA* detail = + (SP_DEVICE_INTERFACE_DETAIL_DATA*) malloc(req_size); + + if (detail != NULL) { + int detail_used = 0; + + /* пытаемся получить всю информацию */ + detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + if (SetupDiGetDeviceInterfaceDetail(infoSet, &intfData, + detail, req_size, + NULL, NULL)) { + t_x502_devrec info; + int info_used = 0; + detail_used = 1; + + X502_DevRecordInit(&info); + + /* получаем информацию о устройстве из служебных файлов, + * предоставляемых драйвером */ + if (f_fill_devlist(detail, &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); + } + + if (!detail_used) { + free(detail); + } + } else { + err = X502_ERR_MEMORY_ALLOC; + } + } + } + index++; + } + + + if (infoSet != NULL) { + SetupDiDestroyDeviceInfoList(infoSet); + } + + if (devcnt != NULL) + *devcnt = curcnt; + + return err != X502_ERR_OK ? err : curcnt > size ? size : curcnt ; +} diff --git a/x502api-1.1.34/doc/Doxyfile.in b/x502api-1.1.34/doc/Doxyfile.in new file mode 100644 index 0000000..2c3651c --- /dev/null +++ b/x502api-1.1.34/doc/Doxyfile.in @@ -0,0 +1,2489 @@ +# Doxyfile 1.8.16 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "x502api" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Библиотека для работы с модулями L502/E502" + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@/doc + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = Russian + +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = NO + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) + +ALIASES = "lpcie_sdk= \"L-Card L502/E502 SDK\" " \ + "lpcie_sdk_dir=SDK_DIR" \ + "filename{1}=\1" \ + "langname{1}=*\1*"\ + "lcard_modules=L-502, E-502 и E16" + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is +# Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = YES + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# (including Cygwin) ands Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = @DOXYGEN_INPUT_FILES_STRING@ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = @CMAKE_CURRENT_SOURCE_DIR@/doc/images + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = NO + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via Javascript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have Javascript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = x502api + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = YES + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = ru.lcard.x502api + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /