diff options
author | Pete Batard <pete@akeo.ie> | 2011-09-09 12:48:58 +0100 |
---|---|---|
committer | Pete Batard <pete@akeo.ie> | 2011-09-09 12:48:58 +0100 |
commit | 6c72fbff2da8856df10fa67b9eead9114d271dda (patch) | |
tree | a8be08608452e1c4fc2e37c089ba97483a83e1ef | |
parent | 845f55c90b0e28b822424956d5df0976cf0f7075 (diff) | |
download | libusb-6c72fbff2da8856df10fa67b9eead9114d271dda.tar.gz |
merge -> pbr342
30 files changed, 1240 insertions, 200 deletions
@@ -16,7 +16,7 @@ configure aclocal.m4 compile config.guess -config.h* +config.h config.log config.status config.sub diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..493e3e1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 2.6) + +# Can be removed once CMake >= 2.8.4 is required +# this has to be set before the project directive +set(CMAKE_LEGACY_CYGWIN_WIN32 0) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") + +project(libusb) + +option(WITHOUT_EXPERIMENTAL_WARNING "Disable the warning that CMake build is experimental whenever CMake is run" OFF) +if (NOT WITHOUT_EXPERIMENTAL_WARNING) + message(WARNING "The CMake build system is not officially endorsed. Support may or may not be provided on the libusb-devel@sourceforge.net mailing list") +endif() + +option(WITH_SHARED "Build a shared library" ON) +option(WITH_STATIC "Build a static library" OFF) +option(WITH_DOCS "Build the documentation" OFF) + +option(WITH_DEBUG_LOG "enable debug logging" OFF) + +# if debug logging is enabled, by default enable logging +option(WITH_LOGGING "if false, disable all logging" ON) + +# enable examples by default if building with maintainer mode +option(WITH_EXAMPLES "build example applications" ${WITH_MAINTAINER_MODE}) + +option(WITHOUT_PTHREADS "force pthreads to not be used. if on, then they are used based on detection logic" OFF) + +set(LIBUSB_MAJOR 1) +set(LIBUSB_MINOR 0) +set(LIBUSB_MICRO 8) + +macro(append_compiler_flags) + foreach(FLAG IN ITEMS ${ARGN}) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAG}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}") + endforeach() +endmacro() + +if (WITH_DOCS) + add_subdirectory(docs) +endif () + +if (WITH_EXAMPLES) + add_subdirectory(examples) +endif() + +add_subdirectory(libusb) diff --git a/cmake/modules/FindCoreFoundation.cmake b/cmake/modules/FindCoreFoundation.cmake new file mode 100644 index 0000000..d998e89 --- /dev/null +++ b/cmake/modules/FindCoreFoundation.cmake @@ -0,0 +1,17 @@ +# CoreFoundation_INCLUDE_DIR +# CoreFoundation_LIBRARIES +# CoreFoundation_FOUND +include(LibFindMacros) + +find_path(CoreFoundation_INCLUDE_DIR + CoreFoundation.h + PATH_SUFFIXES CoreFoundation +) + +find_library(CoreFoundation_LIBRARY + NAMES CoreFoundation +) + +set(CoreFoundation_PROCESS_INCLUDES CoreFoundation_INCLUDE_DIR) +set(CoreFoundation_PROCESS_LIBS CoreFoundation_LIBRARY) +libfind_process(CoreFoundation) diff --git a/cmake/modules/FindIOKit.cmake b/cmake/modules/FindIOKit.cmake new file mode 100644 index 0000000..584e225 --- /dev/null +++ b/cmake/modules/FindIOKit.cmake @@ -0,0 +1,20 @@ +# IOKit_INCLUDE_DIR +# IOKit_LIBRARIES +# IOKit_FOUND +include(LibFindMacros) + +# IOKit depends on CoreFoundation +find_package(CoreFoundation REQUIRED) + +find_path(IOKit_INCLUDE_DIR + IOKitLib.h + PATH_SUFFIXES IOKit +) + +find_library(IOKit_LIBRARY + NAMES IOKit +) + +set(IOKit_PROCESS_INCLUDES IOKit_INCLUDE_DIR CoreFoundation_INCLUDE_DIR) +set(IOKit_PROCESS_LIBS IOKit_LIBRARY CoreFoundation_LIBRARIES) +libfind_process(IOKit) diff --git a/cmake/modules/LibFindMacros.cmake b/cmake/modules/LibFindMacros.cmake new file mode 100644 index 0000000..69975c5 --- /dev/null +++ b/cmake/modules/LibFindMacros.cmake @@ -0,0 +1,99 @@ +# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments +# used for the current package. For this to work, the first parameter must be the +# prefix of the current package, then the prefix of the new package etc, which are +# passed to find_package. +macro (libfind_package PREFIX) + set (LIBFIND_PACKAGE_ARGS ${ARGN}) + if (${PREFIX}_FIND_QUIETLY) + set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) + endif (${PREFIX}_FIND_QUIETLY) + if (${PREFIX}_FIND_REQUIRED) + set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) + endif (${PREFIX}_FIND_REQUIRED) + find_package(${LIBFIND_PACKAGE_ARGS}) +endmacro (libfind_package) + +# CMake developers made the UsePkgConfig system deprecated in the same release (2.6) +# where they added pkg_check_modules. Consequently I need to support both in my scripts +# to avoid those deprecated warnings. Here's a helper that does just that. +# Works identically to pkg_check_modules, except that no checks are needed prior to use. +macro (libfind_pkg_check_modules PREFIX PKGNAME) + if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + include(UsePkgConfig) + pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) + else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(${PREFIX} ${PKGNAME}) + endif (PKG_CONFIG_FOUND) + endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) +endmacro (libfind_pkg_check_modules) + +# Do the final processing once the paths have been detected. +# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain +# all the variables, each of which contain one include directory. +# Ditto for ${PREFIX}_PROCESS_LIBS and library files. +# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. +# Also handles errors in case library detection was required, etc. +macro (libfind_process PREFIX) + # Skip processing if already processed during this run + if (NOT ${PREFIX}_FOUND) + # Start with the assumption that the library was found + set (${PREFIX}_FOUND TRUE) + + # Process all includes and set _FOUND to false if any are missing + foreach (i ${${PREFIX}_PROCESS_INCLUDES}) + if (${i}) + set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) + mark_as_advanced(${i}) + else (${i}) + set (${PREFIX}_FOUND FALSE) + endif (${i}) + endforeach (i) + + # Process all libraries and set _FOUND to false if any are missing + foreach (i ${${PREFIX}_PROCESS_LIBS}) + if (${i}) + set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) + mark_as_advanced(${i}) + else (${i}) + set (${PREFIX}_FOUND FALSE) + endif (${i}) + endforeach (i) + + # Print message and/or exit on fatal error + if (${PREFIX}_FOUND) + if (NOT ${PREFIX}_FIND_QUIETLY) + message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") + endif (NOT ${PREFIX}_FIND_QUIETLY) + else (${PREFIX}_FOUND) + if (${PREFIX}_FIND_REQUIRED) + foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) + message("${i}=${${i}}") + endforeach (i) + message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") + endif (${PREFIX}_FIND_REQUIRED) + endif (${PREFIX}_FOUND) + endif (NOT ${PREFIX}_FOUND) +endmacro (libfind_process) + +macro(libfind_library PREFIX basename) + set(TMP "") + if(MSVC80) + set(TMP -vc80) + endif(MSVC80) + if(MSVC90) + set(TMP -vc90) + endif(MSVC90) + set(${PREFIX}_LIBNAMES ${basename}${TMP}) + if(${ARGC} GREATER 2) + set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) + string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) + set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) + endif(${ARGC} GREATER 2) + find_library(${PREFIX}_LIBRARY + NAMES ${${PREFIX}_LIBNAMES} + PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} + ) +endmacro(libfind_library) + diff --git a/configure.ac b/configure.ac index 1aa606b..1812089 100644 --- a/configure.ac +++ b/configure.ac @@ -60,6 +60,7 @@ case $host in AC_MSG_RESULT([Darwin/Mac OS X]) backend="darwin" threads="posix" + LIBS="${LIBS} -lobjc" PC_LIBS_PRIVATE="-Wl,-framework,IOKit -Wl,-framework,CoreFoundation" LTLDFLAGS="${LTLDFLAGS} -Wl,-prebind" AC_CHECK_HEADERS([poll.h]) @@ -165,12 +166,6 @@ AC_ARG_ENABLE([examples-build], [AS_HELP_STRING([--enable-examples-build], [build_examples='no']) AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$build_examples" != "xno"]) -# Restore gnu89 inline semantics on gcc 4.3 and newer -saved_cflags="$CFLAGS" -CFLAGS="$CFLAGS -fgnu89-inline" -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], inline_cflags="-fgnu89-inline", inline_cflags="") -CFLAGS="$saved_cflags" - # check for -fvisibility=hidden compiler support (GCC >= 3.4) saved_cflags="$CFLAGS" # -Werror required for cygwin @@ -197,7 +192,7 @@ AM_CONDITIONAL([HAVE_SIGACTION], [test "x$have_sigaction" = "xyes"]) # headers not available on all platforms but required on others AC_CHECK_HEADERS([sys/time.h]) -AM_CFLAGS="-std=gnu99 $inline_cflags -Wall -Wundef -Wunused -Wstrict-prototypes -Werror-implicit-function-declaration $nopointersign_cflags -Wshadow" +AM_CFLAGS="-std=gnu99 -Wall -Wundef -Wunused -Wstrict-prototypes -Werror-implicit-function-declaration $nopointersign_cflags -Wshadow" AC_SUBST(VISIBILITY_CFLAGS) AC_SUBST(AM_CFLAGS) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..d73d729 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,27 @@ +include(CheckFunctionExists) +include(FindThreads) + +check_function_exists(sigaction HAVE_SIGACTION) + +if (WITH_SHARED) + set(LIBUSB_LIBRARY usb-1.0) +else() + set(LIBUSB_LIBRARY usb-1.0-static) +endif() + +include_directories(../libusb) + +add_executable(lsusb lsusb.c) +target_link_libraries(lsusb ${LIBUSB_LIBRARY}) +add_executable(xusb xusb.c) +target_link_libraries(xusb ${LIBUSB_LIBRARY}) + +if (HAVE_SIGACTION) + add_executable(dpfp dpfp.c) + target_link_libraries(dpfp ${LIBUSB_LIBRARY}) + + if (CMAKE_USE_PTHREADS_INIT) + add_executable(dpfp_threaded dpfp_threaded.c) + target_link_libraries(dpfp_threaded ${LIBUSB_LIBRARY}) + endif() +endif() diff --git a/examples/dpfp.c b/examples/dpfp.c index 07ffe4d..abbe9dc 100644 --- a/examples/dpfp.c +++ b/examples/dpfp.c @@ -225,7 +225,6 @@ static int save_to_file(unsigned char *data) { FILE *fd; char filename[64]; - size_t ignore; sprintf(filename, "finger%d.pgm", img_idx++); fd = fopen(filename, "w"); @@ -233,7 +232,7 @@ static int save_to_file(unsigned char *data) return -1; fputs("P5 384 289 255 ", fd); - ignore = fwrite(data + 64, 1, 384*289, fd); + (void) fwrite(data + 64, 1, 384*289, fd); fclose(fd); printf("saved image to %s\n", filename); return 0; diff --git a/examples/dpfp_threaded.c b/examples/dpfp_threaded.c index 7868015..d13d50e 100644 --- a/examples/dpfp_threaded.c +++ b/examples/dpfp_threaded.c @@ -91,7 +91,7 @@ static void *poll_thread_main(void *arg) } printf("poll thread shutting down\n"); - pthread_exit(NULL); + return NULL; } static int find_dpfp_device(void) @@ -254,7 +254,6 @@ static int save_to_file(unsigned char *data) { FILE *fd; char filename[64]; - size_t ignore; sprintf(filename, "finger%d.pgm", img_idx++); fd = fopen(filename, "w"); @@ -262,7 +261,7 @@ static int save_to_file(unsigned char *data) return -1; fputs("P5 384 289 255 ", fd); - ignore = fwrite(data + 64, 1, 384*289, fd); + (void) fwrite(data + 64, 1, 384*289, fd); fclose(fd); printf("saved image to %s\n", filename); return 0; diff --git a/examples/xusb.c b/examples/xusb.c index de23cdd..0d8fc49 100644 --- a/examples/xusb.c +++ b/examples/xusb.c @@ -540,6 +540,8 @@ int test_device(uint16_t vid, uint16_t pid) int iface_detached = -1; #endif struct libusb_device_descriptor dev_desc; + char* speed_name[5] = { "Unknown", "1.5 Mbit/s (USB 1.0 LowSpeed)", "12 Mbit/s (USB 1.0 FullSpeed)", + "480 Mbit/s (USB 2.0 HighSpeed)", "5000 Mbit/s (USB 3.0 SuperSpeed)"}; char string[128]; uint8_t string_index[3]; // indexes of the string descriptors uint8_t endpoint_in = 0, endpoint_out = 0; // default IN and OUT endpoints @@ -562,6 +564,9 @@ int test_device(uint16_t vid, uint16_t pid) } printf("\n"); } + r = libusb_get_device_speed(dev); + if ((r<0) || (r>4)) r=0; + printf("speed: %s\n", speed_name[r]); printf("\nReading device descriptor:\n"); CALL_CHECK(libusb_get_device_descriptor(dev, &dev_desc)); diff --git a/libusb/CMakeLists.txt b/libusb/CMakeLists.txt new file mode 100644 index 0000000..a1b18fa --- /dev/null +++ b/libusb/CMakeLists.txt @@ -0,0 +1,100 @@ +add_subdirectory(os) + +include(config.cmake) +include(FindThreads) + +set (LIBUSB_COMMON + core.c + descriptor.c + io.c + sync.c + libusb-1.0.rc + libusb-1.0.def +) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/os) + +if (CMAKE_THREAD_LIBS_INIT) + list(APPEND LIBUSB_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) +endif() + +# The CLEAN_DIRECT_OUTPUT property setting can be removed once CMake >= 2.8.4 +if (WITH_SHARED) + add_library(usb-1.0 + SHARED + ${LIBUSB_COMMON} + ${LIBUSB_PLATFORM} + ) + + if (MSVC) + set_target_properties(usb-1.0 PROPERTIES PREFIX "lib") + set_target_properties(usb-1.0 PROPERTIES IMPORT_PREFIX "lib") + set_target_properties(usb-1.0 PROPERTIES IMPORT_SUFFIX ".dll.lib") + endif() + + set_target_properties(usb-1.0 PROPERTIES + CLEAN_DIRECT_OUTPUT 1 + PUBLIC_HEADER libusb.h + VERSION "${LIBUSB_MAJOR}.${LIBUSB_MINOR}.${LIBUSB_MICRO}" + SOVERSION "${LIBUSB_MAJOR}.${LIBUSB_MINOR}.${LIBUSB_MICRO}" + ) + + if (DEFINED LIBUSB_LIBRARIES) + message("Linking shared library against ${LIBUSB_LIBRARIES}") + target_link_libraries(usb-1.0 + ${LIBUSB_LIBRARIES} + ) + endif() + + list(APPEND LIBUSB_LIBTARGETS usb-1.0) +endif() + +if (WITH_STATIC) + add_library(usb-1.0-static + STATIC + ${LIBUSB_COMMON} + ${LIBUSB_PLATFORM} + ) + + set_target_properties(usb-1.0-static PROPERTIES + PREFIX "lib" + OUTPUT_NAME "usb-1.0" + CLEAN_DIRECT_OUTPUT 1 + PUBLIC_HEADER libusb.h + VERSION "${LIBUSB_MAJOR}.${LIBUSB_MINOR}.${LIBUSB_MICRO}" + SOVERSION "${LIBUSB_MAJOR}.${LIBUSB_MINOR}.${LIBUSB_MICRO}" + ) + + if (DEFINED LIBUSB_LIBRARIES) + target_link_libraries(usb-1.0-static + ${LIBUSB_LIBRARIES} + ) + endif() + + list(APPEND LIBUSB_LIBTARGETS usb-1.0-static) +endif() + +install(TARGETS ${LIBUSB_LIBTARGETS} EXPORT libusb-1 + PUBLIC_HEADER DESTINATION include/libusb-1.0 + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION lib +) +install(EXPORT libusb-1 DESTINATION lib/libusb) + +foreach(LIB IN LISTS LIBUSB_LIBRARIES) + if (LIB MATCHES .framework$) + get_filename_component(LIB "${LIB}" NAME) + set(LIB "-Wl,-framework,${LIB}") + elseif (LIB MATCHES .dylib$) + get_filename_component(LIBDIR "${LIB}" PATH) + get_filename_component(LIB "${LIB}" NAME) + string(REGEX REPLACE "lib(.*).dylib$" "\\1" LIB "${LIB}") + set(LIB "-L${LIBDIR} -l${LIB}") + endif() + set(LIBUSB_LIB_DEPENDS "${LIBUSB_LIB_DEPENDS} ${LIB}") +endforeach() + +configure_file(libusb-1.0.pc.cmake "${CMAKE_CURRENT_BINARY_DIR}/libusb-1.0.pc" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libusb-1.0.pc" DESTINATION lib/pkgconfig) diff --git a/libusb/Makefile.am b/libusb/Makefile.am index 2b6ca7b..a5a46cd 100644 --- a/libusb/Makefile.am +++ b/libusb/Makefile.am @@ -11,7 +11,7 @@ DARWIN_USB_SRC = os/darwin_usb.c WINDOWS_USB_SRC = os/poll_windows.c os/windows_usb.c libusb-1.0.rc EXTRA_DIST = $(LINUX_USBFS_SRC) $(DARWIN_USB_SRC) $(WINDOWS_USB_SRC) \ - os/threads_windows.c + os/threads_posix.c os/threads_windows.c if OS_LINUX OS_SRC = $(LINUX_USBFS_SRC) @@ -25,19 +25,21 @@ endif if OS_WINDOWS OS_SRC = $(WINDOWS_USB_SRC) -if !THREADS_POSIX -OS_SRC += os/threads_windows.c -endif - .rc.lo: $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) -I. -i $< -o $@ 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 = $(VISIBILITY_CFLAGS) $(AM_CFLAGS) $(THREAD_CFLAGS) libusb_1_0_la_LDFLAGS = $(LTLDFLAGS) libusb_1_0_la_SOURCES = libusbi.h core.c descriptor.c io.c sync.c $(OS_SRC) \ os/linux_usbfs.h os/darwin_usb.h os/windows_usb.h \ - os/threads_posix.h os/threads_windows.h \ + $(THREADS_SRC) \ os/poll_posix.h os/poll_windows.h hdrdir = $(includedir)/libusb-1.0 diff --git a/libusb/config.cmake b/libusb/config.cmake new file mode 100644 index 0000000..f064121 --- /dev/null +++ b/libusb/config.cmake @@ -0,0 +1,97 @@ +include(CheckCXXCompilerFlag) +include(CheckIncludeFiles) +include(CheckTypeSize) + +if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + if (NOT OS_WINDOWS) + # mingw appears to print a bunch of warnings about this + check_cxx_compiler_flag("-fvisibility=hidden" HAVE_VISIBILITY) + endif() + check_cxx_compiler_flag("-Wno-pointer-sign" HAVE_WARN_NO_POINTER_SIGN) + + set(_GNU_SOURCE 1 CACHE INTERNAL "" FORCE) + + unset(ADDITIONAL_CC_FLAGS) + + if (HAVE_VISIBILITY) + list(APPEND ADDITIONAL_CC_FLAGS -fvisibility=hidden) + endif() + + if (HAVE_WARN_NO_POINTER_SIGN) + list(APPEND ADDITIONAL_CC_FLAGS -Wno-pointer-sign) + endif() + + append_compiler_flags( + -std=gnu99 + -Wall + -Wundef + -Wunused + -Wstrict-prototypes + -Werror-implicit-function-declaration + -Wshadow + ${ADDITIONAL_CC_FLAGS} + ) +else(MSVC) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + append_compiler_flags(/Wp64) +endif() + +check_include_files(sys/timerfd.h USBI_TIMERFD_AVAILABLE) +check_type_size(struct timespec STRUCT_TIMESPEC) + +if (HAVE_VISIBILITY) + set(DEFAULT_VISIBILITY "__attribute__((visibility(\"default\")))" CACHE INTERNAL "visibility attribute to function decl" FORCE) +else() + set(DEFAULT_VISIBILITY "" CACHE INTERNAL "visibility attribute to function decl" FORCE) +endif() + +if (NOT WITHOUT_POLL_H) + check_include_files(poll.h HAVE_POLL_H) +else() + set(HAVE_POLL_H FALSE CACHE INTERNAL "poll.h explicitely disabled" FORCE) +endif() + +if (HAVE_POLL_H) + list(APPEND CMAKE_EXTRA_INCLUDE_FILES "poll.h") + check_type_size(nfds_t NFDS_T) + unset(CMAKE_EXTRA_INCLUDE_FILES) +else() + set(HAVE_NFDS_T FALSE CACHE INTERNAL "poll.h not found - assuming no nfds_t (windows)" FORCE) + set(NFDS_T "" CACHE INTERNAL "" FORCE) +endif() + +if (HAVE_NFDS_T) + set(POLL_NFDS_TYPE nfds_t CACHE INTERNAL "the poll nfds types for this platform" FORCE) +else() + set(POLL_NFDS_TYPE "unsigned int" CACHE INTERNAL "the poll nfds for this platform" FORCE) +endif() + +if (OS_WINDOWS) + macro(copy_header_if_missing HEADER VARIABLE ALTERNATIVE_DIR) + check_include_files(${HEADER} ${VARIABLE}) + if (NOT ${VARIABLE}) + message(STATUS "Missing ${HEADER} - grabbing from ${ALTERNATIVE_DIR}") + file(COPY "${ALTERNATIVE_DIR}/${HEADER}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/") + endif() + endmacro() + + # Only VS 2010 has stdint.h + copy_header_if_missing(stdint.h HAVE_STDINT_H ../msvc) + copy_header_if_missing(inttypes.h HAVE_INTTYPES_H ../msvc) +endif() + +set(ENABLE_DEBUG_LOGGING ${WITH_DEBUG_LOG} CACHE INTERNAL "enable debug logging (WITH_DEBUG_LOGGING)" FORCE) +set(ENABLE_LOGGING ${WITH_LOGGING} CACHE INTERNAL "enable logging (WITH_LOGGING)" FORCE) +set(PACKAGE "libusb" CACHE INTERNAL "The package name" FORCE) +set(PACKAGE_BUGREPORT "libusb-devel@lists.sourceforge.net" CACHE INTERNAL "Where to send bug reports" FORCE) +set(PACKAGE_VERSION "${LIBUSB_MAJOR}.${LIBUSB_MINOR}.${LIBUSB_MICRO}" CACHE INTERNAL "package version" FORCE) +set(PACKAGE_STRING "${PACKAGE} ${PACKAGE_VERSION}" CACHE INTERNAL "package string" FORCE) +set(PACKAGE_URL "http://www.libusb.org" CACHE INTERNAL "package url" FORCE) +set(PACKAGE_TARNAME "libusb" CACHE INTERNAL "tarball name" FORCE) +set(VERSION "${PACKAGE_VERSION}" CACHE INTERNAL "version" FORCE) + +configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) +message(STATUS "Generated configuration file in ${CMAKE_CURRENT_BINARY_DIR}/config.h") + +# for generated config.h +include_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/libusb/config.h.cmake b/libusb/config.h.cmake new file mode 100644 index 0000000..cc6b3c3 --- /dev/null +++ b/libusb/config.h.cmake @@ -0,0 +1,37 @@ +#ifndef LIBUSB_CONFIG_H +#define LIBUSB_CONFIG_H + +#define DEFAULT_VISIBILITY @DEFAULT_VISIBILITY@ + +#cmakedefine ENABLE_DEBUG_LOGGING + +#cmakedefine ENABLE_LOGGING + +#define LIBUSB_MAJOR @LIBUSB_MAJOR@ + +#define LIBUSB_MINOR @LIBUSB_MINOR@ + +#define LIBUSB_MICRO @LIBUSB_MINOR@ + +#cmakedefine _GNU_SOURCE 1 + +#define PACKAGE @PACKAGE@ +#define PACKAGE_BUGREPORT @PACKAGE_BUGREPORT@ +#define PACKAGE_STRING @PACKAGE_STRING@ +#define PACKAGE_URL @PACKAGE_URL@ +#define PACKAGE_VERSION @PACKAGE_VERSION@ +#define PACKAGE_TARNAME @PACKAGE_TARNAME@ + +#define VERSION @VERSION@ + +#cmakedefine OS_LINUX +#cmakedefine OS_DARWIN +#cmakedefine OS_WINDOWS +#cmakedefine THREADS_POSIX +#cmakedefine USBI_TIMERFD_AVAILABLE +#cmakedefine HAVE_STRUCT_TIMESPEC +#cmakedefine HAVE_POLL_H +#cmakedefine HAVE_SYS_TIME_H +#define POLL_NFDS_TYPE @POLL_NFDS_TYPE@ + +#endif /* LIBUSB_CONFIG_H */ diff --git a/libusb/core.c b/libusb/core.c index 4c299c8..d2f67f8 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -533,6 +533,7 @@ struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, dev->ctx = ctx; dev->refcnt = 1; dev->session_data = session_id; + dev->speed = LIBUSB_SPEED_UNKNOWN; memset(&dev->os_priv, 0, priv_size); usbi_mutex_lock(&ctx->usb_devs_lock); @@ -775,6 +776,17 @@ unsigned long API_EXPORTED libusb_get_session_id(libusb_device *dev) return dev->session_data; } +/** \ingroup dev + * Get the negotiated speed of the device. + * \param dev a device + * \returns the device speed or LIBUSB_SPEED_UNKNOWN if the OS doesn't know or + * support returning the negotiated speed. + */ +enum libusb_speed 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) { @@ -1108,6 +1120,51 @@ out: 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); @@ -1322,7 +1379,7 @@ int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev, int r = 0; usbi_dbg("interface %d", interface_number); - if (interface_number >= sizeof(dev->claimed_interfaces) * 8) + if (interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; usbi_mutex_lock(&dev->lock); @@ -1359,7 +1416,7 @@ int API_EXPORTED libusb_release_interface(libusb_device_handle *dev, int r; usbi_dbg("interface %d", interface_number); - if (interface_number >= sizeof(dev->claimed_interfaces) * 8) + if (interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; usbi_mutex_lock(&dev->lock); @@ -1403,7 +1460,7 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev, { usbi_dbg("interface %d altsetting %d", interface_number, alternate_setting); - if (interface_number >= sizeof(dev->claimed_interfaces) * 8) + if (interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; usbi_mutex_lock(&dev->lock); diff --git a/libusb/descriptor.c b/libusb/descriptor.c index 11480e8..d6ec46c 100644 --- a/libusb/descriptor.c +++ b/libusb/descriptor.c @@ -257,11 +257,13 @@ static int parse_interface(libusb_context *ctx, } /* Did we hit an unexpected descriptor? */ - usbi_parse_descriptor(buffer, "bb", &header, 0); - if ((size >= DESC_HEADER_LENGTH) && - ((header.bDescriptorType == LIBUSB_DT_CONFIG) || - (header.bDescriptorType == LIBUSB_DT_DEVICE))) - return parsed; + if (size >= DESC_HEADER_LENGTH) { + usbi_parse_descriptor(buffer, "bb", &header, 0); + if ((header.bDescriptorType == LIBUSB_DT_CONFIG) || + (header.bDescriptorType == LIBUSB_DT_DEVICE)) { + return parsed; + } + } if (ifp->bNumEndpoints > USB_MAXENDPOINTS) { usbi_err(ctx, "too many endpoints (%d)", ifp->bNumEndpoints); diff --git a/libusb/io.c b/libusb/io.c index bffa484..bc2ef79 100644 --- a/libusb/io.c +++ b/libusb/io.c @@ -1022,7 +1022,7 @@ int usbi_io_init(struct libusb_context *ctx) #endif usbi_mutex_init(&ctx->flying_transfers_lock, NULL); #if defined(OS_WINDOWS) - usbi_mutex_init(&ctx->events_lock, NULL); + usbi_mutex_init_recursive(&ctx->events_lock, NULL); #else pthread_mutex_init(&ctx->events_lock, &attr); pthread_mutexattr_destroy(&attr); @@ -1290,6 +1290,8 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) if (r < 0) r = LIBUSB_ERROR_OTHER; } +#else + (void)first; #endif out: @@ -1320,9 +1322,16 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer) usbi_dbg(""); usbi_mutex_lock(&itransfer->lock); r = usbi_backend->cancel_transfer(itransfer); - if (r < 0) + if (r < 0) { usbi_err(TRANSFER_CTX(transfer), "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; } diff --git a/libusb/libusb-1.0.def b/libusb/libusb-1.0.def index 1ae2756..b2f3861 100644 --- a/libusb/libusb-1.0.def +++ b/libusb/libusb-1.0.def @@ -240,6 +240,16 @@ EXPORTS libusb_get_device_list@32 = libusb_get_device_list libusb_get_device_list@4 = libusb_get_device_list libusb_get_device_list@8 = libusb_get_device_list + libusb_get_device_speed + libusb_get_device_speed@0 = libusb_get_device_speed + libusb_get_device_speed@12 = libusb_get_device_speed + libusb_get_device_speed@16 = libusb_get_device_speed + libusb_get_device_speed@20 = libusb_get_device_speed + libusb_get_device_speed@24 = libusb_get_device_speed + libusb_get_device_speed@28 = libusb_get_device_speed + libusb_get_device_speed@32 = libusb_get_device_speed + libusb_get_device_speed@4 = libusb_get_device_speed + libusb_get_device_speed@8 = libusb_get_device_speed libusb_get_max_iso_packet_size libusb_get_max_iso_packet_size@0 = libusb_get_max_iso_packet_size libusb_get_max_iso_packet_size@12 = libusb_get_max_iso_packet_size diff --git a/libusb/libusb-1.0.pc.cmake b/libusb/libusb-1.0.pc.cmake new file mode 100644 index 0000000..6bf58d0 --- /dev/null +++ b/libusb/libusb-1.0.pc.cmake @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@CMAKE_INSTALL_PREFIX@/lib@ +includedir=@CMAKE_INSTALL_PREFIX@/include@ + +Name: libusb-1.0 +Description: C API for USB device access from Linux, Mac OS X and Windows userspace +Version: @VERSION@ +Libs: -L${libdir} -lusb-1.0 +Libs.private: @LIBUSB_LIB_DEPENDS@ +Cflags: -I${includedir}/libusb-1.0 + diff --git a/libusb/libusb.h b/libusb/libusb.h index 9799fb5..852a9e3 100644 --- a/libusb/libusb.h +++ b/libusb/libusb.h @@ -155,11 +155,15 @@ enum libusb_class_code { /** Human Interface Device class */ LIBUSB_CLASS_HID = 3, - /** Printer dclass */ + /** Physical */ + LIBUSB_CLASS_PHYSICAL = 5, + + /** Printer class */ LIBUSB_CLASS_PRINTER = 7, - /** Picture transfer protocol class */ - LIBUSB_CLASS_PTP = 6, + /** 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, @@ -170,6 +174,21 @@ enum libusb_class_code { /** 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, @@ -675,6 +694,25 @@ typedef struct libusb_device libusb_device; */ typedef struct libusb_device_handle libusb_device_handle; +/** \ingroup dev + * Speed codes. Indicates the speed at which the device is operating. + */ +enum libusb_speed { + /** The OS doesn't report or know the device speed. */ + LIBUSB_SPEED_UNKNOWN = 0, + + /** The device is operating at low speed (1.5MBit/s). */ + LIBUSB_SPEED_LOW = 1, + + /** The device is operating at full speed (12MBit/s). */ + LIBUSB_SPEED_FULL = 2, + + /** The device is operating at high speed (480MBit/s). */ + LIBUSB_SPEED_HIGH = 3, + + /** The device is operating at super speed (5000MBit/s). */ + LIBUSB_SPEED_SUPER = 4, +}; /** \ingroup misc * Error codes. Most libusb functions return 0 on success or one of these @@ -900,6 +938,7 @@ uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev); libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev); int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t* path, uint8_t path_length); uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev); +enum libusb_speed 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, diff --git a/libusb/libusbi.h b/libusb/libusbi.h index 9cf8aa3..5b39903 100644 --- a/libusb/libusbi.h +++ b/libusb/libusbi.h @@ -297,6 +297,7 @@ struct libusb_device { uint8_t device_address; usbi_mutex_t devaddr_lock; uint8_t num_configurations; + enum libusb_speed speed; struct list_head list; unsigned long session_data; @@ -353,7 +354,13 @@ enum usbi_transfer_flags { USBI_TRANSFER_TIMED_OUT = 1 << 0, /* Set by backend submit_transfer() if the OS handles timeout */ - USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1 << 1 + USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1 << 1, + + /* Cancellation was requested via libusb_cancel_transfer() */ + USBI_TRANSFER_CANCELLING = 1 << 2, + + /* Operation on the transfer failed because the device disappeared */ + USBI_TRANSFER_DEVICE_DISAPPEARED = 1 << 3, }; #define __USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \ diff --git a/libusb/os/CMakeLists.txt b/libusb/os/CMakeLists.txt new file mode 100644 index 0000000..1172c9b --- /dev/null +++ b/libusb/os/CMakeLists.txt @@ -0,0 +1,101 @@ +include(FindThreads) + +set(PTHREADS_ENABLED FALSE) +if (CMAKE_USE_PTHREADS_INIT) + set(PTHREADS_ENABLED TRUE) +endif() + +if (WIN32 OR "${CMAKE_SYSTEM_NAME}" STREQUAL "CYGWIN") + set(OS_WINDOWS 1 CACHE INTERNAL "controls config.h macro definition" FORCE) + + # Enable MingW support for RC language (for CMake pre-2.8) + if (MINGW) + set(CMAKE_RC_COMPILER_INIT windres) + set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> <FLAGS> -O coff <DEFINES> -i <SOURCE> -o <OBJECT>") + endif() + enable_language(RC) + + if ("${CMAKE_SYSTEM_NAME}" STREQUAL "CYGWIN") + message(STATUS "Detected cygwin") + set(PTHREADS_ENABLED TRUE) + set(WITHOUT_POLL_H TRUE CACHE INTERNAL "Disable using poll.h even if it's available - use windows poll instead fo cygwin's" FORCE) + endif() + + list(APPEND PLATFORM_SRC + poll_windows.c + windows_usb.c + ) + + if (PTHREADS_ENABLED AND NOT WITHOUT_PTHREADS) + list(APPEND PLATFORM_SRC threads_posix) + else() + list(APPEND PLATFORM_SRC threads_windows.c) + endif() +elseif (APPLE) + # Apple != OSX alone + set(OS_DARWIN 1 CACHE INTERNAL "controls config.h macro definition" FORCE) + + if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") + set(PLATFORM_SRC + darwin_usb.c + threads_posix.c + ) + + find_package(IOKit REQUIRED) + list(APPEND LIBUSB_LIBRARIES ${IOKit_LIBRARIES}) + + # Currently only objc_registerThreadWithCollector requires linking against it + # which is only for MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + include(CheckCSourceCompiles) + check_c_source_compiles( +"#include <AvailabilityMacros.h> +int main() +{ +#if !(MAC_OS_X_VERSION_MIN_REQUIRED >= 1060) +#error \"Don't need objc\" +#endif +} +" NEED_OBJC_REGISTER_THREAD_WITH_COLLECTOR) + + if (NEED_OBJC_REGISTER_THREAD_WITH_COLLECTOR) + find_library(LIBOBJC objc) + if (NOT LIBOBJC) + message(SEND_ERROR "Need objc library but can't find it") + else() + list(APPEND LIBUSB_LIBRARIES ${LIBOBJC}) + endif() + endif() + endif() +elseif (UNIX) + # Unix is for all *NIX systems including OSX + if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + set(OS_LINUX 1 CACHE INTERNAL "controls config.h macro definition" FORCE) + + set(PLATFORM_SRC + linux_usbfs.c + threads_posix.c + ) + + list(APPEND LIBUSB_LIBRARIES rt) + endif() +endif() + +if (NOT PLATFORM_SRC) + message(FATAL_ERROR "Unsupported platform ${CMAKE_SYSTEM_NAME}. Currently only support Windows, OSX, & Linux.") +endif() + +# the paths are relative to this directory but used in the parent directory, +# so we have to adjust the paths +foreach(SRC IN LISTS PLATFORM_SRC) + list(APPEND LIBUSB_PLATFORM ${CMAKE_CURRENT_SOURCE_DIR}/${SRC}) +endforeach() + +# export one level up so that the generic +# libusb parts know what the platform bits are supposed to be +set(LIBUSB_PLATFORM ${LIBUSB_PLATFORM} PARENT_SCOPE) +set(LIBUSB_LIBRARIES ${LIBUSB_LIBRARIES} PARENT_SCOPE) + +if (WITHOUT_PTHREADS) + set(PTHREADS_ENABLED FALSE) +endif() +set(THREADS_POSIX ${PTHREADS_ENABLED} CACHE INTERNAL "use pthreads" FORCE) diff --git a/libusb/os/darwin_usb.c b/libusb/os/darwin_usb.c index 7a8608f..7f12c24 100644 --- a/libusb/os/darwin_usb.c +++ b/libusb/os/darwin_usb.c @@ -1,6 +1,6 @@ /* * darwin backend for libusb 1.0 - * Copyright (C) 2008-2010 Nathan Hjelm <hjelmn@users.sourceforge.net> + * Copyright (C) 2008-2011 Nathan Hjelm <hjelmn@users.sourceforge.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -34,8 +34,13 @@ #include <mach/clock.h> #include <mach/clock_types.h> #include <mach/mach_host.h> - #include <mach/mach_port.h> + +#include <AvailabilityMacros.h> +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + #include <objc/objc-auto.h> +#endif + #include <IOKit/IOCFBundle.h> #include <IOKit/usb/IOUSBLib.h> #include <IOKit/IOCFPlugIn.h> @@ -152,10 +157,32 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui return -1; } -static int usb_setup_device_iterator ( - struct darwin_context_priv* ctx_priv, io_iterator_t * deviceIterator) { - return IOServiceGetMatchingServices ( - ctx_priv->libusb_darwin_mp, IOServiceMatching (kIOUSBDeviceClassName), deviceIterator); +static int usb_setup_device_iterator (io_iterator_t *deviceIterator, long location) { + CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName); + + if (!matchingDict) + return kIOReturnError; + + if (location) { + CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + if (propertyMatchDict) { + CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberLongType, &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(libusb_darwin_mp, matchingDict, deviceIterator); } static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32 *locationp) { @@ -202,7 +229,7 @@ static kern_return_t darwin_get_device (struct darwin_context_priv* ctx_priv, UInt32 location; io_iterator_t deviceIterator; - kresult = usb_setup_device_iterator (ctx_priv, &deviceIterator); + kresult = usb_setup_device_iterator (&deviceIterator, dev_location); if (kresult) return kresult; @@ -272,6 +299,7 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { io_service_t device; long location; + bool locationValid; CFTypeRef locationCF; UInt32 message; @@ -284,10 +312,19 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { device, CFSTR (kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0); - CFNumberGetValue(locationCF, kCFNumberLongType, &location); - CFRelease (locationCF); IOObjectRelease (device); + if (!locationCF) + continue; + + locationValid = CFGetTypeID(locationCF) == CFNumberGetTypeID() && + CFNumberGetValue(locationCF, kCFNumberLongType, &location); + + CFRelease (locationCF); + + if (!locationValid) + continue; + usbi_mutex_lock(&ctx->open_devs_lock); list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) { dpriv = (struct darwin_device_priv *)handle->dev->os_priv; @@ -319,7 +356,15 @@ static void darwin_clear_iterator (io_iterator_t iter) { static void *event_thread_main (void *arg0) { IOReturn kresult; struct libusb_context *ctx = (struct libusb_context *)arg0; - struct darwin_context_priv *ctx_priv = __ctx_priv (ctx); + struct darwin_context_priv *ctx_priv = __ctx_priv (ctx); CFRunLoopRef runloop; + + /* 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. */ +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + objc_registerThreadWithCollector(); +#endif /* hotplug (device removal) source */ CFRunLoopSourceRef libusb_notification_cfsource; @@ -329,7 +374,8 @@ static void *event_thread_main (void *arg0) { usbi_info (ctx, "creating hotplug event source"); - CFRetain (CFRunLoopGetCurrent ()); + runloop = CFRunLoopGetCurrent (); + CFRetain (runloop); /* add the notification port to the run loop */ libusb_notification_port = @@ -360,7 +406,7 @@ static void *event_thread_main (void *arg0) { if (kresult != kIOReturnSuccess) { usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); - pthread_exit ((void *)kresult); + pthread_exit (NULL); } /* arm notifiers */ @@ -380,11 +426,11 @@ static void *event_thread_main (void *arg0) { CFRunLoopSourceInvalidate (libusb_notification_cfsource); IONotificationPortDestroy (libusb_notification_port); - CFRelease (CFRunLoopGetCurrent ()); + CFRelease (runloop); ctx_priv->libusb_darwin_acfl = NULL; - pthread_exit (0); + pthread_exit (NULL); } static int darwin_init(struct libusb_context *ctx) { @@ -422,12 +468,10 @@ static int darwin_init(struct libusb_context *ctx) { static void darwin_exit (libusb_context* ctx) { struct darwin_context_priv *ctx_priv = __ctx_priv (ctx); if (!(--ctx_priv->initCount)) { - void *ret; /* stop the async runloop */ - CFRunLoopStop (ctx_priv->libusb_darwin_acfl); - /* TODO: pthread_join -> usbi_join? */ - pthread_join (ctx_priv->libusb_darwin_at, &ret); + CFRunLoopStop (libusb_darwin_acfl); + pthread_join (libusb_darwin_at, NULL); if (ctx_priv->libusb_darwin_mp) mach_port_deallocate(mach_task_self(), ctx_priv->libusb_darwin_mp); @@ -525,91 +569,225 @@ static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t confi return darwin_to_libusb (kresult); } -static int add_new_device ( - struct libusb_context *ctx, usb_device_t **device, UInt32 locationID, - struct libusb_device **dev) -{ - struct darwin_device_priv *priv; - UInt16 address, idVendor, idProduct; - UInt8 bDeviceClass, bDeviceSubClass; - IOUSBDevRequest req; - int ret = 0, need_unref = 0; +/* check whether the os has configured the device */ +static int darwin_check_configuration (struct libusb_context *ctx, struct libusb_device *dev, usb_device_t **darwin_device) { + struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv; - usbi_info (ctx, "allocating new device for location 0x%08x", locationID); - *dev = usbi_alloc_device(ctx, locationID); + IOUSBConfigurationDescriptorPtr configDesc; + IOUSBFindInterfaceRequest request; + kern_return_t kresult; + io_iterator_t interface_iterator; + io_service_t firstInterface; - if (!*dev) { - return LIBUSB_ERROR_NO_MEM; + if (priv->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 */ } - priv = (struct darwin_device_priv *)(*dev)->os_priv; + /* find the first configuration */ + kresult = (*darwin_device)->GetConfigurationDescriptorPtr (darwin_device, 0, &configDesc); + priv->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 (priv->dev_descriptor.bNumConfigurations == 1) + /* to avoid problems with some devices get the configurations value from the configuration descriptor */ + priv->active_config = priv->first_config; + else + /* devices with more than one configuration should work with GetConfiguration */ + (*darwin_device)->GetConfiguration (darwin_device, &priv->active_config); + } else + /* not configured */ + priv->active_config = 0; + + usbi_info (ctx, "active config: %u, first config: %u", priv->active_config, priv->first_config); + + return 0; +} + +static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct libusb_device *dev, usb_device_t **device) { + struct darwin_device_priv *priv; + int retries = 5, delay = 30000; + int unsuspended = 0, try_unsuspend = 1, try_reconfigure = 1; + int is_open = 0; + int ret = 0, ret2; + IOUSBDevRequest req; + UInt8 bDeviceClass; + UInt16 idProduct, idVendor; + + (*device)->GetDeviceClass (device, &bDeviceClass); + (*device)->GetDeviceProduct (device, &idProduct); + (*device)->GetDeviceVendor (device, &idVendor); + + priv = (struct darwin_device_priv *)dev->os_priv; + + /* try to open the device (we can usually continue even if this fails) */ + is_open = ((*device)->USBDeviceOpenSeize(device) == kIOReturnSuccess); + /**** retrieve device descriptor ****/ + do { /* Set up request for device descriptor */ + memset (&(priv->dev_descriptor), 0, sizeof(IOUSBDeviceDescriptor)); req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); req.bRequest = kUSBRqGetDescriptor; req.wValue = kUSBDeviceDesc << 8; req.wIndex = 0; - req.wLength = sizeof(IOUSBDeviceDescriptor); + req.wLength = sizeof(priv->dev_descriptor); req.pData = &(priv->dev_descriptor); - (*(device))->GetDeviceAddress (device, (USBDeviceAddress *)&address); - (*(device))->GetDeviceProduct (device, &idProduct); - (*(device))->GetDeviceVendor (device, &idVendor); - (*(device))->GetDeviceClass (device, &bDeviceClass); - (*(device))->GetDeviceSubClass (device, &bDeviceSubClass); - - /**** retrieve device descriptors ****/ - /* device must be open for DeviceRequest */ - (*device)->USBDeviceOpen(device); + /* 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 */ ret = (*(device))->DeviceRequest (device, &req); - if (ret != kIOReturnSuccess) { - int try_unsuspend = 1; + + if (kIOReturnOverrun == ret && kUSBDeviceDesc == priv->dev_descriptor.bDescriptorType) + /* received an overrun error but we still received a device descriptor */ + ret = kIOReturnSuccess; + + if (kIOReturnSuccess == ret && (0 == priv->dev_descriptor.idProduct || + 0 == priv->dev_descriptor.bNumConfigurations || + 0 == priv->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; - /* device may be suspended. unsuspend it and try again */ /* IOUSBFamily 320+ provides a way to detect device suspension but earlier versions do not */ (void)(*device)->GetUSBDeviceInformation (device, &info); try_unsuspend = info & (1 << kUSBInformationDeviceIsSuspendedBit); #endif - /* the device should be open before to device is unsuspended */ - (void) (*device)->USBDeviceOpenSeize(device); - if (try_unsuspend) { /* resume the device */ - (void)(*device)->USBDeviceSuspend (device, 0); - - ret = (*(device))->DeviceRequest (device, &req); - - /* resuspend the device */ - (void)(*device)->USBDeviceSuspend (device, 1); + 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; } - - (*device)->USBDeviceClose (device); } - if (ret != kIOReturnSuccess) { - usbi_warn (ctx, "could not retrieve device descriptor: %s. skipping device", darwin_error_str (ret)); - ret = -1; - goto end; + 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--); - /**** end: retrieve device descriptors ****/ + 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. skipping device", idVendor, idProduct, darwin_error_str (ret)); + else + usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s. skipping device", idVendor, idProduct, darwin_error_str (ret)); - /* catch buggy hubs (which appear to be virtual). Apple's own USB prober has problems with these devices. */ - if (libusb_le16_to_cpu (priv->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 (priv->dev_descriptor.idProduct)); - ret = -1; + return -1; + } + + usbi_dbg ("device descriptor:"); + usbi_dbg (" bDescriptorType: 0x%02x", priv->dev_descriptor.bDescriptorType); + usbi_dbg (" bcdUSB: 0x%04x", priv->dev_descriptor.bcdUSB); + usbi_dbg (" bDeviceClass: 0x%02x", priv->dev_descriptor.bDeviceClass); + usbi_dbg (" bDeviceSubClass: 0x%02x", priv->dev_descriptor.bDeviceSubClass); + usbi_dbg (" bDeviceProtocol: 0x%02x", priv->dev_descriptor.bDeviceProtocol); + usbi_dbg (" bMaxPacketSize0: 0x%02x", priv->dev_descriptor.bMaxPacketSize0); + usbi_dbg (" idVendor: 0x%04x", priv->dev_descriptor.idVendor); + usbi_dbg (" idProduct: 0x%04x", priv->dev_descriptor.idProduct); + usbi_dbg (" bcdDevice: 0x%04x", priv->dev_descriptor.bcdDevice); + usbi_dbg (" iManufacturer: 0x%02x", priv->dev_descriptor.iManufacturer); + usbi_dbg (" iProduct: 0x%02x", priv->dev_descriptor.iProduct); + usbi_dbg (" iSerialNumber: 0x%02x", priv->dev_descriptor.iSerialNumber); + usbi_dbg (" bNumConfigurations: 0x%02x", priv->dev_descriptor.bNumConfigurations); + + /* catch buggy hubs (which appear to be virtual). Apple's own USB prober has problems with these devices. */ + if (libusb_le16_to_cpu (priv->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 (priv->dev_descriptor.idProduct)); + return -1; + } + + return 0; +} + +static int process_new_device (struct libusb_context *ctx, usb_device_t **device, UInt32 locationID, struct discovered_devs **_discdevs) { + struct darwin_device_priv *priv; + struct libusb_device *dev; + struct discovered_devs *discdevs; + UInt16 address; + UInt8 devSpeed; + int ret = 0, need_unref = 0; + + do { + dev = usbi_get_device_by_session_id(ctx, locationID); + if (!dev) { + usbi_info (ctx, "allocating new device for location 0x%08x", locationID); + dev = usbi_alloc_device(ctx, locationID); + need_unref = 1; + } else + usbi_info (ctx, "using existing device for location 0x%08x", locationID); + + if (!dev) { + ret = LIBUSB_ERROR_NO_MEM; goto end; } - (*dev)->bus_number = locationID >> 24; - (*dev)->device_address = address; + priv = (struct darwin_device_priv *)dev->os_priv; + + (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&address); + + ret = darwin_cache_device_descriptor (ctx, dev, device); + if (ret < 0) + goto end; /* check current active configuration (and cache the first configuration value-- which may be used by claim_interface) */ /* TODO: where does this come from??? @@ -617,10 +795,23 @@ static int add_new_device ( if (ret < 0) break; */ + dev->bus_number = locationID >> 24; + dev->device_address = address; + + (*device)->GetDeviceSpeed (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; + default: + usbi_warn (ctx, "Got unknown device speed %d", devSpeed); + } + /* save our location, we'll need this later */ priv->location = locationID; - priv->status_changed = 0; - snprintf(priv->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", address, idVendor, idProduct, bDeviceClass, bDeviceSubClass); + snprintf(priv->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", address, priv->dev_descriptor.idVendor, priv->dev_descriptor.idProduct, + priv->dev_descriptor.bDeviceClass, priv->dev_descriptor.bDeviceSubClass); ret = usbi_sanitize_device (*dev); @@ -681,7 +872,7 @@ static int darwin_get_device_list(struct libusb_context *ctx, struct discovered_ if (!ctx_priv->libusb_darwin_mp) return LIBUSB_ERROR_INVALID_PARAM; - kresult = usb_setup_device_iterator (ctx_priv, &deviceIterator); + kresult = usb_setup_device_iterator (&deviceIterator, 0); if (kresult != kIOReturnSuccess) return darwin_to_libusb (kresult); diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c index 9bf49d2..6f1067f 100644 --- a/libusb/os/linux_usbfs.c +++ b/libusb/os/linux_usbfs.c @@ -24,7 +24,6 @@ #include <errno.h> #include <fcntl.h> #include <poll.h> -#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -96,10 +95,10 @@ static clockid_t monotonic_clkid = -1; /* do we have a busnum to relate devices? this also implies that we can read * the active configuration through bConfigurationValue */ -static int sysfs_can_relate_devices = -1; +static int sysfs_can_relate_devices = 0; /* do we have a descriptors file? */ -static int sysfs_has_descriptors = -1; +static int sysfs_has_descriptors = 0; struct linux_context_priv { struct udev *udev_ctx; @@ -254,6 +253,22 @@ static int check_flag_bulk_continuation(void) return 1; } +/* Return 1 if filename exists inside dirname in sysfs. + SYSFS_DEVICE_PATH is assumed to be the beginning of the path. */ +static int sysfs_has_file(const char *dirname, const char *filename) +{ + struct stat statbuf; + char path[PATH_MAX]; + int r; + + snprintf(path, PATH_MAX, "%s/%s/%s", SYSFS_DEVICE_PATH, dirname, filename); + r = stat(path, &statbuf); + if (r == 0 && S_ISREG(statbuf.st_mode)) + return 1; + + return 0; +} + static int op_init(struct libusb_context *ctx) { struct linux_context_priv* ctx_priv = __ctx_priv(ctx); @@ -283,7 +298,60 @@ static int op_init(struct libusb_context *ctx) r = stat(SYSFS_DEVICE_PATH, &statbuf); if (r == 0 && S_ISDIR(statbuf.st_mode)) { + DIR *devices = opendir(SYSFS_DEVICE_PATH); + struct dirent *entry; + usbi_dbg("found usb devices in sysfs"); + + if (!devices) { + usbi_err(ctx, "opendir devices failed errno=%d", errno); + return LIBUSB_ERROR_IO; + } + + /* Make sure sysfs supports all the required files. If it + * does not, then usbfs will be used instead. Determine + * this by looping through the directories in + * SYSFS_DEVICE_PATH. With the assumption that there will + * always be subdirectories of the name usbN (usb1, usb2, + * etc) representing the root hubs, check the usbN + * subdirectories to see if they have all the needed files. + * This algorithm uses the usbN subdirectories (root hubs) + * because a device disconnection will cause a race + * condition regarding which files are available, sometimes + * causing an incorrect result. The root hubs are used + * because it is assumed that they will always be present. + * See the "sysfs vs usbfs" comment at the top of this file + * for more details. */ + while ((entry = readdir(devices))) { + int has_busnum=0, has_devnum=0, has_descriptors=0; + int has_configuration_value=0; + + /* Only check the usbN directories. */ + if (strncmp(entry->d_name, "usb", 3) != 0) + continue; + + /* Check for the files libusb needs from sysfs. */ + has_busnum = sysfs_has_file(entry->d_name, "busnum"); + has_devnum = sysfs_has_file(entry->d_name, "devnum"); + has_descriptors = sysfs_has_file(entry->d_name, "descriptors"); + has_configuration_value = sysfs_has_file(entry->d_name, "bConfigurationValue"); + + if (has_busnum && has_devnum && has_configuration_value) + sysfs_can_relate_devices = 1; + if (has_descriptors) + sysfs_has_descriptors = 1; + + /* Only need to check until we've found ONE device which + has all the attributes. */ + if (sysfs_has_descriptors && sysfs_can_relate_devices) + break; + } + closedir(devices); + + /* Only use sysfs descriptors if the rest of + sysfs will work for libusb. */ + if (!sysfs_can_relate_devices) + sysfs_has_descriptors = 0; } else { usbi_dbg("sysfs usb info not available"); sysfs_has_descriptors = 0; @@ -356,6 +424,41 @@ static int __open_sysfs_attr(struct libusb_device *dev, const char *attr) 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 sysfs_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer) { @@ -730,7 +833,7 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum, struct linux_device_priv *priv = __device_priv(dev); unsigned char *dev_buf; char path[PATH_MAX]; - int fd; + int fd, speed; int active_config = 0; int device_configured = 1; ssize_t r; @@ -743,6 +846,20 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum, 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); + } + } } if (sysfs_has_descriptors) @@ -983,70 +1100,20 @@ out: } static int sysfs_scan_device(struct libusb_context *ctx, - struct discovered_devs **_discdevs, const char *devname, - int *usbfs_fallback) + struct discovered_devs **_discdevs, const char *devname) { - int r; - FILE *fd; - char filename[PATH_MAX]; int busnum; int devaddr; usbi_dbg("scan %s", devname); - /* determine descriptors presence ahead of time, we need to know this - * when we reach initialize_device */ - if (sysfs_has_descriptors == -1) { - struct stat statbuf; - - snprintf(filename, PATH_MAX, "%s/%s/descriptors", SYSFS_DEVICE_PATH, - devname); - r = stat(filename, &statbuf); - if (r == 0 && S_ISREG(statbuf.st_mode)) { - usbi_dbg("sysfs descriptors available"); - sysfs_has_descriptors = 1; - } else { - usbi_dbg("sysfs descriptors not available"); - sysfs_has_descriptors = 0; - } - } - - snprintf(filename, PATH_MAX, "%s/%s/busnum", SYSFS_DEVICE_PATH, devname); - fd = fopen(filename, "r"); - if (!fd) { - if (errno == ENOENT) { - usbi_dbg("busnum not found, cannot relate sysfs to usbfs, " - "falling back on pure usbfs"); - sysfs_can_relate_devices = 0; - *usbfs_fallback = 1; - return LIBUSB_ERROR_OTHER; - } - usbi_err(ctx, "open busnum failed, errno=%d", errno); - return LIBUSB_ERROR_IO; - } - - sysfs_can_relate_devices = 1; + busnum = __read_sysfs_attr(ctx, devname, "busnum"); + if (busnum < 0) + return busnum; - r = fscanf(fd, "%d", &busnum); - fclose(fd); - if (r != 1) { - usbi_err(ctx, "fscanf busnum returned %d, errno=%d", r, errno); - return LIBUSB_ERROR_IO; - } - - snprintf(filename, PATH_MAX, "%s/%s/devnum", SYSFS_DEVICE_PATH, devname); - fd = fopen(filename, "r"); - if (!fd) { - usbi_err(ctx, "open devnum failed, errno=%d", errno); - return LIBUSB_ERROR_IO; - } - - r = fscanf(fd, "%d", &devaddr); - fclose(fd); - if (r != 1) { - usbi_err(ctx, "fscanf devnum returned %d, errno=%d", r, errno); - return LIBUSB_ERROR_IO; - } + devaddr = __read_sysfs_attr(ctx, devname, "devnum"); + if (devaddr < 0) + return devaddr; usbi_dbg("bus=%d dev=%d", busnum, devaddr); if (busnum > 255 || devaddr > 255) @@ -1125,7 +1192,7 @@ static void sysfs_analyze_topology(struct discovered_devs *discdevs) } static int sysfs_get_device_list(struct libusb_context *ctx, - struct discovered_devs **_discdevs, int *usbfs_fallback) + struct discovered_devs **_discdevs) { struct discovered_devs *discdevs = *_discdevs; DIR *devices = opendir(SYSFS_DEVICE_PATH); @@ -1144,13 +1211,12 @@ static int sysfs_get_device_list(struct libusb_context *ctx, || strchr(entry->d_name, ':')) continue; - r = sysfs_scan_device(ctx, &discdevs_new, entry->d_name, - usbfs_fallback); - if (r < 0) + r = sysfs_scan_device(ctx, &discdevs_new, entry->d_name); + if (r < 0 && r != LIBUSB_ERROR_NO_DEVICE) goto out; discdevs = discdevs_new; } - + r = 0; out: closedir(devices); *_discdevs = discdevs; @@ -1166,19 +1232,15 @@ static int op_get_device_list(struct libusb_context *ctx, * 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, sometimes we have - * sysfs but not enough information to relate sysfs devices to usbfs - * nodes. the usbfs_fallback variable is used to indicate that we should - * fall back on usbfs. + * 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) { - int usbfs_fallback = 0; - int r = sysfs_get_device_list(ctx, _discdevs, &usbfs_fallback); - if (!usbfs_fallback) - return r; - } - - return usbfs_get_device_list(ctx, _discdevs); + if (sysfs_can_relate_devices != 0) + return sysfs_get_device_list(ctx, _discdevs); + else + return usbfs_get_device_list(ctx, _discdevs); } static int op_open(struct libusb_device_handle *handle) @@ -1222,6 +1284,9 @@ static int op_get_configuration(struct libusb_device_handle *handle, return LIBUSB_ERROR_NOT_SUPPORTED; r = sysfs_get_active_config(handle->dev, config); + if (r < 0) + return r; + if (*config == -1) *config = 0; @@ -1344,17 +1409,47 @@ static int op_clear_halt(struct libusb_device_handle *handle, static int op_reset_device(struct libusb_device_handle *handle) { int fd = __device_handle_priv(handle)->fd; - int r = ioctl(fd, IOCTL_USBFS_RESET, NULL); + 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)) { + op_release_interface(handle, i); + } + } + + usbi_mutex_lock(&handle->lock); + r = ioctl(fd, IOCTL_USBFS_RESET, NULL); if (r) { - if (errno == ENODEV) - return LIBUSB_ERROR_NOT_FOUND; + if (errno == ENODEV) { + ret = LIBUSB_ERROR_NOT_FOUND; + goto out; + } usbi_err(HANDLE_CTX(handle), "reset failed error %d errno %d", r, errno); - return LIBUSB_ERROR_OTHER; + ret = LIBUSB_ERROR_OTHER; + goto out; } - return 0; + /* And re-claim any interfaces which were claimed before the reset */ + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (handle->claimed_interfaces & (1L << i)) { + r = op_claim_interface(handle, i); + if (r) { + usbi_warn(HANDLE_CTX(handle), + "failed to re-claim interface %d after reset", i); + handle->claimed_interfaces &= ~(1L << i); + } + } + } +out: + usbi_mutex_unlock(&handle->lock); + return ret; } static int op_kernel_driver_active(struct libusb_device_handle *handle, @@ -1477,6 +1572,9 @@ static int discard_urbs(struct usbi_transfer *itransfer, int first, int last_plu if (EINVAL == errno) { usbi_dbg("URB not found --> assuming ready to be reaped"); 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); @@ -2136,6 +2234,7 @@ static int handle_control_completion(struct usbi_transfer *itransfer, status = LIBUSB_TRANSFER_COMPLETED; break; case -ENOENT: /* cancelled */ + status = LIBUSB_TRANSFER_CANCELLED; break; case -ESHUTDOWN: usbi_dbg("device removed"); diff --git a/libusb/os/poll_windows.c b/libusb/os/poll_windows.c index 9d6cea1..9d6f3e6 100644 --- a/libusb/os/poll_windows.c +++ b/libusb/os/poll_windows.c @@ -120,7 +120,7 @@ static volatile LONG compat_spinlock = 0; // platform headers, we hook into the Kernel32 system DLL directly to seek it. static BOOL (__stdcall *pCancelIoEx)(HANDLE, LPOVERLAPPED) = NULL; #define CancelIoEx_Available (pCancelIoEx != NULL) -__inline BOOL cancel_io(int _index) +static __inline BOOL cancel_io(int _index) { if ((_index < 0) || (_index >= MAX_FDS)) { return FALSE; diff --git a/libusb/os/threads_posix.c b/libusb/os/threads_posix.c new file mode 100644 index 0000000..9b8487c --- /dev/null +++ b/libusb/os/threads_posix.c @@ -0,0 +1,55 @@ +/* + * libusb synchronization using POSIX Threads + * + * Copyright (C) 2011 Vitali Lovich <vlovich@aliph.com> + * Copyright (C) 2011 Peter Stuge <peter@stuge.se> + * + * 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 + */ + +#ifdef _XOPEN_SOURCE +# if _XOPEN_SOURCE < 500 +# undef _XOPEN_SOURCE +# define _XOPEN_SOURCE 500 +# endif +#else +#define _XOPEN_SOURCE 500 +#endif /* _XOPEN_SOURCE */ + +#include <pthread.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; + } + + 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; +}
\ No newline at end of file diff --git a/libusb/os/threads_posix.h b/libusb/os/threads_posix.h index dc558d4..6aa13f5 100644 --- a/libusb/os/threads_posix.h +++ b/libusb/os/threads_posix.h @@ -43,4 +43,6 @@ #define usbi_cond_destroy pthread_cond_destroy #define usbi_cond_signal pthread_cond_signal +extern int usbi_mutex_init_recursive(pthread_mutex_t *mutex, pthread_mutexattr_t *attr); + #endif /* __LIBUSB_THREADS_POSIX_H__ */ diff --git a/libusb/os/threads_windows.h b/libusb/os/threads_windows.h index 2cd1867..9f77598 100644 --- a/libusb/os/threads_windows.h +++ b/libusb/os/threads_windows.h @@ -58,6 +58,8 @@ struct timespec { #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); @@ -81,4 +83,3 @@ int usbi_cond_broadcast(usbi_cond_t *cond); int usbi_cond_signal(usbi_cond_t *cond); #endif /* __LIBUSB_THREADS_WINDOWS_H__ */ - diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c index 9bd6924..743632a 100644 --- a/libusb/os/windows_usb.c +++ b/libusb/os/windows_usb.c @@ -1154,7 +1154,7 @@ static int init_device(struct libusb_device* dev, struct libusb_device* parent_d { HANDLE handle; DWORD size; - USB_NODE_CONNECTION_INFORMATION conn_info; + USB_NODE_CONNECTION_INFORMATION_EX conn_info; struct windows_device_priv *priv, *parent_priv; struct libusb_context *ctx = DEVICE_CTX(dev); struct libusb_device* tmp_dev; @@ -1206,9 +1206,9 @@ static int init_device(struct libusb_device* dev, struct libusb_device* parent_d usbi_warn(ctx, "could not open hub %s: %s", parent_priv->path, windows_error_str(0)); return LIBUSB_ERROR_ACCESS; } - size = sizeof(USB_NODE_CONNECTION_INFORMATION); + size = sizeof(conn_info); conn_info.ConnectionIndex = (ULONG)port_number; - if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION, &conn_info, size, + 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)); @@ -1221,6 +1221,14 @@ static int init_device(struct libusb_device* dev, struct libusb_device* parent_d return LIBUSB_ERROR_NO_DEVICE; } dev->device_address = (uint8_t)conn_info.DeviceAddress; + 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; + default: + usbi_warn(ctx, "Got unknown device speed %d", conn_info.Speed); + break; + } memcpy(&priv->dev_descriptor, &(conn_info.DeviceDescriptor), sizeof(USB_DEVICE_DESCRIPTOR)); dev->num_configurations = priv->dev_descriptor.bNumConfigurations; priv->active_config = conn_info.CurrentConfigurationValue; diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h index ce755e1..0abac3b 100644 --- a/libusb/os/windows_usb.h +++ b/libusb/os/windows_usb.h @@ -70,7 +70,7 @@ extern char *_strdup(const char *strSource); #define safe_sprintf _snprintf #define safe_unref_device(dev) do {if (dev != NULL) {libusb_unref_device(dev); dev = NULL;}} while(0) #define wchar_to_utf8_ms(wstr, str, strlen) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, strlen, NULL, NULL) -inline void upperize(char* str) { +static inline void upperize(char* str) { size_t i; if (str == NULL) return; for (i=0; i<safe_strlen(str); i++) @@ -327,10 +327,12 @@ typedef RETURN_TYPE CONFIGRET; #define USB_REQUEST_SYNC_FRAME LIBUSB_REQUEST_SYNCH_FRAME #define USB_GET_NODE_INFORMATION 258 -#define USB_GET_NODE_CONNECTION_INFORMATION 259 #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 @@ -391,8 +393,8 @@ DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG) #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 \ - CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES \ CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_ATTRIBUTES, METHOD_BUFFERED, FILE_ANY_ACCESS) @@ -522,17 +524,17 @@ typedef struct _USB_PIPE_INFO { ULONG ScheduleOffset; } USB_PIPE_INFO, *PUSB_PIPE_INFO; -typedef struct _USB_NODE_CONNECTION_INFORMATION { +typedef struct _USB_NODE_CONNECTION_INFORMATION_EX { ULONG ConnectionIndex; USB_DEVICE_DESCRIPTOR DeviceDescriptor; UCHAR CurrentConfigurationValue; - BOOLEAN LowSpeed; + UCHAR Speed; BOOLEAN DeviceIsHub; USHORT DeviceAddress; ULONG NumberOfOpenPipes; USB_CONNECTION_STATUS ConnectionStatus; // USB_PIPE_INFO PipeList[0]; -} USB_NODE_CONNECTION_INFORMATION, *PUSB_NODE_CONNECTION_INFORMATION; +} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX; typedef struct _USB_HUB_CAP_FLAGS { ULONG HubIsHighSpeedCapable:1; |