diff --git a/compat/libusb-1.0/AUTHORS b/compat/libusb-1.0/AUTHORS index 32d69483..2e80830d 100644 --- a/compat/libusb-1.0/AUTHORS +++ b/compat/libusb-1.0/AUTHORS @@ -1,46 +1,62 @@ -Copyright (C) 2007-2009 Daniel Drake -Copyright (c) 2001 Johannes Erdfelt -Copyright (C) 2008-2013 Nathan Hjelm -Copyright (C) 2009-2012 Pete Batard -Copyright (C) 2010 Michael Plante -Copyright (C) 2010-2012 Peter Stuge -Copyright (C) 2011-2012 Hans de Goede -Copyright (C) 2012 Martin Pieuchot +Copyright © 2001 Johannes Erdfelt +Copyright © 2007-2009 Daniel Drake +Copyright © 2010-2012 Peter Stuge +Copyright © 2008-2013 Nathan Hjelm +Copyright © 2009-2013 Pete Batard +Copyright © 2009-2013 Ludovic Rousseau +Copyright © 2010-2012 Michael Plante +Copyright © 2011-2013 Hans de Goede +Copyright © 2012-2013 Martin Pieuchot +Copyright © 2012-2013 Toby Gray Other contributors: Alan Ott Alan Stern Alex Vatchenko +Anthony Clay Artem Egorkine Aurelien Jarno Bastien Nocera -Brian Shirley +Benjamin Dobell +Chris Dickens +Colin Walters +Dave Camarillo David Engraf David Moore +Davidlohr Bueso +Federico Manzan Felipe Balbi +Florian Albrechtskirchinger +Francesco Montorsi Graeme Gill -Hans de Goede Hans Ulrich Niedermann Hector Martin Hoi-Ho Chan +Ilya Konstantinov James Hanko Konrad Rzepecki -Ludovic Rousseau +Lars Wirzenius +Luca Longinotti Martin Koegler -Martin Pieuchot -Maya Erez +Matthias Bolte Mike Frysinger Mikhail Gusarov +Nicholas Corgan Orin Eman +Paul Fertser Pekka Nikander -Peter Stuge Rob Walker Sean McBride Sebastian Pipping -Stephan Meyer +Simon Haggett Thomas Röfer +Tim Roberts Toby Peterson Trygve Laugstøl +Uri Lublin Vasily Khoruzhick +Vegard Storheil Eriksen Vitali Lovich Xiaofan Chen +Zoltán Kovács +Роман Донченко diff --git a/compat/libusb-1.0/ChangeLog b/compat/libusb-1.0/ChangeLog new file mode 100644 index 00000000..05e08de6 --- /dev/null +++ b/compat/libusb-1.0/ChangeLog @@ -0,0 +1,196 @@ +For detailed information about the changes below, please see the git log or +visit: http://log.libusbx.org + +2013-09-06: v1.0.17 +* Hotplug callbacks now always get passed a libusb_context, even if it is + the default context. Previously NULL would be passed for the default context, + but since the first context created is the default context, and most apps + use only 1 context, this meant that apps explicitly creating a context would + still get passed NULL +* Android: Add .mk files to build with the Android NDK +* Darwin: Add Xcode project +* Darwin: Fix crash on unplug (#121) +* Linux: Fix hang (deadlock) on libusb_exit +* Linux: Fix libusbx build failure with --disable-udev (#124) +* Linux: Fix libusb_get_device_list() hang with --disable-udev (#130) +* OpenBSD: Update OpenBSD backend with support for control transfers to + non-ugen(4) devices and make get_configuration() no longer generate I/O. + Note that using this libusbx version on OpenBSD requires using + OpenBSD 5.3-current or later. Users of older OpenBSD versions are advised + to stay with the libusb shipped with OpenBSD (mpi) +* Windows: fix libusb_dll_2010.vcxproj link errors (#129) +* Various other bug fixes and improvements +The (#xx) numbers are libusbx issue numbers, see ie: +https://github.com/libusbx/libusbx/issues/121 + +2013-07-11: v1.0.16 +* Add hotplug support for Darwin and Linux (#9) +* Add superspeed endpoint companion descriptor support (#15) +* Add BOS descriptor support (#15) +* Make descriptor parsing code more robust +* New libusb_get_port_numbers API, this is libusb_get_port_path without + the unnecessary context parameter, libusb_get_port_path is now deprecated +* New libusb_strerror API (#14) +* New libusb_set_auto_detach_kernel_driver API (#17) +* Improve topology API docs (#95) +* Logging now use a single write call per log-message, avoiding log-message + "interlacing" when using multiple threads. +* Android: use Android logging when building on Android (#101) +* Darwin: make libusb_reset reenumerate device on descriptors change (#89) +* Darwin: add support for the LIBUSB_TRANSFER_ADD_ZERO_PACKET flag (#91) +* Darwin: add a device cache (#112, #114) +* Examples: Add sam3u_benchmark isochronous example by Harald Welte (#109) +* Many other bug fixes and improvements +The (#xx) numbers are libusbx issue numbers, see ie: +https://github.com/libusbx/libusbx/issues/9 + +2013-04-15: v1.0.15 +* Improve transfer cancellation and avoid short read failures on broken descriptors +* Filter out 8-bit characters in libusb_get_string_descriptor_ascii() +* Add WinCE support +* Add library stress tests +* Add Cypress FX3 firmware upload support for fxload sample +* Add HID and kernel driver detach support capabilities detection +* Add SuperSpeed detection on OS X +* Fix bInterval value interpretation on OS X +* Fix issues with autoclaim, composite HID devices, interface autoclaim and + early abort in libusb_close() on Windows. Also add VS2012 solution files. +* Improve fd event handling on Linux +* Other bug fixes and improvements + +2012-09-26: v1.0.14 +* Reverts the previous API change with regards to bMaxPower. + If this doesn't matter to you, you are encouraged to keep using v1.0.13, + as it will use the same attribute as v2.0, to be released soon. +* Note that LIBUSBX_API_VERSION is *decreased* to 0x010000FF and the previous + guidelines with regards to concurrent use of MaxPower/bMaxPower still apply. + +2012-09-20: v1.0.13 +* [MAJOR] Fix a typo in the API with struct libusb_config_descriptor where + MaxPower was used instead of bMaxPower, as defined in the specs. If your + application was accessing the MaxPower attribute, and you need to maintain + compatibility with libusb or older versions, see APPENDIX A below. +* Fix broken support for the 0.1 -> 1.0 libusb-compat layer +* Fix unwanted cancellation of pending timeouts as well as major timeout related bugs +* Fix handling of HID and composite devices on Windows +* Introduce LIBUSBX_API_VERSION macro +* Add Cypress FX/FX2 firmware upload sample, based on fxload from + http://linux-hotplug.sourceforge.net +* Add libusb0 (libusb-win32) and libusbK driver support on Windows. Note that while + the drivers allow it, isochronous transfers are not supported yet in libusbx. Also + not supported yet is the use of libusb-win32 filter drivers on composite interfaces +* Add support for the new get_capabilities ioctl on Linux and avoid unnecessary + splitting of bulk transfers +* Improve support for newer Intel and Renesas USB 3.0 controllers on Windows +* Harmonize the device number for root hubs across platforms +* Other bug fixes and improvements + +2012-06-15: v1.0.12 +* Fix a potential major regression with pthread on Linux +* Fix missing thread ID from debug log output on cygwin +* Fix possible crash when using longjmp and MinGW's gcc 4.6 +* Add topology calls: libusb_get_port_number(), libusb_get_parent() & libusb_get_port_path() +* Add toggleable debug, using libusb_set_debug() or the LIBUSB_DEBUG environment variable +* Define log levels in libusb.h and set timestamp origin to first libusb_init() call +* All logging is now sent to to stderr (info was sent to stdout previously) +* Update log messages severity and avoid polluting log output on OS-X +* Add HID driver support on Windows +* Enable interchangeability of MSVC and MinGW DLLs +* Additional bug fixes and improvements + +2012-05-08: v1.0.11 +* Revert removal of critical Windows event handling that was introduced in 1.0.10 +* Fix a possible deadlock in Windows when submitting transfers +* Add timestamped logging +* Add NetBSD support (experimental) and BSD libusb_get_device_speed() data +* Add bootstrap.sh alongside autogen.sh (bootstrap.sh doesn't invoke configure) +* Search for device nodes in /dev for Android support +* Other bug fixes + +2012-04-17: v1.0.10 +* Public release +* Add libusb_get_version +* Add Visual Studio 2010 project files +* Some Windows code cleanup +* Fix xusb sample warnings + +2012-04-02: v1.0.9 +* First libusbx release +* Add libusb_get_device_speed (all, except BSD) and libusb_error_name +* Add Windows support (WinUSB driver only) +* Add OpenBSD support +* Add xusb sample +* Tons of bug fixes + +2010-05-07: v1.0.8 +* Bug fixes + +2010-04-19: v1.0.7 +* Bug fixes and documentation tweaks +* Add more interface class definitions + +2009-11-22: v1.0.6 +* Bug fixes +* Increase libusb_handle_events() timeout to 60s for powersaving + +2009-11-15: v1.0.5 + * Use timerfd when available for timer management + * Small fixes/updates + +2009-11-06: v1.0.4 release + * Bug fixes including transfer locking to fix some potential threading races + * More flexibility with clock types on Linux + * Use new bulk continuation tracking in Linux 2.6.32 for improved handling + of short/failed transfers + +2009-08-27: v1.0.3 release + * Bug fixes + * Add libusb_get_max_iso_packet_size() + +2009-06-13: v1.0.2 release + * Bug fixes + +2009-05-12: v1.0.1 release + * Bug fixes + * Darwin backend + +2008-12-13: v1.0.0 release + * Bug fixes + +2008-11-21: v0.9.4 release + * Bug fixes + * Add libusb_attach_kernel_driver() + +2008-08-23: v0.9.3 release + * Bug fixes + +2008-07-19: v0.9.2 release + * Bug fixes + +2008-06-28: v0.9.1 release + * Bug fixes + * Introduce contexts to the API + * Compatibility with new Linux kernel features + +2008-05-25: v0.9.0 release + * First libusb-1.0 beta release + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +APPENDIX A - How to maintain code compatibility with versions of libusb and +libusbx that use MaxPower: + +If you must to maintain compatibility with versions of the library that aren't +using the bMaxPower attribute in struct libusb_config_descriptor, the +recommended way is to use the new LIBUSBX_API_VERSION macro with an #ifdef. +For instance, if your code was written as follows: + + if (dev->config[0].MaxPower < 250) + +Then you should modify it to have: + +#if defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000100) + if (dev->config[0].bMaxPower < 250) +#else + if (dev->config[0].MaxPower < 250) +#endif diff --git a/compat/libusb-1.0/Makefile.am b/compat/libusb-1.0/Makefile.am index 3f1e7d20..0155625a 100644 --- a/compat/libusb-1.0/Makefile.am +++ b/compat/libusb-1.0/Makefile.am @@ -1,18 +1,23 @@ AUTOMAKE_OPTIONS = dist-bzip2 no-dist-gzip ACLOCAL_AMFLAGS = -I m4 DISTCLEANFILES = libusb-1.0.pc -MAINTAINERCLEANFILES = ChangeLog -EXTRA_DIST = TODO PORTING -SUBDIRS = libusb +EXTRA_DIST = TODO PORTING msvc libusb/libusb-1.0.def libusb/version_nano.h \ + examples/getopt/getopt.c examples/getopt/getopt1.c examples/getopt/getopt.h \ + android Xcode +SUBDIRS = libusb doc + +if BUILD_EXAMPLES +SUBDIRS += examples +endif + +if BUILD_TESTS +SUBDIRS += tests +endif pkgconfigdir=$(libdir)/pkgconfig pkgconfig_DATA=libusb-1.0.pc -.PHONY: ChangeLog dist-up -ChangeLog: - git --git-dir $(top_srcdir)/.git log > ChangeLog || touch ChangeLog - -dist-hook: ChangeLog +.PHONY: dist-up reldir = .release/$(distdir) dist-up: dist @@ -21,4 +26,3 @@ dist-up: dist cp $(distdir).tar.bz2 $(reldir) rsync -rv $(reldir) frs.sourceforge.net:/home/frs/project/l/li/libusb/libusb-1.0/ rm -rf $(reldir) - diff --git a/compat/libusb-1.0/NEWS b/compat/libusb-1.0/NEWS index f9487001..9c04694c 100644 --- a/compat/libusb-1.0/NEWS +++ b/compat/libusb-1.0/NEWS @@ -1,65 +1,2 @@ -This file lists notable changes in each release. For the full history of all -changes, see ChangeLog. - -2012-04-20: 1.0.9 -* Numerous bug fixes and improvements -* Backend for Windows, for devices using the WinUSB.sys driver -* Backend for OpenBSD and NetBSD, for devices using the ugen driver -* Add libusb_get_device_speed() -* Add libusb_has_capability() -* Add libusb_error_name() -* Add libusb_get_version() - -2010-05-07: v1.0.8 -* Bug fixes - -2010-04-19: v1.0.7 -* Bug fixes and documentation tweaks -* Add more interface class definitions - -2009-11-22: v1.0.6 -* Bug fixes -* Increase libusb_handle_events() timeout to 60s for powersaving - -2009-11-15: v1.0.5 - * Use timerfd when available for timer management - * Small fixes/updates - -2009-11-06: v1.0.4 release - * Bug fixes including transfer locking to fix some potential threading races - * More flexibility with clock types on Linux - * Use new bulk continuation tracking in Linux 2.6.32 for improved handling - of short/failed transfers - -2009-08-27: v1.0.3 release - * Bug fixes - * Add libusb_get_max_iso_packet_size() - -2009-06-13: v1.0.2 release - * Bug fixes - -2009-05-12: v1.0.1 release - * Bug fixes - * Darwin backend - -2008-12-13: v1.0.0 release - * Bug fixes - -2008-11-21: v0.9.4 release - * Bug fixes - * Add libusb_attach_kernel_driver() - -2008-08-23: v0.9.3 release - * Bug fixes - -2008-07-19: v0.9.2 release - * Bug fixes - -2008-06-28: v0.9.1 release - * Bug fixes - * Introduce contexts to the API - * Compatibility with new Linux kernel features - -2008-05-25: v0.9.0 release - * First libusb-1.0 beta release - +For the latest libusbx news, please refer to the ChangeLog file, or visit: +http://libusbx.org diff --git a/compat/libusb-1.0/PORTING b/compat/libusb-1.0/PORTING index 7070784d..9185c37f 100644 --- a/compat/libusb-1.0/PORTING +++ b/compat/libusb-1.0/PORTING @@ -1,15 +1,15 @@ -PORTING LIBUSB TO OTHER PLATFORMS +PORTING LIBUSBX TO OTHER PLATFORMS Introduction ============ -This document is aimed at developers wishing to port libusb to unsupported -platforms. I believe the libusb API is OS-independent, so by supporting +This document is aimed at developers wishing to port libusbx to unsupported +platforms. I believe the libusbx API is OS-independent, so by supporting multiple operating systems we pave the way for cross-platform USB device drivers. Implementation-wise, the basic idea is that you provide an interface to -libusb's internal "backend" API, which performs the appropriate operations on +libusbx's internal "backend" API, which performs the appropriate operations on your target platform. In terms of USB I/O, your backend provides functionality to submit @@ -27,16 +27,16 @@ e.g. setting configuration, obtaining descriptors, etc. File descriptors for I/O polling ================================ -For libusb to work, your event handling function obviously needs to be called +For libusbx to work, your event handling function obviously needs to be called at various points in time. Your backend must provide a set of file descriptors -which libusb and its users can pass to poll() or select() to determine when +which libusbx and its users can pass to poll() or select() to determine when it is time to call the event handling function. On Linux, this is easy: the usbfs kernel interface exposes a file descriptor which can be passed to poll(). If something similar is not true for your platform, you can emulate this using an internal library thread to reap I/O as necessary, and a pipe() with the main library to raise events. The file -descriptor of the pipe can then be provided to libusb as an event source. +descriptor of the pipe can then be provided to libusbx as an event source. Interface semantics and documentation @@ -46,7 +46,7 @@ Documentation of the backend interface can be found in libusbi.h inside the usbi_os_backend structure definition. Your implementations of these functions will need to call various internal -libusb functions, prefixed with "usbi_". Documentation for these functions +libusbx functions, prefixed with "usbi_". Documentation for these functions can be found in the .c files where they are implemented. You probably want to skim over *all* the documentation before starting your @@ -72,18 +72,18 @@ right usbi_backend for your platform. 4. Produce and test your implementation. -5. Send your implementation to libusb-devel mailing list. +5. Send your implementation to libusbx-devel mailing list. Implementation difficulties? Questions? ======================================= -If you encounter difficulties porting libusb to your platform, please raise -these issues on the libusb-devel mailing list. Where possible and sensible, I -am interested in solving problems preventing libusb from operating on other +If you encounter difficulties porting libusbx to your platform, please raise +these issues on the libusbx-devel mailing list. Where possible and sensible, I +am interested in solving problems preventing libusbx from operating on other platforms. -The libusb-devel mailing list is also a good place to ask questions and +The libusbx-devel mailing list is also a good place to ask questions and make suggestions about the internal API. Hopefully we can produce some better documentation based on your questions and other input. @@ -92,4 +92,3 @@ some infrastructure additions/modifications to better support your platform, you are encouraged to make such changes (in cleanly distinct patch submissions). Even if you do not make such changes yourself, please do raise the issues on the mailing list at the very minimum. - diff --git a/compat/libusb-1.0/README b/compat/libusb-1.0/README index 08ae1695..2ac89f9c 100644 --- a/compat/libusb-1.0/README +++ b/compat/libusb-1.0/README @@ -1,22 +1,29 @@ -libusb -====== +libusbx +======= -libusb is a library for USB device access from Linux, Mac OS X, -OpenBSD, NetBSD, and Windows userspace. -It is written in C and licensed under the LGPL-2.1 (see COPYING). +libusbx is a library for USB device access from Linux, Mac OS X, +Windows and OpenBSD/NetBSD userspace, with OpenBSD/NetBSD, and to a +lesser extent some of the newest features of Windows (such as libusbK +and libusb-win32 driver support) being EXPERIMENTAL. +It is written in C and licensed under the GNU Lesser General Public +License version 2.1 or, at your option, any later version (see COPYING). -libusb is abstracted internally in such a way that it can hopefully -be ported to other operating systems. See the PORTING file for some -information, if you fancy a challenge. :) +libusbx is abstracted internally in such a way that it can hopefully +be ported to other operating systems. Please see the PORTING file +for more information. -libusb homepage: -http://libusb.org/ +libusbx homepage: +http://libusbx.org/ Developers will wish to consult the API documentation: -http://libusb.sourceforge.net/api-1.0/ +http://api.libusbx.org Use the mailing list for questions, comments, etc: -http://libusb.org/wiki/MailingList +http://mailing-list.libusbx.org -- Peter Stuge -(use the mailing list rather than mailing developers directly) +- Pete Batard +- Hans de Goede +- Xiaofan Chen +- Ludovic Rousseau +- Nathan Hjelm +(Please use the mailing list rather than mailing developers directly) diff --git a/compat/libusb-1.0/THANKS b/compat/libusb-1.0/THANKS deleted file mode 100644 index d9261261..00000000 --- a/compat/libusb-1.0/THANKS +++ /dev/null @@ -1,8 +0,0 @@ -Development contributors are listed in the AUTHORS file. Other community -members who have made significant contributions in other areas are listed -in this file: - -Alan Stern -Ludovic Rousseau -Tim Roberts -Xiaofan Chen diff --git a/compat/libusb-1.0/TODO b/compat/libusb-1.0/TODO index 6c162a31..e64b2791 100644 --- a/compat/libusb-1.0/TODO +++ b/compat/libusb-1.0/TODO @@ -1,9 +1,2 @@ -for 1.1 or future -================== -optional timerfd support (runtime detection) -notifications of hotplugged/unplugged devices -offer API to create/destroy handle_events thread -isochronous sync I/O? -exposing of parent-child device relationships -"usb primer" introduction docs -more examples +Please see the libusbx roadmap by visiting: +https://github.com/libusbx/libusbx/issues/milestones?direction=asc&sort=due_date \ No newline at end of file diff --git a/compat/libusb-1.0/configure.ac b/compat/libusb-1.0/configure.ac index 1fccea70..bb924f16 100644 --- a/compat/libusb-1.0/configure.ac +++ b/compat/libusb-1.0/configure.ac @@ -8,25 +8,25 @@ m4_define([LU_DEFINE_VERSION_RC_ATOM], [^#define\s*$1\s*"\(-rc[0-9]*\)".*], [\1]))]) dnl The m4_bregexp() returns (only) the numbers following the #define named dnl in the first macro parameter. m4_define() then defines the name for use -dnl in AC_INIT(). +dnl in AC_INIT. LU_DEFINE_VERSION_ATOM([LIBUSB_MAJOR]) LU_DEFINE_VERSION_ATOM([LIBUSB_MINOR]) LU_DEFINE_VERSION_ATOM([LIBUSB_MICRO]) LU_DEFINE_VERSION_RC_ATOM([LIBUSB_RC]) -AC_INIT([libusb], LIBUSB_MAJOR[.]LIBUSB_MINOR[.]LIBUSB_MICRO[]LIBUSB_RC, [libusb-devel@lists.sourceforge.net], [libusb], [http://www.libusb.org/]) +AC_INIT([libusbx],[LIBUSB_MAJOR[.]LIBUSB_MINOR[.]LIBUSB_MICRO[]LIBUSB_RC],[libusbx-devel@lists.sourceforge.net],[libusbx],[http://libusbx.org]) # Library versioning # These numbers should be tweaked on every release. Read carefully: # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html # http://sourceware.org/autobook/autobook/autobook_91.html -lt_current="2" +lt_current="1" lt_revision="0" -lt_age="0" +lt_age="1" LTLDFLAGS="-version-info ${lt_current}:${lt_revision}:${lt_age}" -AM_INIT_AUTOMAKE([foreign subdir-objects]) +AM_INIT_AUTOMAKE AM_MAINTAINER_MODE AC_CONFIG_SRCDIR([libusb/core.c]) @@ -36,7 +36,7 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) AC_PREREQ([2.50]) AC_PROG_CC -AC_PROG_LIBTOOL +LT_INIT LT_LANG([Windows Resource]) AC_C_INLINE AM_PROG_CC_C_O @@ -45,26 +45,34 @@ AC_DEFINE([_GNU_SOURCE], 1, [Use GNU extensions]) LTLDFLAGS="${LTLDFLAGS} -no-undefined" AC_MSG_CHECKING([operating system]) + case $host in *-linux*) AC_MSG_RESULT([Linux]) backend="linux" + threads="posix" ;; *-darwin*) AC_MSG_RESULT([Darwin/Mac OS X]) backend="darwin" + threads="posix" ;; *-openbsd*) AC_MSG_RESULT([OpenBSD]) backend="openbsd" + threads="posix" ;; *-netbsd*) - AC_MSG_RESULT([NetBSD (using OpenBSD backend)]) - backend="openbsd" + AC_MSG_RESULT([NetBSD]) + backend="netbsd" + threads="posix" ;; *-mingw*) AC_MSG_RESULT([Windows]) backend="windows" + threads="windows" + create_import_lib="yes" + AM_CFLAGS="${AM_CFLAGS} -fno-omit-frame-pointer" ;; *-cygwin*) AC_MSG_RESULT([Cygwin (using Windows backend)]) @@ -74,24 +82,32 @@ case $host in *) AC_MSG_ERROR([unsupported operating system]) esac + case $backend in linux) AC_DEFINE(OS_LINUX, 1, [Linux backend]) AC_SUBST(OS_LINUX) - AC_CHECK_LIB(rt, clock_gettime, -pthread) + AC_SEARCH_LIBS(clock_gettime, rt, [], [], -pthread) AC_ARG_ENABLE([udev], - [AC_HELP_STRING([--with-udev], [use udev for device enumeration and hotplug support (recommended, default: yes)])], - [], [enable_udev="yes"]) - if test "x$enable_udev" = "xyes" ; then - # system has udev. use it or fail! - AC_CHECK_HEADERS([libudev.h],[],[AC_ERROR(["udev support requested but libudev not installed"])]) - AC_CHECK_LIB([udev], [udev_new], [], [AC_ERROR(["udev support requested but libudev not installed"])]) - AC_DEFINE(USE_UDEV, 1, [Use udev for device enumeration/hotplug]) - else - AC_CHECK_HEADERS([linux/netlink.h linux/filter.h], [], [AC_ERROR(["Linux netlink headers not found"])]) - fi - AC_SUBST(USE_UDEV) - threads="posix" + [AC_HELP_STRING([--enable-udev], [use udev for device enumeration and hotplug support (recommended) [default=yes]])], + [], [enable_udev="yes"]) + if test "x$enable_udev" = "xyes" ; then + # system has udev. use it or fail! + AC_CHECK_HEADERS([libudev.h],[],[AC_ERROR(["udev support requested but libudev not installed"])]) + AC_CHECK_LIB([udev], [udev_new], [], [AC_ERROR(["udev support requested but libudev not installed"])]) + AC_DEFINE(USE_UDEV, 1, [Use udev for device enumeration/hotplug]) + else + AC_CHECK_HEADERS([asm/types.h sys/socket.h], [], []) + AC_CHECK_HEADERS([linux/netlink.h linux/filter.h], [], [AC_ERROR(["Linux netlink headers not found"])], [ +#ifdef HAVE_ASM_TYPES_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +]) + fi + AC_SUBST(USE_UDEV) THREAD_CFLAGS="-pthread" LIBS="${LIBS} -pthread" AC_CHECK_HEADERS([poll.h]) @@ -100,7 +116,6 @@ linux) darwin) AC_DEFINE(OS_DARWIN, 1, [Darwin backend]) AC_SUBST(OS_DARWIN) - threads="posix" LIBS="-lobjc -Wl,-framework,IOKit -Wl,-framework,CoreFoundation" LTLDFLAGS="${LTLDFLAGS} -Wl,-prebind" AC_CHECK_HEADERS([poll.h]) @@ -112,7 +127,14 @@ darwin) openbsd) AC_DEFINE(OS_OPENBSD, 1, [OpenBSD backend]) AC_SUBST(OS_OPENBSD) - threads="posix" + THREAD_CFLAGS="-pthread" + LIBS="-pthread" + AC_CHECK_HEADERS([poll.h]) + AC_DEFINE([POLL_NFDS_TYPE],[nfds_t],[type of second poll() argument]) + ;; +netbsd) + AC_DEFINE(OS_NETBSD, 1, [NetBSD backend]) + AC_SUBST(OS_NETBSD) THREAD_CFLAGS="-pthread" LIBS="-pthread" AC_CHECK_HEADERS([poll.h]) @@ -126,13 +148,16 @@ windows) AC_DEFINE([POLL_NFDS_TYPE],[unsigned int],[type of second poll() argument]) ;; esac + AC_SUBST(LIBS) AM_CONDITIONAL(OS_LINUX, test "x$backend" = xlinux) AM_CONDITIONAL(OS_DARWIN, test "x$backend" = xdarwin) AM_CONDITIONAL(OS_OPENBSD, test "x$backend" = xopenbsd) +AM_CONDITIONAL(OS_NETBSD, test "x$backend" = xnetbsd) AM_CONDITIONAL(OS_WINDOWS, test "x$backend" = xwindows) AM_CONDITIONAL(THREADS_POSIX, test "x$threads" = xposix) +AM_CONDITIONAL(CREATE_IMPORT_LIB, test "x$create_import_lib" = "xyes") AM_CONDITIONAL(USE_UDEV, test "x$enable_udev" = xyes) if test "$threads" = posix; then AC_DEFINE(THREADS_POSIX, 1, [Use POSIX Threads]) @@ -142,7 +167,7 @@ fi AC_CHECK_HEADER([sys/timerfd.h], [timerfd_h=1], [timerfd_h=0]) AC_ARG_ENABLE([timerfd], [AS_HELP_STRING([--enable-timerfd], - [use timerfd for timing (default auto)])], + [use timerfd for timing [default=auto]])], [use_timerfd=$enableval], [use_timerfd='auto']) if test "x$use_timerfd" = "xyes" -a "x$timerfd_h" = "x0"; then @@ -177,20 +202,42 @@ if test "x$log_enabled" != "xno"; then fi AC_ARG_ENABLE([debug-log], [AS_HELP_STRING([--enable-debug-log], - [enable debug logging (default n)])], + [start with debug message logging enabled [default=no]])], [debug_log_enabled=$enableval], [debug_log_enabled='no']) if test "x$debug_log_enabled" != "xno"; then - AC_DEFINE([ENABLE_DEBUG_LOGGING], 1, [Debug message logging]) + AC_DEFINE([ENABLE_DEBUG_LOGGING], 1, [Start with debug message logging enabled]) +fi + +AC_ARG_ENABLE([system-log], [AS_HELP_STRING([--enable-system-log], + [output logging messages to system wide log, if supported by the OS [default=no]])], + [system_log_enabled=$enableval], + [system_log_enabled='no']) +if test "x$system_log_enabled" != "xno"; then + AC_DEFINE([USE_SYSTEM_LOGGING_FACILITY], 1, [Enable output to system log]) +fi + +# Check if syslog is available in standard C library +AC_CHECK_HEADERS(syslog.h) +AC_CHECK_FUNC([syslog], [have_syslog=yes], [have_syslog=no]) +if test "x$have_syslog" != "xno"; then + AC_DEFINE([HAVE_SYSLOG_FUNC], 1, [syslog() function available]) fi # Examples build AC_ARG_ENABLE([examples-build], [AS_HELP_STRING([--enable-examples-build], - [build example applications (default n)])], + [build example applications [default=no]])], [build_examples=$enableval], [build_examples='no']) AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$build_examples" != "xno"]) +# Tests build +AC_ARG_ENABLE([tests-build], [AS_HELP_STRING([--enable-tests-build], + [build test applications [default=no]])], + [build_tests=$enableval], + [build_tests='no']) +AM_CONDITIONAL([BUILD_TESTS], [test "x$build_tests" != "xno"]) + # check for -fvisibility=hidden compiler support (GCC >= 3.4) saved_cflags="$CFLAGS" # -Werror required for cygwin @@ -217,8 +264,9 @@ 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]) AC_CHECK_FUNCS(gettimeofday) +AC_CHECK_HEADERS([signal.h]) -AM_CFLAGS="-std=gnu99 -Wall -Wundef -Wunused -Wstrict-prototypes -Werror-implicit-function-declaration $nopointersign_cflags -Wshadow ${THREAD_CFLAGS} ${VISIBILITY_CFLAGS}" +AM_CFLAGS="${AM_CFLAGS} -std=gnu99 -Wall -Wundef -Wunused -Wstrict-prototypes -Werror-implicit-function-declaration $nopointersign_cflags -Wshadow ${THREAD_CFLAGS} ${VISIBILITY_CFLAGS}" AC_SUBST(AM_CFLAGS) AC_SUBST(LTLDFLAGS) @@ -226,4 +274,8 @@ AC_SUBST(LTLDFLAGS) AC_CONFIG_FILES([libusb-1.0.pc]) AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([libusb/Makefile]) +AC_CONFIG_FILES([examples/Makefile]) +AC_CONFIG_FILES([tests/Makefile]) +AC_CONFIG_FILES([doc/Makefile]) +AC_CONFIG_FILES([doc/doxygen.cfg]) AC_OUTPUT diff --git a/compat/libusb-1.0/doc/Makefile.am b/compat/libusb-1.0/doc/Makefile.am index 931a7c0f..544a281a 100644 --- a/compat/libusb-1.0/doc/Makefile.am +++ b/compat/libusb-1.0/doc/Makefile.am @@ -5,6 +5,5 @@ docs: doxygen.cfg docs-upload: docs ln -s html api-1.0 - rsync -av api-1.0/ web.sourceforge.net:htdocs/api-1.0/ + scp -r api-1.0 pbatard@web.sourceforge.net:/home/project-web/libusbx/htdocs rm -f api-1.0 - diff --git a/compat/libusb-1.0/doc/doxygen.cfg.in b/compat/libusb-1.0/doc/doxygen.cfg.in index 00452204..05f984ad 100644 --- a/compat/libusb-1.0/doc/doxygen.cfg.in +++ b/compat/libusb-1.0/doc/doxygen.cfg.in @@ -25,13 +25,26 @@ DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. -PROJECT_NAME = libusb +PROJECT_NAME = # 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 = +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 = "eXpand your USB potential" + +# With the PROJECT_LOGO tag one can specify an logo or 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 = libusbx.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. @@ -154,13 +167,6 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO -# If the DETAILS_AT_TOP tag is set to YES then Doxygen -# will output the detailed description near the top, like JavaDoc. -# If set to NO, the detailed description appears after the member -# documentation. - -DETAILS_AT_TOP = NO - # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. @@ -401,12 +407,6 @@ MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - # 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 @@ -424,7 +424,7 @@ FILE_VERSION_FILTER = # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. -QUIET = NO +QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank @@ -701,12 +701,6 @@ HTML_FOOTER = HTML_STYLESHEET = -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) diff --git a/compat/libusb-1.0/examples/Makefile.am b/compat/libusb-1.0/examples/Makefile.am index 037b886a..80819274 100644 --- a/compat/libusb-1.0/examples/Makefile.am +++ b/compat/libusb-1.0/examples/Makefile.am @@ -1,6 +1,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/libusb LDADD = ../libusb/libusb-1.0.la -noinst_PROGRAMS = listdevs hotplugtest testlibusb1 + +noinst_PROGRAMS = listdevs xusb fxload hotplugtest if HAVE_SIGACTION noinst_PROGRAMS += dpfp @@ -13,3 +14,6 @@ endif sam3u_benchmark_SOURCES = sam3u_benchmark.c noinst_PROGRAMS += sam3u_benchmark endif + +fxload_SOURCES = ezusb.c ezusb.h fxload.c +fxload_CFLAGS = $(THREAD_CFLAGS) $(AM_CFLAGS) diff --git a/compat/libusb-1.0/examples/dpfp.c b/compat/libusb-1.0/examples/dpfp.c index ecd5a923..3f41e0e7 100644 --- a/compat/libusb-1.0/examples/dpfp.c +++ b/compat/libusb-1.0/examples/dpfp.c @@ -1,6 +1,6 @@ /* - * libusb example program to manipulate U.are.U 4000B fingerprint scanner. - * Copyright (C) 2007 Daniel Drake + * libusbx example program to manipulate U.are.U 4000B fingerprint scanner. + * Copyright © 2007 Daniel Drake * * Basic image capture program only, does not consider the powerup quirks or * the fact that image encryption may be enabled. Not expected to work @@ -27,7 +27,7 @@ #include #include -#include +#include "libusb.h" #define EP_INTR (1 | LIBUSB_ENDPOINT_IN) #define EP_DATA (2 | LIBUSB_ENDPOINT_IN) @@ -164,7 +164,7 @@ static void LIBUSB_CALL cb_mode_changed(struct libusb_transfer *transfer) static int set_mode_async(unsigned char data) { - unsigned char *buf = malloc(LIBUSB_CONTROL_SETUP_SIZE + 1); + unsigned char *buf = (unsigned char*) malloc(LIBUSB_CONTROL_SETUP_SIZE + 1); struct libusb_transfer *transfer; if (!buf) @@ -504,4 +504,3 @@ out: libusb_exit(NULL); return r >= 0 ? r : -r; } - diff --git a/compat/libusb-1.0/examples/dpfp_threaded.c b/compat/libusb-1.0/examples/dpfp_threaded.c index 93de9d75..6970dac6 100644 --- a/compat/libusb-1.0/examples/dpfp_threaded.c +++ b/compat/libusb-1.0/examples/dpfp_threaded.c @@ -1,6 +1,6 @@ /* - * libusb example program to manipulate U.are.U 4000B fingerprint scanner. - * Copyright (C) 2007 Daniel Drake + * libusbx example program to manipulate U.are.U 4000B fingerprint scanner. + * Copyright © 2007 Daniel Drake * * Basic image capture program only, does not consider the powerup quirks or * the fact that image encryption may be enabled. Not expected to work @@ -28,7 +28,7 @@ #include #include -#include +#include "libusb.h" #define EP_INTR (1 | LIBUSB_ENDPOINT_IN) #define EP_DATA (2 | LIBUSB_ENDPOINT_IN) @@ -193,7 +193,7 @@ static void LIBUSB_CALL cb_mode_changed(struct libusb_transfer *transfer) static int set_mode_async(unsigned char data) { - unsigned char *buf = malloc(LIBUSB_CONTROL_SETUP_SIZE + 1); + unsigned char *buf = (unsigned char*) malloc(LIBUSB_CONTROL_SETUP_SIZE + 1); struct libusb_transfer *transfer; if (!buf) @@ -542,4 +542,3 @@ out: libusb_exit(NULL); return r >= 0 ? r : -r; } - diff --git a/compat/libusb-1.0/examples/hotplugtest.c b/compat/libusb-1.0/examples/hotplugtest.c index c804aea1..fef01aff 100644 --- a/compat/libusb-1.0/examples/hotplugtest.c +++ b/compat/libusb-1.0/examples/hotplugtest.c @@ -1,6 +1,7 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ /* * libusb example program for hotplug API - * Copyright (C) 2012-2013 Nathan Hjelm + * Copyright © 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 @@ -19,77 +20,78 @@ #include #include -#include -#include -#include +#include "libusb.h" int done = 0; libusb_device_handle *handle; -static int hotplug_callback (libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data) { - struct libusb_device_descriptor desc; - int rc; +static int LIBUSB_CALL hotplug_callback(libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data) +{ + struct libusb_device_descriptor desc; + int rc; - rc = libusb_get_device_descriptor(dev, &desc); - if (LIBUSB_SUCCESS != rc) { - fprintf (stderr, "Error getting device descriptor\n"); - } + rc = libusb_get_device_descriptor(dev, &desc); + if (LIBUSB_SUCCESS != rc) { + fprintf (stderr, "Error getting device descriptor\n"); + } - printf ("Device attach: %04x:%04x\n", desc.idVendor, desc.idProduct); + printf ("Device attached: %04x:%04x\n", desc.idVendor, desc.idProduct); - libusb_open (dev, &handle); + libusb_open (dev, &handle); - done++; + done++; - return 0; + return 0; } -static int hotplug_callback_detach (libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data) { - printf ("Device detached\n"); +static int LIBUSB_CALL hotplug_callback_detach(libusb_context *ctx, libusb_device *dev, libusb_hotplug_event event, void *user_data) +{ + printf ("Device detached\n"); - libusb_close (handle); + libusb_close (handle); - done++; - return 0; + done++; + return 0; } -int main (int argc, char *argv[]) { - libusb_hotplug_callback_handle hp[2]; - int product_id, vendor_id, class_id; - int rc; - - vendor_id = (argc > 1) ? strtol (argv[1], NULL, 0) : 0x045a; - product_id = (argc > 2) ? strtol (argv[2], NULL, 0) : 0x5005; - class_id = (argc > 3) ? strtol (argv[3], NULL, 0) : LIBUSB_HOTPLUG_MATCH_ANY; - - libusb_init (NULL); - - if (!libusb_has_capability (LIBUSB_CAP_HAS_HOTPLUG)) { - printf ("Hotplug not supported by this build of libusb\n"); - libusb_exit (NULL); - return EXIT_FAILURE; - } - - rc = libusb_hotplug_register_callback (NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, 0, vendor_id, - product_id, class_id, hotplug_callback, NULL, &hp[0]); - if (LIBUSB_SUCCESS != rc) { - fprintf (stderr, "Error registering callback 0\n"); - libusb_exit (NULL); - return EXIT_FAILURE; - } - - rc = libusb_hotplug_register_callback (NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, vendor_id, - product_id,class_id, hotplug_callback_detach, NULL, &hp[1]); - if (LIBUSB_SUCCESS != rc) { - fprintf (stderr, "Error registering callback 1\n"); - libusb_exit (NULL); - return EXIT_FAILURE; - } - - while (done < 2) { - libusb_handle_events (NULL); - } - - libusb_exit (NULL); +int main(int argc, char *argv[]) +{ + libusb_hotplug_callback_handle hp[2]; + int product_id, vendor_id, class_id; + int rc; + + vendor_id = (argc > 1) ? strtol (argv[1], NULL, 0) : 0x045a; + product_id = (argc > 2) ? strtol (argv[2], NULL, 0) : 0x5005; + class_id = (argc > 3) ? strtol (argv[3], NULL, 0) : LIBUSB_HOTPLUG_MATCH_ANY; + + libusb_init (NULL); + + if (!libusb_has_capability (LIBUSB_CAP_HAS_HOTPLUG)) { + printf ("Hotplug capabilites are not supported on this platform\n"); + libusb_exit (NULL); + return EXIT_FAILURE; + } + + rc = libusb_hotplug_register_callback (NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, 0, vendor_id, + product_id, class_id, hotplug_callback, NULL, &hp[0]); + if (LIBUSB_SUCCESS != rc) { + fprintf (stderr, "Error registering callback 0\n"); + libusb_exit (NULL); + return EXIT_FAILURE; + } + + rc = libusb_hotplug_register_callback (NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, vendor_id, + product_id,class_id, hotplug_callback_detach, NULL, &hp[1]); + if (LIBUSB_SUCCESS != rc) { + fprintf (stderr, "Error registering callback 1\n"); + libusb_exit (NULL); + return EXIT_FAILURE; + } + + while (done < 2) { + libusb_handle_events (NULL); + } + + libusb_exit (NULL); } diff --git a/compat/libusb-1.0/examples/listdevs.c b/compat/libusb-1.0/examples/listdevs.c index 6ab89172..31ad26ea 100644 --- a/compat/libusb-1.0/examples/listdevs.c +++ b/compat/libusb-1.0/examples/listdevs.c @@ -1,6 +1,6 @@ /* - * libusb example program to list devices on the bus - * Copyright (C) 2007 Daniel Drake + * libusbx example program to list devices on the bus + * Copyright © 2007 Daniel Drake * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,14 +18,14 @@ */ #include -#include -#include +#include "libusb.h" static void print_devs(libusb_device **devs) { libusb_device *dev; - int i = 0; + int i = 0, j = 0; + uint8_t path[8]; while ((dev = devs[i++]) != NULL) { struct libusb_device_descriptor desc; @@ -35,9 +35,17 @@ static void print_devs(libusb_device **devs) return; } - printf("%04x:%04x (bus %d, device %d)\n", + printf("%04x:%04x (bus %d, device %d)", desc.idVendor, desc.idProduct, libusb_get_bus_number(dev), libusb_get_device_address(dev)); + + r = libusb_get_port_numbers(dev, path, sizeof(path)); + if (r > 0) { + printf(" path: %d", path[0]); + for (j = 1; j < r; j++) + printf(".%d", path[j]); + } + printf("\n"); } } @@ -61,4 +69,3 @@ int main(void) libusb_exit(NULL); return 0; } - diff --git a/compat/libusb-1.0/examples/sam3u_benchmark.c b/compat/libusb-1.0/examples/sam3u_benchmark.c index 6a1f5eeb..99d6b0f1 100644 --- a/compat/libusb-1.0/examples/sam3u_benchmark.c +++ b/compat/libusb-1.0/examples/sam3u_benchmark.c @@ -40,7 +40,7 @@ static struct libusb_device_handle *devh = NULL; static unsigned long num_bytes = 0, num_xfer = 0; static struct timeval tv_start; -static void cb_xfr(struct libusb_transfer *xfr) +static void LIBUSB_CALL cb_xfr(struct libusb_transfer *xfr) { unsigned int i; diff --git a/compat/libusb-1.0/examples/testlibusb1.c b/compat/libusb-1.0/examples/testlibusb1.c deleted file mode 100644 index ac95771d..00000000 --- a/compat/libusb-1.0/examples/testlibusb1.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Test suite program based of libusb-0.1-compat testlibusb - * 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 -#include -#include - -int verbose = 0; - -static void print_endpoint_comp(const struct libusb_ss_endpoint_companion_descriptor *ep_comp) -{ - printf(" USB 3.0 Endpoint Companion:\n"); - printf(" bMaxBurst: %d\n", ep_comp->bMaxBurst); - printf(" bmAttributes: 0x%02x\n", ep_comp->bmAttributes); - printf(" wBytesPerInterval: %d\n", ep_comp->wBytesPerInterval); -} - -static void print_endpoint(const struct libusb_endpoint_descriptor *endpoint) -{ - int i, ret; - - printf(" Endpoint:\n"); - printf(" bEndpointAddress: %02xh\n", endpoint->bEndpointAddress); - printf(" bmAttributes: %02xh\n", endpoint->bmAttributes); - printf(" wMaxPacketSize: %d\n", endpoint->wMaxPacketSize); - printf(" bInterval: %d\n", endpoint->bInterval); - printf(" bRefresh: %d\n", endpoint->bRefresh); - printf(" bSynchAddress: %d\n", endpoint->bSynchAddress); - - for (i = 0 ; i < endpoint->extra_length ; ) { - if (LIBUSB_DT_SS_ENDPOINT_COMPANION == endpoint->extra[i+1]) { - struct libusb_ss_endpoint_companion_descriptor *ep_comp; - - ret = libusb_parse_ss_endpoint_comp(endpoint->extra+i, endpoint->extra[0], &ep_comp); - if (LIBUSB_SUCCESS != ret) { - continue; - } - - print_endpoint_comp(ep_comp); - - libusb_free_ss_endpoint_comp(ep_comp); - } - - i += endpoint->extra[i]; - } -} - -static void print_altsetting(const struct libusb_interface_descriptor *interface) -{ - int i; - - printf(" Interface:\n"); - printf(" bInterfaceNumber: %d\n", interface->bInterfaceNumber); - printf(" bAlternateSetting: %d\n", interface->bAlternateSetting); - printf(" bNumEndpoints: %d\n", interface->bNumEndpoints); - printf(" bInterfaceClass: %d\n", interface->bInterfaceClass); - printf(" bInterfaceSubClass: %d\n", interface->bInterfaceSubClass); - printf(" bInterfaceProtocol: %d\n", interface->bInterfaceProtocol); - printf(" iInterface: %d\n", interface->iInterface); - - for (i = 0; i < interface->bNumEndpoints; i++) - print_endpoint(&interface->endpoint[i]); -} - -static void print_2_0_ext_cap(struct libusb_usb_2_0_device_capability_descriptor *usb_2_0_ext_cap) -{ - printf(" USB 2.0 Extension Capabilities:\n"); - printf(" bDevCapabilityType: %d\n", usb_2_0_ext_cap->bDevCapabilityType); - printf(" bmAttributes: 0x%x\n", usb_2_0_ext_cap->bmAttributes); -} - -static void print_ss_usb_cap(struct libusb_ss_usb_device_capability_descriptor *ss_usb_cap) -{ - printf(" USB 3.0 Capabilities:\n"); - printf(" bDevCapabilityType: %d\n", ss_usb_cap->bDevCapabilityType); - printf(" bmAttributes: 0x%x\n", ss_usb_cap->bmAttributes); - printf(" wSpeedSupported: 0x%x\n", ss_usb_cap->wSpeedSupported); - printf(" bFunctionalitySupport: %d\n", ss_usb_cap->bFunctionalitySupport); - printf(" bU1devExitLat: %d\n", ss_usb_cap->bU1DevExitLat); - printf(" bU2devExitLat: %d\n", ss_usb_cap->bU2DevExitLat); -} - -static void print_bos(libusb_device_handle *handle) -{ - unsigned char buffer[128]; - struct libusb_bos_descriptor *bos; - int ret; - - ret = libusb_get_descriptor(handle, LIBUSB_DT_BOS, 0, buffer, 128); - if (0 > ret) { - return; - } - - ret = libusb_parse_bos_descriptor(buffer, 128, &bos); - if (0 > ret) { - return; - } - - printf(" Binary Object Store (BOS):\n"); - printf(" wTotalLength: %d\n", bos->wTotalLength); - printf(" bNumDeviceCaps: %d\n", bos->bNumDeviceCaps); - if (bos->usb_2_0_ext_cap) { - print_2_0_ext_cap(bos->usb_2_0_ext_cap); - } - - if (bos->ss_usb_cap) { - print_ss_usb_cap(bos->ss_usb_cap); - } -} - -static void print_interface(const struct libusb_interface *interface) -{ - int i; - - for (i = 0; i < interface->num_altsetting; i++) - print_altsetting(&interface->altsetting[i]); -} - -static void print_configuration(struct libusb_config_descriptor *config) -{ - int i; - - printf(" Configuration:\n"); - printf(" wTotalLength: %d\n", config->wTotalLength); - printf(" bNumInterfaces: %d\n", config->bNumInterfaces); - printf(" bConfigurationValue: %d\n", config->bConfigurationValue); - printf(" iConfiguration: %d\n", config->iConfiguration); - printf(" bmAttributes: %02xh\n", config->bmAttributes); - printf(" MaxPower: %d\n", config->MaxPower); - - for (i = 0; i < config->bNumInterfaces; i++) - print_interface(&config->interface[i]); -} - -static int print_device(libusb_device *dev, int level) -{ - struct libusb_device_descriptor desc; - libusb_device_handle *handle = NULL; - char description[256]; - char string[256]; - int ret, i; - - ret = libusb_get_device_descriptor(dev, &desc); - if (ret < 0) { - fprintf(stderr, "failed to get device descriptor"); - return -1; - } - - ret = libusb_open(dev, &handle); - if (LIBUSB_SUCCESS == ret) { - if (desc.iManufacturer) { - ret = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, string, sizeof(string)); - if (ret > 0) - snprintf(description, sizeof(description), "%s - ", string); - else - snprintf(description, sizeof(description), "%04X - ", - desc.idVendor); - } else - snprintf(description, sizeof(description), "%04X - ", - desc.idVendor); - - if (desc.iProduct) { - ret = libusb_get_string_descriptor_ascii(handle, desc.iProduct, string, sizeof(string)); - if (ret > 0) - snprintf(description + strlen(description), sizeof(description) - - strlen(description), "%s", string); - else - snprintf(description + strlen(description), sizeof(description) - - strlen(description), "%04X", desc.idProduct); - } else - snprintf(description + strlen(description), sizeof(description) - - strlen(description), "%04X", desc.idProduct); - } else { - snprintf(description, sizeof(description), "%04X - %04X", - desc.idVendor, desc.idProduct); - } - - printf("%.*sDev (bus %d, device %d): %s\n", level * 2, " ", - libusb_get_bus_number(dev), libusb_get_device_address(dev), description); - - if (handle && verbose) { - if (desc.iSerialNumber) { - ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, string, sizeof(string)); - if (ret > 0) - printf("%.*s - Serial Number: %s\n", level * 2, - " ", string); - } - } - - if (verbose) { - for (i = 0; i < desc.bNumConfigurations; i++) { - struct libusb_config_descriptor *config; - ret = libusb_get_config_descriptor(dev, i, &config); - if (LIBUSB_SUCCESS != ret) { - printf(" Couldn't retrieve descriptors\n"); - continue; - } - - print_configuration(config); - - libusb_free_config_descriptor(config); - } - - if (handle && desc.bcdUSB >= 0x0201) { - print_bos(handle); - } - } - - if (handle) - libusb_close(handle); - - return 0; -} - -int main(int argc, char *argv[]) -{ - libusb_device **devs; - ssize_t cnt; - int r, i; - - if (argc > 1 && !strcmp(argv[1], "-v")) - verbose = 1; - - r = libusb_init(NULL); - if (r < 0) - return r; - - cnt = libusb_get_device_list(NULL, &devs); - if (cnt < 0) - return (int) cnt; - - for (i = 0 ; devs[i] ; ++i) { - print_device(devs[i], 0); - } - - libusb_free_device_list(devs, 1); - - libusb_exit(NULL); - return 0; -} diff --git a/compat/libusb-1.0/libusb-1.0.pc.in b/compat/libusb-1.0/libusb-1.0.pc.in index bb371d10..7bc33c12 100644 --- a/compat/libusb-1.0/libusb-1.0.pc.in +++ b/compat/libusb-1.0/libusb-1.0.pc.in @@ -3,10 +3,9 @@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ -Name: libusb-1.0 -Description: C API for USB device access from Linux, Mac OS X, OpenBSD, NetBSD and Windows userspace +Name: libusbx-1.0 +Description: C API for USB device access from Linux, Mac OS X, Windows and OpenBSD/NetBSD userspace Version: @VERSION@ Libs: -L${libdir} -lusb-1.0 Libs.private: @LIBS@ Cflags: -I${includedir}/libusb-1.0 - diff --git a/compat/libusb-1.0/libusb/Makefile.am b/compat/libusb-1.0/libusb/Makefile.am index 2f3f95f8..587661a2 100644 --- a/compat/libusb-1.0/libusb/Makefile.am +++ b/compat/libusb-1.0/libusb/Makefile.am @@ -1,32 +1,46 @@ +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 -WINDOWS_USB_SRC = os/poll_windows.c os/windows_usb.c libusb-1.0.rc \ - libusb-1.0.def +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) \ - $(WINDOWS_USB_SRC) os/threads_posix.c os/threads_windows.c \ + $(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) os/linux_udev.c +OS_SRC = $(LINUX_USBFS_SRC) $(POSIX_POLL_SRC) \ + os/linux_udev.c else -OS_SRC = $(LINUX_USBFS_SRC) os/linux_netlink.c +OS_SRC = $(LINUX_USBFS_SRC) $(POSIX_POLL_SRC) \ + os/linux_netlink.c endif endif if OS_DARWIN -OS_SRC = $(DARWIN_USB_SRC) +OS_SRC = $(DARWIN_USB_SRC) $(POSIX_POLL_SRC) AM_CFLAGS_EXT = -no-cpp-precomp endif if OS_OPENBSD -OS_SRC = $(OPENBSD_USB_SRC) +OS_SRC = $(OPENBSD_USB_SRC) $(POSIX_POLL_SRC) +endif + +if OS_NETBSD +OS_SRC = $(NETBSD_USB_SRC) $(POSIX_POLL_SRC) endif if OS_WINDOWS @@ -35,7 +49,13 @@ 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 +libusb-1.0.rc: version.h version_nano.h +endif + +libusb-1.0.dll: libusb-1.0.def +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 @@ -44,12 +64,12 @@ else THREADS_SRC = os/threads_windows.h os/threads_windows.c endif -libusb_1_0_la_CFLAGS = $(AM_CFLAGS) \ - -DLIBUSB_DESCRIBE=\"`git --git-dir "$(top_srcdir)/.git" describe --tags 2>/dev/null`\" +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 sync.c $(OS_SRC) \ - hotplug.h hotplug.c os/linux_usbfs.h os/darwin_usb.h os/windows_usb.h \ - $(THREADS_SRC) os/poll_posix.h os/poll_windows.h +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/compat/libusb-1.0/libusb/core.c b/compat/libusb-1.0/libusb/core.c index ed8c3268..e8162846 100644 --- a/compat/libusb-1.0/libusb/core.c +++ b/compat/libusb-1.0/libusb/core.c @@ -1,9 +1,9 @@ -/* -*- Mode: C; indent-tabs-mode:nil ; c-basic-offset:8 -*- */ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ /* - * Core functions for libusb - * Copyright (c) 2012-2013 Nathan Hjelm - * Copyright (C) 2007-2008 Daniel Drake - * Copyright (c) 2001 Johannes Erdfelt + * Core functions for libusbx + * 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 @@ -20,18 +20,26 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#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" @@ -42,43 +50,45 @@ const struct usbi_os_backend * const usbi_backend = &linux_usbfs_backend; 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 -const struct libusb_version libusb_version_internal = { - LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO, LIBUSB_RC, - LIBUSB_DESCRIBE -}; - 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://libusbx.org" }; 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 + * \mainpage libusbx-1.0 API Reference * * \section intro Introduction * - * libusb is an open source library that allows you to communicate with USB + * libusbx is an open source library that allows you to communicate with USB * devices from userspace. For more info, see the - * libusb homepage. + * libusbx 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. + * libusbx-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 2.0 + * 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. * @@ -92,68 +102,71 @@ struct list_head active_contexts_list; * 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 (see \ref hotplug) + * - 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. + * links to the different categories of libusbx'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. + * Some example programs can be found in the libusbx source distribution under + * the "examples" subdirectory. The libusbx homepage includes a list of + * real-life project examples which use libusbx. * * \section errorhandling Error handling * - * libusb functions typically return 0 on success or a negative error code + * libusbx 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 does not log any messages by default. Your application is therefore - * free to close stdout/stderr and those descriptors may be reused without - * worry. + * libusbx 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 stdout/stderr logging - * of certain messages. Under standard configuration, libusb doesn't really - * log much at all, so you are advised to use this function to enable all - * error/warning/informational messages. It will help you debug problems with - * your software. + * The libusb_set_debug() function can be used to enable logging of certain + * messages. Under standard configuration, libusbx 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 + * libusbx 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 suitable for being passed to your application user; - * instead, you should interpret the error codes returned from libusb functions + * These messages are not intended to being passed to your application user; + * instead, you should interpret the error codes returned from libusbx 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 + * getting a strange error code from a libusbx 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 number, which is - * interpreted the same as the libusb_set_debug() parameter. When this + * 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 + * libusbx 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. 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. + * libusbx 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 + * libusbx does have imperfections. The \ref caveats "caveats" page attempts * to document these. */ @@ -168,7 +181,7 @@ struct list_head active_contexts_list; * 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 + * is working with, at any time. libusbx 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. * @@ -193,7 +206,7 @@ struct list_head active_contexts_list; * * \section configsel Configuration selection and handling * - * When libusb presents a device handle to an application, there is a chance + * When libusbx 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. @@ -204,13 +217,13 @@ struct list_head active_contexts_list; * -# 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 + * -# libusbx 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 + * -# In the case where the desired configuration is already active, libusbx * 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 + * the fingerprint reader interface through libusbx, 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 @@ -250,50 +263,23 @@ if (cfg != desired) * 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 + * any one packet than the transfer buffer allows for) then libusbx 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. Prior to - * libusb v1.0.2, this information was lost (and for device-to-host transfers, - * the corresponding data was discarded). As of libusb v1.0.3, 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. - * - * A previous workaround was to only ever submit transfers of size 16kb or - * less. - * - * As of libusb v1.0.4 and Linux v2.6.32, this is fixed. A technical - * explanation of this issue follows. - * - * When you ask libusb to submit a bulk transfer larger than 16kb in size, - * libusb breaks it up into a number of smaller subtransfers. This is because - * the usbfs kernel interface only accepts transfers of up to 16kb in size. - * The subtransfers are submitted all at once so that the kernel can queue - * them at the hardware level, therefore maximizing bus throughput. - * - * On legacy platforms, this caused problems when transfers completed early. - * Upon this event, the kernel would terminate all further packets in that - * subtransfer (but not any following ones). libusb would note this event and - * immediately cancel any following subtransfers that had been queued, - * but often libusb was not fast enough, and the following subtransfers had - * started before libusb got around to cancelling them. - * - * Thanks to an API extension to usbfs, this is fixed with recent kernel and - * libusb releases. The solution was to allow libusb to communicate to the - * kernel where boundaries occur between logical libusb-level transfers. When - * a short transfer (or other error) occurs, the kernel will cancel all the - * subtransfers until the boundary without allowing those transfers to start. + * On legacy platforms, libusbx is unable to do this in all situations. After + * the incomplete packet occurs, "surplus" data may be transferred. For recent + * versions of libusbx, 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. On Linux, this did not work with - * libusb versions prior to 1.0.3 and kernel versions prior to 2.6.31. + * - libusbx 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. */ @@ -301,24 +287,24 @@ if (cfg != desired) /** * \page contexts Contexts * - * It is possible that libusb may be used simultaneously from two independent + * It is possible that libusbx 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. + * developed modules may both use libusbx. * - * libusb is written to allow for these multiple user scenarios. The two - * "instances" of libusb will not interfere: libusb_set_debug() calls + * libusbx is written to allow for these multiple user scenarios. The two + * "instances" of libusbx 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. + * users can continue using libusbx after one of them calls libusb_exit(), etc. * - * This is made possible through libusb's context concept. When you + * This is made possible through libusbx'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. + * this context pointer back into future libusbx 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). + * you're sure no other code will attempt to use libusbx 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(). @@ -331,17 +317,17 @@ if (cfg != desired) * 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 + * You may be wondering why only a subset of libusbx functions require a + * context pointer in their function definition. Internally, libusbx 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. + * This page details how to initialize and deinitialize libusbx. Initialization + * must be performed before using any libusbx functionality, and similarly you + * must not call any libusbx functions after deinitialization. */ /** @@ -398,7 +384,7 @@ libusb_free_device_list(list, 1); * device. * * \section devshandles Devices and device handles - * libusb has a concept of a USB device, represented by the + * libusbx 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. @@ -414,8 +400,8 @@ libusb_free_device_list(list, 1); * 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 + * libusbx to open the device using the libusb_open() function. Assuming + * success, libusbx 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. * @@ -423,10 +409,10 @@ libusb_free_device_list(list, 1); * * 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 + * you are done with it. libusbx 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: + * To handle these issues, libusbx provides you with two separate items: * - A function to free the list itself * - A reference counting system for the devices inside * @@ -494,7 +480,7 @@ struct discovered_devs *discovered_devs_append( /* exceeded capacity, need to grow */ usbi_dbg("need to increase capacity"); capacity = discdevs->capacity + DISCOVERED_DEVICES_SIZE_STEP; - discdevs = realloc(discdevs, + discdevs = usbi_reallocf(discdevs, sizeof(*discdevs) + (sizeof(void *) * capacity)); if (discdevs) { discdevs->capacity = capacity; @@ -537,7 +523,6 @@ struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, dev->refcnt = 1; dev->session_data = session_id; dev->speed = LIBUSB_SPEED_UNKNOWN; - memset(&dev->os_priv, 0, priv_size); if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { usbi_connect_device (dev); @@ -548,50 +533,56 @@ struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, void usbi_connect_device(struct libusb_device *dev) { - libusb_hotplug_message message = {LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, dev}; - int ret; + 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. */ + /* 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"); - } - } + 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 = {LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, dev}; + libusb_hotplug_message message; struct libusb_context *ctx = dev->ctx; - int ret; + 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); - /* 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"); - } - } - 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 @@ -617,7 +608,7 @@ int usbi_sanitize_device(struct libusb_device *dev) return 0; } -/* Examine libusb's internal list of known devices, looking for one with +/* Examine libusbx'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, @@ -654,7 +645,7 @@ struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx, * \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 + * \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, @@ -674,6 +665,9 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx, /* 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); @@ -696,7 +690,7 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx, /* convert discovered_devs into a list */ len = discdevs->len; - ret = malloc(sizeof(void *) * (len + 1)); + ret = calloc(len + 1, sizeof(struct libusb_device *)); if (!ret) { len = LIBUSB_ERROR_NO_MEM; goto out; @@ -747,6 +741,87 @@ 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 LIBUSBX_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; + + while(dev) { + // HCDs can be listed as devices and would have port #0 + // TODO: see how the other backends want to implement HCDs as parents + if (dev->port_number == 0) + break; + i--; + if (i < 0) { + usbi_warn(DEVICE_CTX(dev), + "port numbers array too small"); + return LIBUSB_ERROR_OVERFLOW; + } + port_numbers[i] = dev->port_number; + dev = dev->parent_dev; + } + 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 libusbx 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 @@ -836,7 +911,7 @@ int API_EXPORTED libusb_get_max_packet_size(libusb_device *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 configution is examined. The calculation is based on the + * 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. * @@ -879,7 +954,7 @@ int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev, return LIBUSB_ERROR_NOT_FOUND; val = ep->wMaxPacketSize; - ep_type = ep->bmAttributes & 0x3; + ep_type = (enum libusb_transfer_type) (ep->bmAttributes & 0x3); libusb_free_config_descriptor(config); r = val & 0x07ff; @@ -922,6 +997,8 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev) 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); @@ -1022,6 +1099,7 @@ int API_EXPORTED libusb_open(libusb_device *dev, } _handle->dev = libusb_ref_device(dev); + _handle->auto_detach_kernel_driver = 0; _handle->claimed_interfaces = 0; memset(&_handle->os_priv, 0, priv_size); @@ -1042,7 +1120,7 @@ int API_EXPORTED libusb_open(libusb_device *dev, /* 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 + * dedicated libusbx 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); @@ -1053,7 +1131,7 @@ int API_EXPORTED libusb_open(libusb_device *dev, /** \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 + * for those scenarios where you are using libusbx to knock up a quick test * application - it allows you to avoid calling libusb_get_device_list() and * worrying about traversing/freeing the list. * @@ -1116,7 +1194,7 @@ static void do_close(struct libusb_context *ctx, /* 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); + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); if (transfer->dev_handle != dev_handle) continue; @@ -1304,7 +1382,14 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev, * endpoint halts cleared, toggles reset). * * You cannot change/reset configuration if your application has claimed - * interfaces - you should free them with libusb_release_interface() first. + * 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. * @@ -1326,6 +1411,7 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev, * \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) @@ -1339,7 +1425,10 @@ int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev, * 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. + * case libusbx 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 @@ -1357,6 +1446,7 @@ int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev, * 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) @@ -1390,6 +1480,9 @@ out: * 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 @@ -1397,6 +1490,7 @@ out: * \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) @@ -1523,7 +1617,7 @@ int API_EXPORTED libusb_reset_device(libusb_device_handle *dev) /** \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 + * is active, you cannot claim the interface, and libusbx will be unable to * perform I/O. * * This functionality is not available on Windows. @@ -1558,6 +1652,10 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev, * * This functionality is not available on Darwin or Windows. * + * Note that libusbx 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 @@ -1617,30 +1715,58 @@ int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev, return LIBUSB_ERROR_NOT_SUPPORTED; } +/** \ingroup dev + * Enable/disable libusbx's automatic kernel driver detachment. When this is + * enabled libusbx 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 libusbx 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 message verbosity. - * - Level 0: no messages ever printed by the library (default) - * - Level 1: error messages are printed to stderr - * - Level 2: warning and error messages are printed to stderr - * - Level 3: informational messages are printed to stdout, warning and error - * messages are printed to stderr - * - * The default level is 0, 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 set level 3. 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 + * 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. libusbx 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 libusbx 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 + * If libusbx 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 + * If libusbx 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 @@ -1655,7 +1781,7 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level) /** \ingroup lib * Initialize libusb. This function must be called before calling any other - * libusb function. + * libusbx 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 @@ -1675,6 +1801,11 @@ int API_EXPORTED libusb_init(libusb_context **context) 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++; @@ -1682,12 +1813,15 @@ int API_EXPORTED libusb_init(libusb_context **context) return 0; } - ctx = malloc(sizeof(*ctx)); + ctx = calloc(1, sizeof(*ctx)); if (!ctx) { r = LIBUSB_ERROR_NO_MEM; goto err_unlock; } - memset(ctx, 0, sizeof(*ctx)); + +#ifdef ENABLE_DEBUG_LOGGING + ctx->debug = LIBUSB_LOG_LEVEL_DEBUG; +#endif if (dbg) { ctx->debug = atoi(dbg); @@ -1695,13 +1829,15 @@ int API_EXPORTED libusb_init(libusb_context **context) ctx->debug_fixed = 1; } - usbi_dbg("libusb-%d.%d.%d%s%s%s", - libusb_version_internal.major, - libusb_version_internal.minor, - libusb_version_internal.micro, - libusb_version_internal.rc, - libusb_version_internal.describe[0] ? " git:" : "", - libusb_version_internal.describe); + /* 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("libusbx 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); @@ -1710,6 +1846,14 @@ int API_EXPORTED libusb_init(libusb_context **context) 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) @@ -1717,42 +1861,38 @@ int API_EXPORTED libusb_init(libusb_context **context) } r = usbi_io_init(ctx); - if (r < 0) { - if (usbi_backend->exit) - usbi_backend->exit(); - goto err_destroy_mutex; - } + if (r < 0) + goto err_backend_exit; - if (context) { - *context = ctx; - } else if (!usbi_default_context) { - usbi_dbg("created default context"); - usbi_default_context = ctx; - default_context_refcnt++; - } usbi_mutex_static_unlock(&default_context_lock); - 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 (context) + *context = ctx; return 0; -err_destroy_mutex: - usbi_mutex_destroy(&ctx->open_devs_lock); - usbi_mutex_destroy(&ctx->usb_devs_lock); +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); @@ -1773,8 +1913,8 @@ void API_EXPORTED libusb_exit(struct libusb_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) { - usbi_mutex_static_lock(&default_context_lock); if (--default_context_refcnt > 0) { usbi_dbg("not destroying default context"); usbi_mutex_static_unlock(&default_context_lock); @@ -1782,24 +1922,27 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx) } usbi_dbg("destroying default context"); usbi_default_context = NULL; - usbi_mutex_static_unlock(&default_context_lock); } + 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); - usbi_hotplug_deregister_all(ctx); - - 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); + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + usbi_hotplug_deregister_all(ctx); + 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_unlock(&ctx->usb_devs_lock); - /* a little sanity check. doesn't bother with open_devs locking because - * unless there is an application bug, nobody will be accessing this. */ + /* 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"); @@ -1815,18 +1958,23 @@ void API_EXPORTED libusb_exit(struct libusb_context *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 1 if the running library has the capability, 0 otherwise + * \returns nonzero if the running library has the capability, 0 otherwise */ int API_EXPORTED libusb_has_capability(uint32_t capability) { - enum libusb_capability cap = capability; - switch (cap) { + 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; } @@ -1859,87 +2007,161 @@ int API_EXPORTED libusb_has_capability(uint32_t capability) #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; - - if(tp) - { - GetSystemTimeAsFileTime (&_now.ft); - 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; +{ + 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 -void usbi_log_v(struct libusb_context *ctx, enum usbi_log_level level, +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) { - FILE *stream = stdout; - const char *prefix; + const char *prefix = ""; + char buf[USBI_MAX_LOG_LEN]; struct timeval now; - static struct timeval first = { 0, 0 }; + int global_debug, header_len, text_len; + static int has_debug_header_been_displayed = 0; -#ifndef ENABLE_DEBUG_LOGGING +#ifdef ENABLE_DEBUG_LOGGING + global_debug = 1; + UNUSED(ctx); +#else USBI_GET_CONTEXT(ctx); - if (!ctx) + if (ctx == NULL) return; + global_debug = (ctx->debug == LIBUSB_LOG_LEVEL_DEBUG); if (!ctx->debug) return; - if (level == LOG_LEVEL_WARNING && ctx->debug < 2) + if (level == LIBUSB_LOG_LEVEL_WARNING && ctx->debug < LIBUSB_LOG_LEVEL_WARNING) return; - if (level == LOG_LEVEL_INFO && ctx->debug < 3) + if (level == LIBUSB_LOG_LEVEL_INFO && ctx->debug < LIBUSB_LOG_LEVEL_INFO) + return; + if (level == LIBUSB_LOG_LEVEL_DEBUG && ctx->debug < LIBUSB_LOG_LEVEL_DEBUG) return; #endif usbi_gettimeofday(&now, NULL); - if (!first.tv_sec) { - first.tv_sec = now.tv_sec; - first.tv_usec = now.tv_usec; + 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 < first.tv_usec) { + if (now.tv_usec < timestamp_origin.tv_usec) { now.tv_sec--; now.tv_usec += 1000000; } - now.tv_sec -= first.tv_sec; - now.tv_usec -= first.tv_usec; + now.tv_sec -= timestamp_origin.tv_sec; + now.tv_usec -= timestamp_origin.tv_usec; switch (level) { - case LOG_LEVEL_INFO: + case LIBUSB_LOG_LEVEL_INFO: prefix = "info"; break; - case LOG_LEVEL_WARNING: - stream = stderr; + case LIBUSB_LOG_LEVEL_WARNING: prefix = "warning"; break; - case LOG_LEVEL_ERROR: - stream = stderr; + case LIBUSB_LOG_LEVEL_ERROR: prefix = "error"; break; - case LOG_LEVEL_DEBUG: - stream = stderr; + case LIBUSB_LOG_LEVEL_DEBUG: prefix = "debug"; break; + case LIBUSB_LOG_LEVEL_NONE: + return; default: - stream = stderr; prefix = "unknown"; break; } - fprintf(stream, "libusb: %d.%06d %s [%s] ", - (int)now.tv_sec, (int)now.tv_usec, prefix, function); + if (global_debug) { + header_len = snprintf(buf, sizeof(buf), + "[%2d.%06d] [%08x] libusbx: %s [%s] ", + (int)now.tv_sec, (int)now.tv_usec, usbi_get_tid(), prefix, function); + } else { + header_len = snprintf(buf, sizeof(buf), + "libusbx: %s [%s] ", prefix, function); + } - vfprintf(stream, format, args); + 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); - fprintf(stream, "\n"); + usbi_log_str(ctx, level, buf); } -void usbi_log(struct libusb_context *ctx, enum usbi_log_level level, +void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, const char *function, const char *format, ...) { va_list args; @@ -1950,19 +2172,18 @@ void usbi_log(struct libusb_context *ctx, enum usbi_log_level level, } /** \ingroup misc - * Returns a constant NULL-terminated string with the ASCII name of a libusb - * error code. The caller must not free() the returned string. + * Returns a constant NULL-terminated string with the ASCII name of a libusbx + * error or transfer status code. The caller must not free() the returned + * string. * - * \param error_code The \ref libusb_error code to return the name of. + * \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 code. + * error_code is not a known error / status code. */ DEFAULT_VISIBILITY const char * LIBUSB_CALL libusb_error_name(int error_code) { - enum libusb_error error = error_code; - switch (error) { - case LIBUSB_SUCCESS: - return "LIBUSB_SUCCESS"; + switch (error_code) { case LIBUSB_ERROR_IO: return "LIBUSB_ERROR_IO"; case LIBUSB_ERROR_INVALID_PARAM: @@ -1989,58 +2210,30 @@ DEFAULT_VISIBILITY const char * LIBUSB_CALL libusb_error_name(int error_code) return "LIBUSB_ERROR_NOT_SUPPORTED"; case LIBUSB_ERROR_OTHER: return "LIBUSB_ERROR_OTHER"; - } - return "**UNKNOWN**"; -} -/** \ingroup misc - * Returns a constant string with an English short description of the given - * error code. The caller should never free() the returned pointer since it - * points to a constant string. - * The returned string is encoded in ASCII form and always starts with a capital - * letter and ends without any dot. - * \param errcode the error code whose description is desired - * \returns a short description of the error code in English - */ -API_EXPORTED const char* libusb_strerror(enum libusb_error errcode) -{ - switch (errcode) { - case LIBUSB_SUCCESS: - return "Success"; - case LIBUSB_ERROR_IO: - return "Input/output error"; - case LIBUSB_ERROR_INVALID_PARAM: - return "Invalid parameter"; - case LIBUSB_ERROR_ACCESS: - return "Access denied (insufficient permissions)"; - case LIBUSB_ERROR_NO_DEVICE: - return "No such device (it may have been disconnected)"; - case LIBUSB_ERROR_NOT_FOUND: - return "Entity not found"; - case LIBUSB_ERROR_BUSY: - return "Resource busy"; - case LIBUSB_ERROR_TIMEOUT: - return "Operation timed out"; - case LIBUSB_ERROR_OVERFLOW: - return "Overflow"; - case LIBUSB_ERROR_PIPE: - return "Pipe error"; - case LIBUSB_ERROR_INTERRUPTED: - return "System call interrupted (perhaps due to signal)"; - case LIBUSB_ERROR_NO_MEM: - return "Insufficient memory"; - case LIBUSB_ERROR_NOT_SUPPORTED: - return "Operation not supported or unimplemented on this platform"; - case LIBUSB_ERROR_OTHER: - return "Other error"; + 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**"; } - - return "Unknown error"; } /** \ingroup misc * Returns a pointer to const struct libusb_version with the version - * (major, minor, micro, rc, and nano) of the running library. + * (major, minor, micro, nano and rc) of the running library. */ DEFAULT_VISIBILITY const struct libusb_version * LIBUSB_CALL libusb_get_version(void) diff --git a/compat/libusb-1.0/libusb/descriptor.c b/compat/libusb-1.0/libusb/descriptor.c index 4f81dab0..e1c49156 100644 --- a/compat/libusb-1.0/libusb/descriptor.c +++ b/compat/libusb-1.0/libusb/descriptor.c @@ -1,8 +1,8 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ /* - * USB descriptor handling functions for libusb - * Copyright (C) 2007 Daniel Drake - * Copyright (c) 2001 Johannes Erdfelt - * Copyright (c) 2012-2013 Nathan Hjelm + * USB descriptor handling functions for libusbx + * 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 @@ -23,8 +23,6 @@ #include #include #include -#include -#include #include "libusbi.h" @@ -68,10 +66,8 @@ int usbi_parse_descriptor(const unsigned char *source, const char *descriptor, sp += 2; dp += 2; break; - /* 32-bit word, convert from little endian to CPU */ - case 'd': - /* Align to word boundary */ - dp += ((unsigned long)dp & 1); + 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); @@ -83,6 +79,11 @@ int usbi_parse_descriptor(const unsigned char *source, const char *descriptor, sp += 4; dp += 4; break; + case 'u': /* 16 byte UUID */ + memcpy(dp, sp, 16); + sp += 16; + dp += 16; + break; } } @@ -105,25 +106,31 @@ static int parse_endpoint(struct libusb_context *ctx, int parsed = 0; int len; - usbi_parse_descriptor(buffer, "bb", &header, 0); - - /* Everything should be fine being passed into here, but we sanity */ - /* check JIC */ - if (header.bLength > size) { - usbi_err(ctx, "ran out of descriptors parsing"); - return -1; + 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; @@ -134,17 +141,21 @@ static int parse_endpoint(struct libusb_context *ctx, begin = buffer; while (size >= DESC_HEADER_LENGTH) { usbi_parse_descriptor(buffer, "bb", &header, 0); - - if (header.bLength < 2) { - usbi_err(ctx, "invalid descriptor length %d", header.bLength); - return -1; + 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)) + (header.bDescriptorType == LIBUSB_DT_INTERFACE) || + (header.bDescriptorType == LIBUSB_DT_CONFIG) || + (header.bDescriptorType == LIBUSB_DT_DEVICE)) break; usbi_dbg("skipping descriptor %x", header.bDescriptorType); @@ -208,6 +219,7 @@ static int parse_interface(libusb_context *ctx, int len; int r; int parsed = 0; + int interface_number = -1; size_t tmp; struct usb_descriptor_header header; struct libusb_interface_descriptor *ifp; @@ -218,7 +230,7 @@ static int parse_interface(libusb_context *ctx, while (size >= INTERFACE_DESC_LENGTH) { struct libusb_interface_descriptor *altsetting = (struct libusb_interface_descriptor *) usb_interface->altsetting; - altsetting = realloc(altsetting, + altsetting = usbi_reallocf(altsetting, sizeof(struct libusb_interface_descriptor) * (usb_interface->num_altsetting + 1)); if (!altsetting) { @@ -228,12 +240,37 @@ static int parse_interface(libusb_context *ctx, usb_interface->altsetting = altsetting; ifp = altsetting + usb_interface->num_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; @@ -244,20 +281,24 @@ static int parse_interface(libusb_context *ctx, /* Skip over any interface, class or vendor descriptors */ while (size >= DESC_HEADER_LENGTH) { usbi_parse_descriptor(buffer, "bb", &header, 0); - if (header.bLength < 2) { - usbi_err(ctx, "invalid descriptor of length %d", - header.bLength); + 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) || - (header.bDescriptorType == - LIBUSB_DT_SS_ENDPOINT_COMPANION)) + (header.bDescriptorType == LIBUSB_DT_ENDPOINT) || + (header.bDescriptorType == LIBUSB_DT_CONFIG) || + (header.bDescriptorType == LIBUSB_DT_DEVICE)) break; buffer += header.bLength; @@ -278,21 +319,6 @@ static int parse_interface(libusb_context *ctx, ifp->extra_length = len; } - /* Did we hit an unexpected descriptor? */ - 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); - r = LIBUSB_ERROR_IO; - goto err; - } - if (ifp->bNumEndpoints > 0) { struct libusb_endpoint_descriptor *endpoint; tmp = ifp->bNumEndpoints * sizeof(struct libusb_endpoint_descriptor); @@ -305,18 +331,14 @@ static int parse_interface(libusb_context *ctx, memset(endpoint, 0, tmp); for (i = 0; i < ifp->bNumEndpoints; i++) { - usbi_parse_descriptor(buffer, "bb", &header, 0); - - if (header.bLength > size) { - usbi_err(ctx, "ran out of descriptors parsing"); - r = LIBUSB_ERROR_IO; - goto err; - } - 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; @@ -328,7 +350,7 @@ static int parse_interface(libusb_context *ctx, ifp = (struct libusb_interface_descriptor *) buffer; if (size < LIBUSB_DT_INTERFACE_SIZE || ifp->bDescriptorType != LIBUSB_DT_INTERFACE || - !ifp->bAlternateSetting) + ifp->bInterfaceNumber != interface_number) return parsed; } @@ -353,18 +375,35 @@ static void clear_configuration(struct libusb_config_descriptor *config) static int parse_configuration(struct libusb_context *ctx, struct libusb_config_descriptor *config, unsigned char *buffer, - int host_endian) + int size, int host_endian) { int i; int r; - int size; size_t tmp; struct usb_descriptor_header header; struct libusb_interface *usb_interface; - usbi_parse_descriptor(buffer, "bbwbbbbb", config, host_endian); - size = config->wTotalLength; + 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; @@ -393,21 +432,25 @@ static int parse_configuration(struct libusb_context *ctx, while (size >= DESC_HEADER_LENGTH) { usbi_parse_descriptor(buffer, "bb", &header, 0); - if ((header.bLength > size) || - (header.bLength < DESC_HEADER_LENGTH)) { - usbi_err(ctx, "invalid descriptor length of %d", - header.bLength); + 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) || - (header.bDescriptorType == - LIBUSB_DT_SS_ENDPOINT_COMPANION)) + (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); @@ -435,6 +478,10 @@ static int parse_configuration(struct libusb_context *ctx, 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; @@ -447,9 +494,32 @@ err: 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; + int r, host_endian = 0; r = usbi_backend->get_device_descriptor(dev, (unsigned char *) &dev->device_descriptor, &host_endian); @@ -471,6 +541,9 @@ int usbi_device_cache_descriptor(libusb_device *dev) * * This is a non-blocking function; the device descriptor is cached in memory. * + * Note since libusbx-1.0.16, \ref LIBUSBX_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 @@ -501,49 +574,33 @@ int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev, int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev, struct libusb_config_descriptor **config) { - struct libusb_config_descriptor *_config = malloc(sizeof(*_config)); - unsigned char tmp[8]; + struct libusb_config_descriptor _config; + unsigned char tmp[LIBUSB_DT_CONFIG_SIZE]; unsigned char *buf = NULL; int host_endian = 0; int r; - usbi_dbg(""); - if (!_config) - return LIBUSB_ERROR_NO_MEM; - - r = usbi_backend->get_active_config_descriptor(dev, tmp, sizeof(tmp), - &host_endian); + r = usbi_backend->get_active_config_descriptor(dev, tmp, + LIBUSB_DT_CONFIG_SIZE, &host_endian); if (r < 0) - goto err; - - usbi_parse_descriptor(tmp, "bbw", _config, host_endian); - buf = malloc(_config->wTotalLength); - if (!buf) { - r = LIBUSB_ERROR_NO_MEM; - goto err; + 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; } - r = usbi_backend->get_active_config_descriptor(dev, buf, - _config->wTotalLength, &host_endian); - if (r < 0) - goto err; + usbi_parse_descriptor(tmp, "bbw", &_config, host_endian); + buf = malloc(_config.wTotalLength); + if (!buf) + return LIBUSB_ERROR_NO_MEM; - r = parse_configuration(dev->ctx, _config, buf, host_endian); - if (r < 0) { - usbi_err(dev->ctx, "parse_configuration failed with error %d", r); - goto err; - } else if (r > 0) { - usbi_warn(dev->ctx, "descriptor data still left"); - } + 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); - *config = _config; - return 0; - -err: - free(_config); - if (buf) - free(buf); return r; } @@ -566,8 +623,8 @@ err: 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[8]; + struct libusb_config_descriptor _config; + unsigned char tmp[LIBUSB_DT_CONFIG_SIZE]; unsigned char *buf = NULL; int host_endian = 0; int r; @@ -576,51 +633,34 @@ int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev, if (config_index >= dev->num_configurations) return LIBUSB_ERROR_NOT_FOUND; - _config = malloc(sizeof(*_config)); - if (!_config) - return LIBUSB_ERROR_NO_MEM; - r = usbi_backend->get_config_descriptor(dev, config_index, tmp, - sizeof(tmp), &host_endian); + LIBUSB_DT_CONFIG_SIZE, &host_endian); if (r < 0) - goto err; - - usbi_parse_descriptor(tmp, "bbw", _config, host_endian); - buf = malloc(_config->wTotalLength); - if (!buf) { - r = LIBUSB_ERROR_NO_MEM; - goto err; + 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; } - host_endian = 0; - r = usbi_backend->get_config_descriptor(dev, config_index, buf, - _config->wTotalLength, &host_endian); - if (r < 0) - goto err; + usbi_parse_descriptor(tmp, "bbw", &_config, host_endian); + buf = malloc(_config.wTotalLength); + if (!buf) + return LIBUSB_ERROR_NO_MEM; - r = parse_configuration(dev->ctx, _config, buf, host_endian); - if (r < 0) { - usbi_err(dev->ctx, "parse_configuration failed with error %d", r); - goto err; - } else if (r > 0) { - usbi_warn(dev->ctx, "descriptor data still left"); - } + 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); - *config = _config; - return 0; - -err: - free(_config); - if (buf) - 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 or a LIBUSB_ERROR code + * 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) @@ -633,8 +673,10 @@ int usbi_get_config_index_by_value(struct libusb_device *dev, int host_endian; int r = usbi_backend->get_config_descriptor(dev, i, tmp, sizeof(tmp), &host_endian); - if (r < 0) + if (r < 0) { + *idx = -1; return r; + } if (tmp[5] == bConfigurationValue) { *idx = i; return 0; @@ -665,8 +707,18 @@ int usbi_get_config_index_by_value(struct libusb_device *dev, int API_EXPORTED libusb_get_config_descriptor_by_value(libusb_device *dev, uint8_t bConfigurationValue, struct libusb_config_descriptor **config) { - int idx; - int r = usbi_get_config_index_by_value(dev, bConfigurationValue, &idx); + 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) @@ -693,6 +745,394 @@ void API_EXPORTED libusb_free_config_descriptor( 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. * @@ -748,7 +1188,7 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev, if (di >= (length - 1)) break; - if (tbuf[si + 1]) /* high byte */ + if ((tbuf[si] & 0x80) || (tbuf[si + 1])) /* non-ASCII */ data[di++] = '?'; else data[di++] = tbuf[si]; @@ -757,116 +1197,3 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev, data[di] = 0; return di; } - -int API_EXPORTED libusb_parse_ss_endpoint_comp(const void *buf, int len, - struct libusb_ss_endpoint_companion_descriptor **ep_comp) -{ - struct libusb_ss_endpoint_companion_descriptor *ep_comp_desc; - struct usb_descriptor_header header; - - usbi_parse_descriptor(buf, "bb", &header, 0); - - /* Everything should be fine being passed into here, but we sanity */ - /* check JIC */ - if (header.bLength > len) { - usbi_err(NULL, "ran out of descriptors parsing"); - return LIBUSB_ERROR_NO_MEM; - } - - if (header.bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) { - usbi_err(NULL, "unexpected descriptor %x (expected %x)", - header.bDescriptorType, LIBUSB_DT_SS_ENDPOINT_COMPANION); - return LIBUSB_ERROR_INVALID_PARAM; - } - - ep_comp_desc = calloc(1, sizeof (*ep_comp_desc)); - if (!ep_comp_desc) { - return LIBUSB_ERROR_NO_MEM; - } - - if (header.bLength >= LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) - usbi_parse_descriptor(buf, "bbbbw", ep_comp_desc, 0); - - *ep_comp = ep_comp_desc; - - return LIBUSB_SUCCESS; -} - -void API_EXPORTED libusb_free_ss_endpoint_comp(struct libusb_ss_endpoint_companion_descriptor *ep_comp) -{ - assert(ep_comp); - free(ep_comp); -} - -int API_EXPORTED libusb_parse_bos_descriptor(const void *buf, int len, - struct libusb_bos_descriptor **bos) -{ - const unsigned char *buffer = (const unsigned char *) buf; - struct libusb_bos_descriptor *bos_desc; - int i; - - len = len; - bos_desc = calloc (1, sizeof (*bos_desc)); - if (!bos_desc) { - return LIBUSB_ERROR_NO_MEM; - } - - usbi_parse_descriptor(buffer, "bbwb", bos_desc, 0); - buffer += LIBUSB_DT_BOS_SIZE; - - /* Get the device capability descriptors */ - for (i = 0; i < bos_desc->bNumDeviceCaps; ++i) { - if (buffer[2] == LIBUSB_USB_CAP_TYPE_EXT) { - if (!bos_desc->usb_2_0_ext_cap) { - bos_desc->usb_2_0_ext_cap = - (struct libusb_usb_2_0_device_capability_descriptor *) - malloc(sizeof(*bos_desc->usb_2_0_ext_cap)); - usbi_parse_descriptor(buffer, "bbbd", - bos_desc->usb_2_0_ext_cap, 0); - } else - usbi_warn(NULL, - "usb_2_0_ext_cap was already allocated"); - - /* move to the next device capability descriptor */ - buffer += LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE; - } else if (buffer[2] == LIBUSB_SS_USB_CAP_TYPE) { - if (!bos_desc->ss_usb_cap) { - bos_desc->ss_usb_cap = - (struct libusb_ss_usb_device_capability_descriptor *) - malloc(sizeof(*bos_desc->ss_usb_cap)); - usbi_parse_descriptor(buffer, "bbbbwbbw", - bos_desc->ss_usb_cap, 0); - } else - usbi_warn(NULL, - "ss_usb_cap was already allocated"); - - /* move to the next device capability descriptor */ - buffer += LIBUSB_SS_USB_DEVICE_CAPABILITY_SIZE; - } else { - usbi_info(NULL, "wireless/container_id capability " - "descriptor"); - - /* move to the next device capability descriptor */ - buffer += buffer[0]; - } - } - - *bos = bos_desc; - - return LIBUSB_SUCCESS; -} - -void API_EXPORTED libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos) -{ - assert(bos); - - if (bos->usb_2_0_ext_cap) { - free(bos->usb_2_0_ext_cap); - } - - if (bos->ss_usb_cap) { - free(bos->ss_usb_cap); - } - - free(bos); -} diff --git a/compat/libusb-1.0/libusb/hotplug.c b/compat/libusb-1.0/libusb/hotplug.c index d00d0b9e..081bc798 100644 --- a/compat/libusb-1.0/libusb/hotplug.c +++ b/compat/libusb-1.0/libusb/hotplug.c @@ -1,8 +1,8 @@ -/* -*- Mode: C; indent-tabs-mode:nil ; c-basic-offset:8 -*- */ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ /* - * Hotplug functions for libusb - * Copyright (C) 2012-2013 Nathan Hjelm - * Copyright (C) 2012-2013 Peter Stuge + * Hotplug functions for libusbx + * 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 @@ -25,7 +25,9 @@ #include #include #include +#ifdef HAVE_SYS_TYPES_H #include +#endif #include #include "libusbi.h" @@ -33,27 +35,39 @@ /** * @defgroup hotplug Device hotplug event notification - * This page details how to use the libusb hotplug interface. + * 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 * - * Releases of libusb 1.0 newer than 1.X have added support for hotplug - * events. This interface allows you to request notification for the - * arrival and departure of matching USB devices. + * Version 1.0.16, \ref LIBUSBX_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 - * libusb_hotplug_register_callback(). This function will optionally return - * a handle that can be passed to libusb_hotplug_deregister_callback(). + * \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. + * 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 particulat context are automatically deregistered by libusb_exit(). + * Callbacks for a particular context are automatically deregistered by libusb_exit(). * - * As of 1.X there are two supported hotplug events: + * 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 * @@ -66,7 +80,7 @@ * 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, + * safe to call any libusbx 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(). * @@ -128,160 +142,170 @@ int main (void) { \endcode */ -static int usbi_hotplug_match_cb (struct libusb_device *dev, libusb_hotplug_event event, - struct libusb_hotplug_callback *hotplug_cb) { - struct libusb_context *ctx = dev->ctx; - - /* 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 == usbi_default_context ? NULL : ctx, - dev, event, hotplug_cb->user_data); +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_device *dev, libusb_hotplug_event event) { +void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, + libusb_hotplug_event event) +{ struct libusb_hotplug_callback *hotplug_cb, *next; - struct libusb_context *ctx = dev->ctx; + 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); - int ret = usbi_hotplug_match_cb (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); + 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); - /* loop through and disconnect all open handles for this device */ - if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) { - struct libusb_device_handle *handle; - - usbi_mutex_lock(&ctx->open_devs_lock); - list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) { - if (dev == handle->dev) { - usbi_handle_disconnect (handle); - } - } - usbi_mutex_unlock(&ctx->open_devs_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; + 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++; + /* 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); - if (flags & LIBUSB_HOTPLUG_ENUMERATE) { - struct libusb_device *dev; + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); - usbi_mutex_lock(&ctx->usb_devs_lock); - list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) { - (void) usbi_hotplug_match_cb (dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, new_callback); - } + if (flags & LIBUSB_HOTPLUG_ENUMERATE) { + int i, len; + struct libusb_device **devs; - usbi_mutex_unlock(&ctx->usb_devs_lock); - } + len = (int) libusb_get_device_list(ctx, &devs); + if (len < 0) { + libusb_hotplug_deregister_callback(ctx, + new_callback->handle); + return len; + } - usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + for (i = 0; i < len; i++) { + usbi_hotplug_match_cb(ctx, devs[i], + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, + new_callback); + } - if (handle) { - *handle = new_callback->handle; - } + libusb_free_device_list(devs, 1); + } - return LIBUSB_SUCCESS; + + 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; - - /* 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); +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) { @@ -291,7 +315,7 @@ void usbi_hotplug_deregister_all(struct libusb_context *ctx) { list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) { list_del(&hotplug_cb->list); - free(hotplug_cb); + free(hotplug_cb); } usbi_mutex_unlock(&ctx->hotplug_cbs_lock); diff --git a/compat/libusb-1.0/libusb/hotplug.h b/compat/libusb-1.0/libusb/hotplug.h index 64d4c74d..614ddbcf 100644 --- a/compat/libusb-1.0/libusb/hotplug.h +++ b/compat/libusb-1.0/libusb/hotplug.h @@ -1,8 +1,8 @@ -/* -*- Mode: C; indent-tabs-mode:nil ; c-basic-offset:8 -*- */ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ /* - * Hotplug support for libusb 1.0 - * Copyright (C) 2012 Nathan Hjelm - * Copyright (C) 2012 Peter Stuge + * Hotplug support for libusbx + * 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 @@ -22,6 +22,10 @@ #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() @@ -41,7 +45,7 @@ struct libusb_hotplug_callback { int dev_class; /** Hotplug callback flags */ - libusb_hotplug_flag flags; + libusb_hotplug_flag flags; /** Event(s) that will trigger this callback */ libusb_hotplug_event events; @@ -49,29 +53,30 @@ struct libusb_hotplug_callback { /** 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; + /** 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; + /** Callback is marked for deletion */ + int needs_free; /** List this callback is registered in (ctx->hotplug_cbs) */ - struct list_head list; + struct list_head list; }; typedef struct libusb_hotplug_callback libusb_hotplug_callback; struct libusb_hotplug_message { - libusb_hotplug_event event; - struct libusb_device *device; + 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_device *dev, libusb_hotplug_event event); +void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, + libusb_hotplug_event event); #endif diff --git a/compat/libusb-1.0/libusb/io.c b/compat/libusb-1.0/libusb/io.c index f488478b..4f22963c 100644 --- a/compat/libusb-1.0/libusb/io.c +++ b/compat/libusb-1.0/libusb/io.c @@ -1,7 +1,8 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ /* - * I/O functions for libusb - * Copyright (C) 2007-2009 Daniel Drake - * Copyright (c) 2001 Johannes Erdfelt + * I/O functions for libusbx + * 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 @@ -18,19 +19,18 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include "config.h" #include -#include #include #include #include #include -#include - +#ifdef HAVE_SIGNAL_H +#include +#endif #ifdef HAVE_SYS_TIME_H #include #endif - #ifdef USBI_TIMERFD_AVAILABLE #include #endif @@ -43,10 +43,10 @@ * * \section intro Introduction * - * If you're using libusb in your application, you're probably wanting to + * If you're using libusbx 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 + * libusbx 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. @@ -76,7 +76,7 @@ * 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 + * libusbx 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. @@ -131,9 +131,9 @@ if (r == 0 && actual_length == sizeof(data)) { * above. * * Instead of providing which functions that block until the I/O has complete, - * libusb's asynchronous interface presents non-blocking functions which + * libusbx'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 + * callback function pointer to this non-blocking function, which libusbx will * call with the results of the transaction when it has completed. * * Transfers which have been submitted through the non-blocking functions @@ -144,12 +144,12 @@ if (r == 0 && actual_length == sizeof(data)) { * to use threads. * * This added flexibility does come with some complications though: - * - In the interest of being a lightweight library, libusb does not create + * - In the interest of being a lightweight library, libusbx 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 + * application must call into libusbx from it's main loop when events are ready + * to be handled, or you must use some other scheme to allow libusbx to * undertake whatever work needs to be done. - * - libusb also needs to be called into at certain fixed points in time in + * - libusbx 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 @@ -159,7 +159,7 @@ if (r == 0 && actual_length == sizeof(data)) { * 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 + * Internally, libusbx'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 @@ -176,25 +176,25 @@ if (r == 0 && actual_length == sizeof(data)) { * 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 + * libusbx 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 + * When requesting data on a bulk endpoint, libusbx requires you to supply a + * buffer and the maximum number of bytes of data that libusbx 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 + * or equal to the buffer size. libusbx 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 + * the buffer. libusbx 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. @@ -212,7 +212,7 @@ if (r == 0 && actual_length == sizeof(data)) { /** * @defgroup asyncio Asynchronous device I/O * - * This page details libusb's asynchronous (non-blocking) API for USB device + * This page details libusbx'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 @@ -227,7 +227,7 @@ if (r == 0 && actual_length == sizeof(data)) { * * \section asyncabstraction Transfer abstraction * - * For the asynchronous I/O, libusb implements the concept of a generic + * For the asynchronous I/O, libusbx 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. @@ -240,7 +240,7 @@ if (r == 0 && actual_length == sizeof(data)) { * -# 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 + * -# Submission: ask libusbx to submit the transfer * -# Completion handling: examine transfer results in the * libusb_transfer structure * -# Deallocation: clean up resources @@ -287,7 +287,7 @@ if (r == 0 && actual_length == sizeof(data)) { * * 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: + * time, libusbx 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. * @@ -326,7 +326,7 @@ if (r == 0 && actual_length == sizeof(data)) { * 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 + * libusbx 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 @@ -468,7 +468,7 @@ if (r == 0 && actual_length == sizeof(data)) { * * 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 + * transfer may return before libusbx 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. * @@ -488,64 +488,101 @@ if (r == 0 && actual_length == sizeof(data)) { * \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 + * "LIBUSB_TRANSFER_FREE_BUFFER" allows you to ask libusbx 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 + * "LIBUSB_TRANSFER_FREE_TRANSFER" causes libusbx to automatically free the * transfer after the transfer callback returns. * * \section asyncevent Event handling * - * In accordance of the aim of being a lightweight library, libusb does not - * create threads internally. This means that libusb code does not execute - * at any time other than when your application is calling a libusb function. - * However, an asynchronous model requires that libusb perform work at various + * An asynchronous model requires that libusbx 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 + * application must call into when libusbx has work do to. This gives libusbx * the opportunity to reap pending transfers, invoke callbacks, etc. * - * The first issue to discuss here is how your application can figure out - * when libusb has work to do. In fact, there are two naive options which - * do not actually require your application to know this: - * -# Periodically call libusb_handle_events() in non-blocking mode at fixed - * short intervals from your main loop + * There are 2 different approaches to dealing with libusb_handle_events: + * * -# Repeatedly call libusb_handle_events() in blocking mode from a dedicated * thread. + * -# Integrate libusbx with your application's main event loop. libusbx + * exposes a set of file descriptors which allow you to do this. * - * The first option is plainly not very nice, and will cause unnecessary - * CPU wakeups leading to increased power usage and decreased battery life. - * The second option is not very nice either, but may be the nicest option - * available to you if the "proper" approach can not be applied to your - * application (read on...). + * The first approach has the big advantage that it will also work on Windows + * were libusbx' 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. * - * The recommended option is to integrate libusb with your application main - * event loop. libusb exposes a set of file descriptors which allow you to do - * this. 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(). + * 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 libusbx + * into your application's main event loop. * - * There is one final event handling complication. libusb supports - * asynchronous transfers which time out after a specified time period, and - * this requires that libusb is called into at or after the timeout so that - * the timeout can be handled. 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. + * \section eventthread Using an event handling thread * - * For the details on retrieving the set of file descriptors and determining - * the next timeout, see the \ref poll "polling and timing" API documentation. + * Lets begin with stating the obvious: If you're going to use a separate + * thread for libusbx 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 libusbx' \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. + * This page documents libusbx'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 @@ -553,10 +590,28 @@ if (r == 0 && actual_length == sizeof(data)) { * * 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 + * asynchronous API documentation. In summary, libusbx 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. - * In order to know precisely when libusb needs to be called into, libusb + * into libusbx 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 + * libusbx'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. libusbx 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 libusbx is called into at or after + * the timeout to handle it. So, in addition to considering libusbx's file + * descriptors in your main event loop, you must also consider that libusbx + * 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 libusbx needs to be called into, libusbx * offers you a set of pollable file descriptors and information about when * the next timeout expires. * @@ -565,10 +620,10 @@ if (r == 0 && actual_length == sizeof(data)) { * * \section pollsimple The simple option * - * If your application revolves solely around libusb and does not need to + * If your application revolves solely around libusbx and does not need to * handle other event sources, you can have a program structure as follows: \code -// initialize libusb +// initialize libusbx // find and open device // maybe fire off some initial async I/O @@ -585,20 +640,21 @@ while (user_has_not_requested_exit) * \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. Exposing event sources - * on Windows will require some further thought and design. + * 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 + * movements, etc. Through exposing a set of file descriptors, libusbx 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 + * take a set of file descriptors from libusbx and monitor those too. When you + * detect activity on libusbx'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 + * What's more, libusbx 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 @@ -606,25 +662,25 @@ while (user_has_not_requested_exit) * 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 + * libusbx 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, + * interpret the meaning of the file descriptors, just do as libusbx indicates, * polling all of them at once. * * In pseudo-code, you want something that looks like: \code -// initialise libusb +// initialise libusbx 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) + poll(on libusbx file descriptors plus any other event sources of interest, + using a timeout no larger than the value libusbx just suggested) + if (poll() indicated activity on libusbx file descriptors) libusb_handle_events_timeout(ctx, &zero_tv); - if (time has elapsed to or beyond the libusb timeout) + if (time has elapsed to or beyond the libusbx timeout) libusb_handle_events_timeout(ctx, &zero_tv); // handle events from other sources here } @@ -634,7 +690,7 @@ while (user has not requested application exit) { * * \subsection polltime Notes on time-based events * - * The above complication with having to track time and call into libusb at + * The above complication with having to track time and call into libusbx 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 @@ -646,18 +702,18 @@ while (user has not requested application exit) { * - 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 + * - libusbx 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 +// initialise libusbx libusb_get_pollfds(ctx) while (user has not requested application exit) { - poll(on libusb file descriptors plus any other event sources of interest, + poll(on libusbx file descriptors plus any other event sources of interest, using any timeout that you like) - if (poll() indicated activity on libusb file descriptors) + if (poll() indicated activity on libusbx file descriptors) libusb_handle_events_timeout(ctx, &zero_tv); // handle events from other sources here } @@ -667,20 +723,20 @@ while (user has not requested application exit) { * * 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 + * and any future platforms supported by libusbx 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 + * If your application has taken the shortcut of ignoring libusbx'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 + * The set of file descriptors that libusbx 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(). @@ -701,10 +757,10 @@ while (user has not requested application exit) { /** \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. + * libusbx is a thread-safe library, but extra considerations must be applied + * to applications which interact with libusbx from multiple threads. * - * The underlying issue that must be addressed is that all libusb I/O + * The underlying issue that must be addressed is that all libusbx 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 @@ -712,13 +768,13 @@ while (user has not requested application exit) { * 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 + * or select() on libusbx'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 + * synchronous interface on top of the asynchronous interface (and libusbx * does something similar, albeit more advanced due to the complications * explained on this page). * @@ -731,7 +787,7 @@ void cb(struct libusb_transfer *transfer) void myfunc() { struct libusb_transfer *transfer; - unsigned char buffer[LIBUSB_CONTROL_SETUP_SIZE]; + unsigned char buffer[LIBUSB_CONTROL_SETUP_SIZE] __attribute__ ((aligned (2))); int completed = 0; transfer = libusb_alloc_transfer(0); @@ -741,7 +797,7 @@ void myfunc() { libusb_submit_transfer(transfer); while (!completed) { - poll(libusb file descriptors, 120*1000); + poll(libusbx file descriptors, 120*1000); if (poll indicates activity) libusb_handle_events_timeout(ctx, &zero_tv); } @@ -755,7 +811,7 @@ void myfunc() { * 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 + * If this is the only thread that is polling libusbx'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 @@ -767,13 +823,13 @@ void myfunc() { * * 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 + * impact the capabilities of the library, so libusbx 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. + * synchronous I/O functions - libusbx hides this headache from you. * * \section Using libusb_handle_events() from multiple threads * @@ -819,17 +875,17 @@ void myfunc() { * * \section eventlock The events lock * - * The problem is when we consider the fact that libusb exposes file + * The problem is when we consider the fact that libusbx 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 + * libusbx's back. If you do take libusbx'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, + * You must take the events lock before polling libusbx 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(). * @@ -840,7 +896,7 @@ void myfunc() { \code libusb_lock_events(ctx); while (!completed) { - poll(libusb file descriptors, 120*1000); + poll(libusbx file descriptors, 120*1000); if (poll indicates activity) libusb_handle_events_timeout(ctx, &zero_tv); } @@ -856,7 +912,7 @@ void myfunc() { * 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 + * To solve this, libusbx 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). @@ -882,7 +938,7 @@ if (libusb_try_lock_events(ctx) == 0) { libusb_unlock_events(ctx); goto retry; } - poll(libusb file descriptors, 120*1000); + poll(libusbx file descriptors, 120*1000); if (poll indicates activity) libusb_handle_events_locked(ctx, 0); } @@ -928,8 +984,8 @@ printf("completed!\n"); * 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 + * -# libusb_event_handling_ok() checks that libusbx is still happy for your + * thread to be performing event handling. Sometimes, libusbx 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. @@ -939,12 +995,12 @@ printf("completed!\n"); * 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. + * "working behind libusbx'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 + * blocked on libusb_wait_for_event(). This is because libusbx 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). @@ -953,7 +1009,7 @@ printf("completed!\n"); * * 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 + * questions regarding libusbx'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 @@ -968,14 +1024,14 @@ printf("completed!\n"); * 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 + * libusbx 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 + * -# During initialization, libusbx 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 + * -# During libusb_close(), libusbx writes some dummy data on this control pipe. + * This immediately interrupts the event handler. libusbx 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 @@ -990,7 +1046,7 @@ printf("completed!\n"); * 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 + * -# With the events lock held inside libusb_close(), libusbx 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 @@ -1007,7 +1063,7 @@ printf("completed!\n"); * 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 + * -# libusbx 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 @@ -1023,7 +1079,7 @@ printf("completed!\n"); * * 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 + * applies to applications that take libusbx'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 @@ -1072,19 +1128,16 @@ int usbi_io_init(struct libusb_context *ctx) if (r < 0) goto err_close_pipe; - /* create hotplug pipe */ - r = usbi_pipe(ctx->hotplug_pipe); + /* create hotplug pipe */ + r = usbi_pipe(ctx->hotplug_pipe); if (r < 0) { r = LIBUSB_ERROR_OTHER; goto err; } -#ifndef OS_WINDOWS - fcntl (ctx->hotplug_pipe[1], F_SETFD, O_NONBLOCK); -#endif - r = usbi_add_pollfd(ctx, ctx->hotplug_pipe[0], POLLIN); - if (r < 0) - goto err_close_hp_pipe; + 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(), @@ -1106,8 +1159,8 @@ int usbi_io_init(struct libusb_context *ctx) return 0; err_close_hp_pipe: - usbi_close(ctx->hotplug_pipe[0]); - usbi_close(ctx->hotplug_pipe[1]); + 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]); @@ -1126,7 +1179,7 @@ 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_remove_pollfd(ctx, ctx->hotplug_pipe[0]); usbi_close(ctx->hotplug_pipe[0]); usbi_close(ctx->hotplug_pipe[1]); #ifdef USBI_TIMERFD_AVAILABLE @@ -1173,8 +1226,10 @@ static int calculate_timeout(struct usbi_transfer *transfer) } /* add a transfer to the (timeout-sorted) active transfers list. - * returns 1 if the transfer has a timeout and it is the timeout next to - * expire */ + * 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; @@ -1183,19 +1238,16 @@ static int add_to_flying_list(struct usbi_transfer *transfer) int r = 0; int first = 1; - usbi_mutex_lock(&ctx->flying_transfers_lock); - /* 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); - if (timerisset(timeout)) - r = 1; 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; } @@ -1208,21 +1260,38 @@ static int add_to_flying_list(struct usbi_transfer *transfer) (cur_tv->tv_sec == timeout->tv_sec && cur_tv->tv_usec > timeout->tv_usec)) { list_add_tail(&transfer->list, &cur->list); - r = first; 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: - usbi_mutex_unlock(&ctx->flying_transfers_lock); +#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 + * Allocate a libusbx 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(). @@ -1254,11 +1323,10 @@ struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer( + sizeof(struct libusb_transfer) + (sizeof(struct libusb_iso_packet_descriptor) * iso_packets) + os_alloc_size; - struct usbi_transfer *itransfer = malloc(alloc_size); + struct usbi_transfer *itransfer = calloc(1, alloc_size); if (!itransfer) return NULL; - memset(itransfer, 0, alloc_size); itransfer->num_iso_packets = iso_packets; usbi_mutex_init(&itransfer->lock, NULL); return USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); @@ -1295,6 +1363,62 @@ void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer) 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. @@ -1313,7 +1437,6 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) struct usbi_transfer *itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); int r; - int first; int updated_fds; usbi_mutex_lock(&itransfer->lock); @@ -1325,28 +1448,19 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) goto out; } - first = add_to_flying_list(itransfer); - r = usbi_backend->submit_transfer(itransfer); - if (r) { - usbi_mutex_lock(&ctx->flying_transfers_lock); - list_del(&itransfer->list); - usbi_mutex_unlock(&ctx->flying_transfers_lock); + usbi_mutex_lock(&ctx->flying_transfers_lock); + r = add_to_flying_list(itransfer); + if (r == LIBUSB_SUCCESS) { + r = usbi_backend->submit_transfer(itransfer); } -#ifdef USBI_TIMERFD_AVAILABLE - else if (first && usbi_using_timerfd(ctx)) { - /* 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}, - { itransfer->timeout.tv_sec, itransfer->timeout.tv_usec * 1000 } }; - usbi_dbg("arm timerfd for timeout in %dms (first in line)", transfer->timeout); - r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL); - if (r < 0) - r = LIBUSB_ERROR_OTHER; + if (r != LIBUSB_SUCCESS) { + list_del(&itransfer->list); + arm_timerfd_for_next_timeout(ctx); } -#else - (void)first; -#endif + usbi_mutex_unlock(&ctx->flying_transfers_lock); + /* 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); @@ -1379,7 +1493,8 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer) usbi_mutex_lock(&itransfer->lock); r = usbi_backend->cancel_transfer(itransfer); if (r < 0) { - if (r != LIBUSB_ERROR_NOT_FOUND) + if (r != LIBUSB_ERROR_NOT_FOUND && + r != LIBUSB_ERROR_NO_DEVICE) usbi_err(TRANSFER_CTX(transfer), "cancel transfer failed error %d", r); else @@ -1395,66 +1510,6 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer) return r; } -#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)) - return 0; - - /* 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; - } - } - - return 0; -} -#else -static int disarm_timerfd(struct libusb_context *ctx) -{ - (void)ctx; - return 0; -} -static int arm_timerfd_for_next_timeout(struct libusb_context *ctx) -{ - (void)ctx; - return 0; -} -#endif - /* 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 @@ -1469,6 +1524,7 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, 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; @@ -1479,13 +1535,10 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, usbi_mutex_lock(&ctx->flying_transfers_lock); list_del(&itransfer->list); - if (usbi_using_timerfd(ctx)) { + if (usbi_using_timerfd(ctx)) r = arm_timerfd_for_next_timeout(ctx); - if (0 == r) - r = disarm_timerfd(ctx); - } usbi_mutex_unlock(&ctx->flying_transfers_lock); - if (r < 0) + if (usbi_using_timerfd(ctx) && (r < 0)) return r; if (status == LIBUSB_TRANSFER_COMPLETED @@ -1512,6 +1565,7 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, 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; } @@ -1535,11 +1589,11 @@ int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer) /** \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. + * only one thread is monitoring libusbx 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. + * which calls poll() or select() on libusbx's file descriptors directly. + * If you stick to libusbx's event handling loop functions (e.g. * libusb_handle_events()) then you do not need to be concerned with this * locking. * @@ -1555,14 +1609,15 @@ int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer) 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); - r = ctx->pollfd_modify; + ru = ctx->pollfd_modify; usbi_mutex_unlock(&ctx->pollfd_modify_lock); - if (r) { + if (ru) { usbi_dbg("someone else is modifying poll fds"); return 1; } @@ -1578,11 +1633,11 @@ int API_EXPORTED libusb_try_lock_events(libusb_context *ctx) /** \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. + * monitoring libusbx 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. + * which calls poll() or select() on libusbx's file descriptors directly. + * If you stick to libusbx's event handling loop functions (e.g. * libusb_handle_events()) then you do not need to be concerned with this * locking. * @@ -1625,7 +1680,7 @@ void API_EXPORTED libusb_unlock_events(libusb_context *ctx) /** \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 + * Sometimes, libusbx 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. * @@ -1645,7 +1700,7 @@ void API_EXPORTED libusb_unlock_events(libusb_context *ctx) */ int API_EXPORTED libusb_event_handling_ok(libusb_context *ctx) { - int r; + unsigned int r; USBI_GET_CONTEXT(ctx); /* is someone else waiting to modify poll fds? if so, don't let this thread @@ -1673,7 +1728,7 @@ int API_EXPORTED libusb_event_handling_ok(libusb_context *ctx) */ int API_EXPORTED libusb_event_handler_active(libusb_context *ctx) { - int r; + unsigned int r; USBI_GET_CONTEXT(ctx); /* is someone else waiting to modify poll fds? if so, don't let this thread @@ -1699,9 +1754,9 @@ int API_EXPORTED libusb_event_handler_active(libusb_context *ctx) * 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, + * which calls poll() or select() on libusbx'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. + * If you stick to libusbx's event handling loop functions (e.g. * libusb_handle_events()) then you do not need to be concerned with this * locking. * @@ -1849,10 +1904,6 @@ static int handle_timerfd_trigger(struct libusb_context *ctx) { int r; - r = disarm_timerfd(ctx); - if (r < 0) - return r; - usbi_mutex_lock(&ctx->flying_transfers_lock); /* process the timeout that just happened */ @@ -1876,7 +1927,7 @@ 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; + struct pollfd *fds = NULL; int i = -1; int timeout_ms; @@ -1885,7 +1936,8 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv) nfds++; /* TODO: malloc when number of fd's changes, not on every poll */ - fds = malloc(sizeof(*fds) * nfds); + if (nfds != 0) + fds = malloc(sizeof(*fds) * nfds); if (!fds) { usbi_mutex_unlock(&ctx->pollfds_lock); return LIBUSB_ERROR_NO_MEM; @@ -1901,7 +1953,7 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv) } usbi_mutex_unlock(&ctx->pollfds_lock); - timeout_ms = (tv->tv_sec * 1000) + (tv->tv_usec / 1000); + timeout_ms = (int)(tv->tv_sec * 1000) + (tv->tv_usec / 1000); /* round up to next millisecond */ if (tv->tv_usec % 1000) @@ -1939,28 +1991,32 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv) } } - /* fd[1] is always the hotplug pipe */ - if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && fds[1].revents) { - libusb_hotplug_message message; - unsigned int ret; - - /* read the message from the hotplug thread */ - ret = read(ctx->hotplug_pipe[0], &message, sizeof (message)); - if (ret < sizeof(message)) { - ret = LIBUSB_ERROR_OTHER; - goto handled; - } - - usbi_hotplug_match(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 */ + /* 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"); + + /* 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 */ @@ -2026,7 +2082,7 @@ static int get_next_timeout(libusb_context *ctx, struct timeval *tv, /** \ingroup poll * Handle any pending events. * - * libusb determines "pending events" by checking if any timeouts have expired + * libusbx 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 @@ -2175,9 +2231,9 @@ int API_EXPORTED libusb_handle_events_completed(libusb_context *ctx, * 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 + * taken the event lock and are calling poll()/select() directly on libusbx'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 + * You detect events on libusbx'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 @@ -2204,19 +2260,19 @@ int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx, /** \ingroup poll * Determines whether your application must apply special timing considerations - * when monitoring libusb's file descriptors. + * when monitoring libusbx'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). + * libusbx's file descriptors in their own main loop (\ref pollmain). * - * Ordinarily, libusb's event handler needs to be called into at specific + * Ordinarily, libusbx'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 + * Some platforms supported by libusbx 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 @@ -2225,10 +2281,10 @@ int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx, * 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 + * \returns 0 if you must call into libusbx 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. - * \see \ref pollmain "Polling libusb file descriptors for event handling" + * \see \ref pollmain "Polling libusbx file descriptors for event handling" */ int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx) { @@ -2242,21 +2298,21 @@ int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx) } /** \ingroup poll - * Determine the next internal timeout that libusb needs to handle. You only + * Determine the next internal timeout that libusbx 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 + * on libusbx'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 + * long to wait for select() or poll() to return results. libusbx 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. + * (perhaps in non-blocking mode) so that libusbx 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 + * the case, it indicates that libusbx 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. * @@ -2265,7 +2321,7 @@ int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx) * * \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 + * clock in which libusbx 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 */ @@ -2314,7 +2370,7 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, 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 LIBUSB_ERROR_OTHER; + return 0; } TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts); @@ -2332,7 +2388,7 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, /** \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. + * that libusbx uses as an event source. * * To remove notifiers, pass NULL values for the function pointers. * @@ -2410,7 +2466,7 @@ void usbi_remove_pollfd(struct libusb_context *ctx, int fd) /** \ingroup poll * Retrieve a list of file descriptors that should be polled by your main loop - * as libusb event sources. + * as libusbx event sources. * * The returned list is NULL-terminated and should be freed with free() when * done. The actual list contents must not be touched. @@ -2450,14 +2506,15 @@ out: usbi_mutex_unlock(&ctx->pollfds_lock); return (const struct libusb_pollfd **) ret; #else - usbi_err(ctx, "external polling of libusb's internal descriptors "\ + usbi_err(ctx, "external polling of libusbx's internal descriptors "\ "is not yet supported on Windows platforms"); return NULL; #endif } -/* Backends call this from handle_events to report disconnection of a device. - * The transfers get cancelled appropriately. +/* 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) { @@ -2472,12 +2529,22 @@ void usbi_handle_disconnect(struct libusb_device_handle *handle) * * 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 look), the situation + * 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 - * FIXME: is this still potentially racy? + * + * 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) { @@ -2493,6 +2560,9 @@ void usbi_handle_disconnect(struct libusb_device_handle *handle) 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/compat/libusb-1.0/libusb/libusb-1.0.def b/compat/libusb-1.0/libusb/libusb-1.0.def index 1d6a5d21..cb0e32ab 100644 --- a/compat/libusb-1.0/libusb/libusb-1.0.def +++ b/compat/libusb-1.0/libusb/libusb-1.0.def @@ -1,4 +1,4 @@ -LIBRARY +LIBRARY "libusb-1.0.dll" EXPORTS libusb_alloc_transfer libusb_alloc_transfer@4 = libusb_alloc_transfer @@ -26,14 +26,26 @@ EXPORTS 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_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 @@ -42,6 +54,8 @@ EXPORTS 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 @@ -58,10 +72,24 @@ EXPORTS 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 @@ -76,6 +104,10 @@ EXPORTS 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 @@ -98,6 +130,8 @@ EXPORTS 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 @@ -106,6 +140,10 @@ EXPORTS 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_try_lock_events diff --git a/compat/libusb-1.0/libusb/libusb-1.0.rc b/compat/libusb-1.0/libusb/libusb-1.0.rc index a59a4307..ae49757c 100644 --- a/compat/libusb-1.0/libusb/libusb-1.0.rc +++ b/compat/libusb-1.0/libusb/libusb-1.0.rc @@ -5,16 +5,22 @@ * 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" +#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" +#define LIBUSB_VERSIONSTRING \ + LU_XSTR(LIBUSB_MAJOR) "." LU_XSTR(LIBUSB_MINOR) "." \ + LU_XSTR(LIBUSB_MICRO) LIBUSB_RC "\0" #endif #endif @@ -35,8 +41,7 @@ BEGIN BEGIN BLOCK "040904b0" BEGIN - VALUE "Comments", "\0" - VALUE "CompanyName", "libusb.org\0" + VALUE "CompanyName", "libusbx.org\0" VALUE "FileDescription", "C library for writing portable USB drivers in userspace\0" VALUE "FileVersion", LIBUSB_VERSIONSTRING VALUE "InternalName", "libusb\0" diff --git a/compat/libusb-1.0/libusb/libusb.h b/compat/libusb-1.0/libusb/libusb.h index f3a8f387..da3f1ef3 100644 --- a/compat/libusb-1.0/libusb/libusb.h +++ b/compat/libusb-1.0/libusb/libusb.h @@ -1,9 +1,10 @@ /* - * Public libusb header file - * Copyright (C) 2007-2008 Daniel Drake - * Copyright (c) 2001 Johannes Erdfelt - * Copyright (C) 2012-2013 Nathan Hjelm - * Copyright (C) 2012 Peter Stuge + * Public libusbx 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://libusbx.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,7 +26,9 @@ #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 @@ -38,7 +41,7 @@ #endif /* _SSIZE_T_DEFINED */ #endif /* _MSC_VER */ -/* stdint.h is also not usually available on MS */ +/* 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; @@ -47,36 +50,49 @@ typedef unsigned __int32 uint32_t; #include #endif +#if !defined(_WIN32_WCE) #include -#include -#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 + * undefine it so as not to break the current libusbx 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__) +#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. + * libusbx'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 + * Matching the Windows API itself, libusbx 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 @@ -84,7 +100,7 @@ typedef unsigned __int32 uint32_t; * 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 + * all functions which you use as libusbx callbacks with this LIBUSB_CALL * annotation, so that they too get compiled for the correct calling * convention. * @@ -92,24 +108,49 @@ typedef unsigned __int32 uint32_t; * 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 +/* LIBUSB_CALL must be defined on both definition and declaration of libusbx * 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__) +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) #define LIBUSB_CALL WINAPI #else #define LIBUSB_CALL #endif +/** \def LIBUSBX_API_VERSION + * \ingroup misc + * libusbx's API version. + * + * Since version 1.0.13, to help with feature detection, libusbx defines + * a LIBUSBX_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 + * libusbx applications may want to detect at compilation time. + * + * The macro is typically used in an application as follows: + * \code + * #if defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01001234) + * // Use one of the newer features from the libusbx API + * #endif + * \endcode + * + * Another feature of LIBUSBX_API_VERSION is that it can be used to detect + * whether you are compiling against the libusb or the libusbx library. + * + * Internally, LIBUSBX_API_VERSION is defined as follows: + * (libusbx major << 24) | (libusbx minor << 16) | (16 bit incremental) + */ +#define LIBUSBX_API_VERSION 0x01000102 + #ifdef __cplusplus extern "C" { #endif -/** \def libusb_cpu_to_le16 +/** \fn libusb_cpu_to_le16 * \ingroup misc * Convert a 16-bit value from host-endian to little-endian format. On * little endian systems, this function does nothing. On big endian systems, @@ -123,8 +164,8 @@ static inline uint16_t libusb_cpu_to_le16(const uint16_t x) uint8_t b8[2]; uint16_t b16; } _tmp; - _tmp.b8[1] = x >> 8; - _tmp.b8[0] = x & 0xff; + _tmp.b8[1] = (uint8_t) (x >> 8); + _tmp.b8[0] = (uint8_t) (x & 0xff); return _tmp.b16; } @@ -220,6 +261,12 @@ enum libusb_descriptor_type { /** 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, @@ -232,11 +279,8 @@ enum libusb_descriptor_type { /** Hub descriptor */ LIBUSB_DT_HUB = 0x29, - /** BOS descriptor */ - LIBUSB_DT_BOS = 0x0f, - - /** Device Capability descriptor */ - LIBUSB_DT_DEVICE_CAPABILITY = 0x10, + /** SuperSpeed Hub descriptor */ + LIBUSB_DT_SUPERSPEED_HUB = 0x2a, /** SuperSpeed Endpoint Companion descriptor */ LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30 @@ -246,16 +290,23 @@ enum libusb_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_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_SS_ENDPOINT_COMPANION_SIZE 6 #define LIBUSB_DT_BOS_SIZE 5 -#define LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE 7 -#define LIBUSB_SS_USB_DEVICE_CAPABILITY_SIZE 10 -#define LIBUSB_DT_BOS_MAX_SIZE ((LIBUSB_DT_BOS_SIZE) + \ - (LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE) + \ - (LIBUSB_SS_USB_DEVICE_CAPABILITY_SIZE)) +#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 @@ -293,7 +344,7 @@ enum libusb_transfer_type { }; /** \ingroup misc - * Standard requests, as defined in table 9-3 of the USB2 specifications */ + * 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, @@ -331,6 +382,13 @@ enum libusb_standard_request { /** 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 @@ -410,7 +468,7 @@ enum libusb_iso_usage_type { /** \ingroup desc * A structure representing the standard USB device descriptor. This - * descriptor is documented in section 9.6.1 of the USB 2.0 specification. + * 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 { @@ -462,41 +520,9 @@ struct libusb_device_descriptor { uint8_t bNumConfigurations; }; -/** \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 ultiple-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 structure representing the standard USB endpoint descriptor. This - * descriptor is documented in section 9.6.3 of the USB 2.0 specification. + * 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 { @@ -536,7 +562,7 @@ struct libusb_endpoint_descriptor { /** For audio devices only: the address if the synch endpoint */ uint8_t bSynchAddress; - /** Extra descriptors. If libusb encounters unknown endpoint descriptors, + /** Extra descriptors. If libusbx encounters unknown endpoint descriptors, * it will store them here, should you wish to parse them. */ const unsigned char *extra; @@ -544,10 +570,9 @@ struct libusb_endpoint_descriptor { int extra_length; }; - /** \ingroup desc * A structure representing the standard USB interface descriptor. This - * descriptor is documented in section 9.6.5 of the USB 2.0 specification. + * 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 { @@ -587,7 +612,7 @@ struct libusb_interface_descriptor { * by the bNumEndpoints field. */ const struct libusb_endpoint_descriptor *endpoint; - /** Extra descriptors. If libusb encounters unknown interface descriptors, + /** Extra descriptors. If libusbx encounters unknown interface descriptors, * it will store them here, should you wish to parse them. */ const unsigned char *extra; @@ -609,7 +634,7 @@ struct libusb_interface { /** \ingroup desc * A structure representing the standard USB configuration descriptor. This - * descriptor is documented in section 9.6.3 of the USB 2.0 specification. + * 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 { @@ -645,7 +670,7 @@ struct libusb_config_descriptor { * this array is determined by the bNumInterfaces field. */ const struct libusb_interface *interface; - /** Extra descriptors. If libusb encounters unknown configuration + /** Extra descriptors. If libusbx encounters unknown configuration * descriptors, it will store them here, should you wish to parse them. */ const unsigned char *extra; @@ -654,11 +679,66 @@ struct libusb_config_descriptor { }; /** \ingroup desc - * A structure representing the BOS descriptor. This - * descriptor is documented in section 9.6.2 of the USB 3.0 - * specification. All multiple-byte fields are represented in + * 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; @@ -675,20 +755,22 @@ struct libusb_bos_descriptor { * the BOS */ uint8_t bNumDeviceCaps; - /** USB 2.0 extension capability descriptor */ - struct libusb_usb_2_0_device_capability_descriptor *usb_2_0_ext_cap; - - /** SuperSpeed capabilty descriptor */ - struct libusb_ss_usb_device_capability_descriptor *ss_usb_cap; + /** 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 device capability descriptor for - * USB 2.0. This descriptor is documented in section 9.6.2.1 of - * the USB 3.0 specification. All mutiple-byte fields are represented - * in host-endian format. + * 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_device_capability_descriptor { +struct libusb_usb_2_0_extension_descriptor { /** Size of this descriptor (in bytes) */ uint8_t bLength; @@ -698,22 +780,21 @@ struct libusb_usb_2_0_device_capability_descriptor { uint8_t bDescriptorType; /** Capability type. Will have value - * \ref libusb_capability_type::LIBUSB_USB_CAP_TYPE_EXT - * LIBUSB_USB_CAP_TYPE_EXT in this context. */ + * \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_capability_attributes. */ + * See \ref libusb_usb_2_0_extension_attributes. */ uint32_t bmAttributes; }; /** \ingroup desc - * A structure representing the device capability descriptor for - * USB 3.0. This descriptor is documented in section 9.6.2.2 of - * the USB 3.0 specification. All mutiple-byte fields are represented - * in host-endian format. + * 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) */ @@ -725,14 +806,14 @@ struct libusb_ss_usb_device_capability_descriptor { uint8_t bDescriptorType; /** Capability type. Will have value - * \ref libusb_capability_type::LIBUSB_SS_USB_CAP_TYPE - * LIBUSB_SS_USB_CAP_TYPE in this context. */ + * \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_capability_attributes. */ + * See \ref libusb_ss_usb_device_capability_attributes. */ uint8_t bmAttributes; /** Bitmap encoding of the speed supported by this device when @@ -752,6 +833,31 @@ struct libusb_ss_usb_device_capability_descriptor { 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. */ @@ -783,7 +889,7 @@ struct libusb_control_setup { #define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) -/* libusb */ +/* libusbx */ struct libusb_context; struct libusb_device; @@ -791,7 +897,7 @@ struct libusb_device_handle; struct libusb_hotplug_callback; /** \ingroup lib - * Structure representing the libusb version. + * Structure providing the version of the libusbx runtime */ struct libusb_version { /** Library major version. */ @@ -803,27 +909,27 @@ struct libusb_version { /** Library micro version. */ const uint16_t micro; - /** Library nano version. This field is only nonzero on Windows. */ + /** Library nano version. */ const uint16_t nano; /** Library release candidate suffix string, e.g. "-rc4". */ const char *rc; - /** Output of `git describe --tags` at library build time. */ - const char *describe; + /** For ABI compatibility only. */ + const char* describe; }; /** \ingroup lib - * Structure representing a libusb session. The concept of individual libusb + * Structure representing a libusbx session. The concept of individual libusbx * 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 + * interference between the individual libusbx 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 + * If your application is guaranteed to only ever include a single libusbx * 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. @@ -864,20 +970,20 @@ typedef struct libusb_device_handle libusb_device_handle; * 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 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 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 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 high speed (480MBit/s). */ + LIBUSB_SPEED_HIGH = 3, - /** The device is operating at super speed (5000MBit/s). */ - LIBUSB_SPEED_SUPER = 4, + /** The device is operating at super speed (5000MBit/s). */ + LIBUSB_SPEED_SUPER = 4, }; /** \ingroup dev @@ -886,43 +992,61 @@ enum libusb_speed { */ enum libusb_supported_speed { /** Low speed operation supported (1.5MBit/s). */ - LIBUSB_LOW_SPEED_OPERATION = 1, + LIBUSB_LOW_SPEED_OPERATION = 1, /** Full speed operation supported (12MBit/s). */ - LIBUSB_FULL_SPEED_OPERATION = 2, + LIBUSB_FULL_SPEED_OPERATION = 2, /** High speed operation supported (480MBit/s). */ - LIBUSB_HIGH_SPEED_OPERATION = 4, + LIBUSB_HIGH_SPEED_OPERATION = 4, /** Superspeed operation supported (5000MBit/s). */ - LIBUSB_5GBPS_OPERATION = 8, + LIBUSB_SUPER_SPEED_OPERATION = 8, }; /** \ingroup dev - * Capability attributes + * 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_capability_attributes { +enum libusb_usb_2_0_extension_attributes { /** Supports Link Power Management (LPM) */ - LIBUSB_LPM_SUPPORT = 2, + 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_capability_type { - /** USB 2.0 extension capability type */ - LIBUSB_USB_CAP_TYPE_EXT = 2, +enum libusb_bos_type { + /** Wireless USB device capability */ + LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 1, - /** SuperSpeed capability type */ - LIBUSB_SS_USB_CAP_TYPE = 3, + /** 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 + * Error codes. Most libusbx functions return 0 on success or one of these * codes on failure. - * You can call \ref libusb_error_name() to retrieve a string representation - * of an error code or \ret libusb_strerror() to get an english description - * of an error code. + * 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) */ @@ -964,13 +1088,16 @@ enum libusb_error { /** Operation not supported or unimplemented on this platform */ LIBUSB_ERROR_NOT_SUPPORTED = -12, - /* NB! Remember to update libusb_error_name() and - libusb_strerror() when adding new error codes here. */ + /* 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 { @@ -996,6 +1123,9 @@ enum libusb_transfer_status { /** Device sent more data than requested */ LIBUSB_TRANSFER_OVERFLOW, + + /* NB! Remember to update libusb_error_name() + when adding new status codes here. */ }; /** \ingroup asyncio @@ -1058,7 +1188,7 @@ struct libusb_transfer; * 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 + * structure. libusbx 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. @@ -1130,15 +1260,41 @@ struct libusb_transfer { }; /** \ingroup misc - * Capabilities supported by this instance of libusb. Test if the loaded - * library supports a given capability by calling + * 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 = 0, - /** The libusb hotplug API is available. */ - LIBUSB_CAP_HAS_HOTPLUG = 1, + 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 libusbx 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); @@ -1147,6 +1303,7 @@ 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, @@ -1168,7 +1325,38 @@ 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, @@ -1202,6 +1390,8 @@ 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 */ @@ -1238,7 +1428,7 @@ static inline unsigned char *libusb_control_transfer_get_data( static inline struct libusb_control_setup *libusb_control_transfer_get_setup( struct libusb_transfer *transfer) { - return (struct libusb_control_setup *) transfer->buffer; + return (struct libusb_control_setup *)(void *) transfer->buffer; } /** \ingroup asyncio @@ -1247,6 +1437,7 @@ static inline struct libusb_control_setup *libusb_control_transfer_get_setup( * 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 @@ -1267,7 +1458,7 @@ static inline void libusb_fill_control_setup(unsigned char *buffer, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength) { - struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; + struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer; setup->bmRequestType = bmRequestType; setup->bRequest = bRequest; setup->wValue = libusb_cpu_to_le16(wValue); @@ -1303,6 +1494,7 @@ void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer); * \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 @@ -1312,15 +1504,15 @@ static inline void libusb_fill_control_transfer( unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) { - struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; + 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 = LIBUSB_CONTROL_SETUP_SIZE - + libusb_le16_to_cpu(setup->wLength); + transfer->length = (int) (LIBUSB_CONTROL_SETUP_SIZE + + libusb_le16_to_cpu(setup->wLength)); transfer->user_data = user_data; transfer->callback = callback; } @@ -1455,7 +1647,7 @@ static inline unsigned char *libusb_get_iso_packet_buffer( * signed to avoid compiler warnings. FIXME for libusb-2. */ if (packet > INT_MAX) return NULL; - _packet = packet; + _packet = (int) packet; if (_packet >= transfer->num_iso_packets) return NULL; @@ -1495,12 +1687,12 @@ static inline unsigned char *libusb_get_iso_packet_buffer_simple( * signed to avoid compiler warnings. FIXME for libusb-2. */ if (packet > INT_MAX) return NULL; - _packet = packet; + _packet = (int) packet; if (_packet >= transfer->num_iso_packets) return NULL; - return transfer->buffer + (transfer->iso_packet_desc[0].length * _packet); + return transfer->buffer + ((int) transfer->iso_packet_desc[0].length * _packet); } /* sync I/O */ @@ -1533,8 +1725,8 @@ static inline int libusb_get_descriptor(libusb_device_handle *dev, uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length) { return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, - LIBUSB_REQUEST_GET_DESCRIPTOR, (desc_type << 8) | desc_index, 0, data, - (uint16_t) length, 1000); + LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t) ((desc_type << 8) | desc_index), + 0, data, (uint16_t) length, 1000); } /** \ingroup desc @@ -1629,48 +1821,6 @@ 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 desc - * Parse a USB 3.0 endpoint companion descriptor. - * - * \param[in] buf the buffer containing the endpoint companion descriptor - * \param[in] len the length of the buffer - * \param[out] ep_comp a parsed endpoint companion descriptor. must be freed by - * libusb_free_ss_endpoint_comp() - * - * \returns LIBUSB_SUCCESS on success - * \returns LIBUSB_ERROR code on error - */ -int LIBUSB_CALL libusb_parse_ss_endpoint_comp(const void *buf, int len, - struct libusb_ss_endpoint_companion_descriptor **ep_comp); - -/** \ingroup desc - * Free a USB 3.0 endpoint companion descriptor. - * - * \param[in] ep_comp the descriptor to free - */ -void LIBUSB_CALL libusb_free_ss_endpoint_comp(struct libusb_ss_endpoint_companion_descriptor *ep_comp); - -/** \ingroup desc - * Parse a Binary Object Store (BOS) descriptor. - * - * \param[in] buf the buffer containing the BOS descriptor - * \param[in] len the length of the buffer - * \param[out] bos a parsed BOS descriptor. must be freed by - * libusb_free_bos_descriptor() - * - * \returns LIBUSB_SUCCESS on success - * \returns LIBUSB_ERROR code on error - */ -int LIBUSB_CALL libusb_parse_bos_descriptor(const void *buf, int len, - struct libusb_bos_descriptor **bos); - -/** \ingroup desc - * Free a Binary Object Store (BOS) descriptor. - * - * \param[in] bos the descriptor to free - */ -void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos); - /** \ingroup hotplug * Callback handle. * @@ -1679,11 +1829,16 @@ void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos); * per libusb_context and it is safe to call libusb_hotplug_deregister_callback() * on an already deregisted callback. * + * Since version 1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102 + * * For more information, see \ref hotplug. */ typedef int libusb_hotplug_callback_handle; /** \ingroup hotplug + * + * Since version 1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102 + * * Flags for hotplug events */ typedef enum { /** Arm the callback and fire it for all matching currently attached devices. */ @@ -1691,6 +1846,9 @@ typedef enum { } libusb_hotplug_flag; /** \ingroup hotplug + * + * Since version 1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102 + * * Hotplug events */ typedef enum { /** A device has been plugged in and is ready to use */ @@ -1713,13 +1871,15 @@ typedef enum { * 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 + * libusbx 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. * - * \param libusb_context context of this notification + * Since version 1.0.16, \ref LIBUSBX_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 @@ -1727,9 +1887,9 @@ typedef enum { * 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); + libusb_device *device, + libusb_hotplug_event event, + void *user_data); /** \ingroup hotplug * Register a hotplug callback function @@ -1739,6 +1899,20 @@ typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx, * 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 libusbx 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 LIBUSBX_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 @@ -1752,13 +1926,13 @@ typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx, * \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); + 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. @@ -1766,11 +1940,13 @@ int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx, * Deregister a callback from a libusb_context. This function is safe to call from within * a hotplug callback. * + * Since version 1.0.16, \ref LIBUSBX_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); + libusb_hotplug_callback_handle handle); #ifdef __cplusplus } diff --git a/compat/libusb-1.0/libusb/libusbi.h b/compat/libusb-1.0/libusb/libusbi.h index 198978e4..bc608b92 100644 --- a/compat/libusb-1.0/libusb/libusbi.h +++ b/compat/libusb-1.0/libusb/libusbi.h @@ -1,7 +1,7 @@ /* - * Internal header for libusb - * Copyright (C) 2007-2009 Daniel Drake - * Copyright (c) 2001 Johannes Erdfelt + * Internal header for libusbx + * 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 @@ -21,7 +21,9 @@ #ifndef LIBUSBI_H #define LIBUSBI_H -#include +#include "config.h" + +#include #include #include @@ -31,14 +33,17 @@ #include #endif -#include -#include +#ifdef HAVE_MISSING_H +#include "missing.h" +#endif +#include "libusb.h" +#include "version.h" -/* Inside the libusb code, mark all public functions as follows: +/* Inside the libusbx 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: + * In the libusbx public header, mark all declarations as: * return_type LIBUSB_CALL function_name(params); */ #define API_EXPORTED LIBUSB_CALL DEFAULT_VISIBILITY @@ -49,34 +54,50 @@ #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" + * ptr - the address of this list_head element in "type" + * type - the data type that contains "member" + * member - the list_head element in "type" */ #define list_entry(ptr, type, member) \ - ((type *)((uintptr_t)(ptr) - (uintptr_t)(&((type *)0L)->member))) + ((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 + * 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)) + 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_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)) @@ -108,6 +129,15 @@ static inline void list_del(struct list_head *entry) { entry->next->prev = entry->prev; 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) ({ \ @@ -119,93 +149,52 @@ static inline void list_del(struct list_head *entry) #define TIMESPEC_IS_SET(ts) ((ts)->tv_sec != 0 || (ts)->tv_nsec != 0) -enum usbi_log_level { - LOG_LEVEL_DEBUG, - LOG_LEVEL_INFO, - LOG_LEVEL_WARNING, - LOG_LEVEL_ERROR, -}; - -void usbi_log(struct libusb_context *ctx, enum usbi_log_level level, +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 usbi_log_level level, +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) -#endif - -#ifdef ENABLE_DEBUG_LOGGING -#define usbi_dbg(...) _usbi_log(NULL, LOG_LEVEL_DEBUG, __VA_ARGS__) -#else #define usbi_dbg(...) do {} while(0) #endif -#define usbi_info(ctx, ...) _usbi_log(ctx, LOG_LEVEL_INFO, __VA_ARGS__) -#define usbi_warn(ctx, ...) _usbi_log(ctx, LOG_LEVEL_WARNING, __VA_ARGS__) -#define usbi_err(ctx, ...) _usbi_log(ctx, LOG_LEVEL_ERROR, __VA_ARGS__) +#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 */ -/* Old MS compilers don't support variadic macros. The code is simple, so we - * repeat it for each loglevel. Note that the debug case is special. - * - * Support for variadic macros was introduced in Visual C++ 2005. - * http://msdn.microsoft.com/en-us/library/ms177415%28v=VS.80%29.aspx - */ - -static inline void usbi_info(struct libusb_context *ctx, const char *fmt, ...) -{ #ifdef ENABLE_LOGGING - va_list args; - va_start(args, fmt); - usbi_log_v(ctx, LOG_LEVEL_INFO, "", fmt, args); - va_end(args); -#else - (void)ctx; -#endif +#define LOG_BODY(ctxt, level) \ +{ \ + va_list args; \ + va_start (args, format); \ + usbi_log_v(ctxt, level, "", format, args); \ + va_end(args); \ } - -static inline void usbi_warn(struct libusb_context *ctx, const char *fmt, ...) -{ -#ifdef ENABLE_LOGGING - va_list args; - va_start(args, fmt); - usbi_log_v(ctx, LOG_LEVEL_WARNING, "", fmt, args); - va_end(args); #else - (void)ctx; +#define LOG_BODY(ctxt, level) do { (void)(ctxt); } while(0) #endif -} -static inline void usbi_err(struct libusb_context *ctx, const char *fmt, ...) -{ -#ifdef ENABLE_LOGGING - va_list args; - va_start(args, fmt); - usbi_log_v(ctx, LOG_LEVEL_ERROR, "", fmt, args); - va_end(args); -#else - (void)ctx; -#endif -} +static inline void usbi_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 *fmt, ...) -{ -#ifdef ENABLE_DEBUG_LOGGING - va_list args; - va_start(args, fmt); - usbi_log_v(NULL, LOG_LEVEL_DEBUG, "", fmt, args); - va_end(args); -#else - (void)fmt; -#endif -} +static inline void usbi_dbg(const char *format, ...) + LOG_BODY(NULL,LIBUSB_LOG_LEVEL_DEBUG) #endif /* !defined(_MSC_VER) || _MSC_VER >= 1400 */ @@ -221,32 +210,13 @@ static inline void usbi_dbg(const char *fmt, ...) #define IS_XFERIN(xfer) (0 != ((xfer)->endpoint & LIBUSB_ENDPOINT_IN)) #define IS_XFEROUT(xfer) (!IS_XFERIN(xfer)) -/* Internal abstractions for thread synchronization and poll */ +/* Internal abstraction for thread synchronization */ #if defined(THREADS_POSIX) -#include -#elif defined(OS_WINDOWS) +#include "os/threads_posix.h" +#elif defined(OS_WINDOWS) || defined(OS_WINCE) #include #endif -#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD) -#include -#include -#elif defined(OS_WINDOWS) -#include -#endif - -#if defined(OS_WINDOWS) && !defined(__GCC__) -#undef HAVE_GETTIMEOFDAY -int usbi_gettimeofday(struct timeval *tp, void *tzp); -#define LIBUSB_GETTIMEOFDAY_WIN32 -#define HAVE_USBI_GETTIMEOFDAY -#else -#ifdef HAVE_GETTIMEOFDAY -#define usbi_gettimeofday(tv, tz) gettimeofday((tv), (tz)) -#define HAVE_USBI_GETTIMEOFDAY -#endif -#endif - extern struct libusb_context *usbi_default_context; struct libusb_context { @@ -268,7 +238,7 @@ struct libusb_context { /* A list of registered hotplug callbacks */ struct list_head hotplug_cbs; usbi_mutex_t hotplug_cbs_lock; - int hotplug_pipe[2]; + 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 @@ -308,7 +278,7 @@ struct libusb_context { int timerfd; #endif - struct list_head list; + struct list_head list; }; #ifdef USBI_TIMERFD_AVAILABLE @@ -326,6 +296,8 @@ struct libusb_device { 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; @@ -342,7 +314,7 @@ struct libusb_device { #else [0] /* non-standard, but usually working code */ #endif - ; + ; }; struct libusb_device_handle { @@ -352,13 +324,14 @@ struct libusb_device_handle { 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 { @@ -409,7 +382,7 @@ enum usbi_transfer_flags { /* 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 were updated */ + /* Set by backend submit_transfer() if the fds in use have been updated */ USBI_TRANSFER_UPDATED_FDS = 1 << 4, }; @@ -461,7 +434,26 @@ int usbi_get_config_index_by_value(struct libusb_device *dev, void usbi_connect_device (struct libusb_device *dev); void usbi_disconnect_device (struct libusb_device *dev); -/* polling */ +/* 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 */ @@ -490,7 +482,7 @@ struct discovered_devs { #else [0] /* non-standard, but usually working code */ #endif - ; + ; }; struct discovered_devs *discovered_devs_append( @@ -504,11 +496,14 @@ 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 + * This function is called when a libusbx user initializes the library * prior to use. * * Return 0 on success, or a LIBUSB_ERROR code on failure. @@ -538,7 +533,7 @@ struct usbi_os_backend { * 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 + * usbi_get_device_by_session_id(). This function checks if libusbx already * knows about the device, and if so, it provides you with a libusb_device * structure for it. * @@ -569,18 +564,37 @@ struct usbi_os_backend { * 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 + * without hiccup. This is also a good place to inform libusbx that it * should monitor certain file descriptors related to this device - * see the usbi_add_pollfd() function. * @@ -604,7 +618,7 @@ struct usbi_os_backend { /* 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 + * libusbx 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. @@ -683,13 +697,29 @@ struct usbi_os_backend { 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. + * libusbx to retrieve the information through a standard control transfer. * * This function must be non-blocking. * Return: @@ -866,6 +896,8 @@ struct usbi_os_backend { * * 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 @@ -886,7 +918,7 @@ struct usbi_os_backend { * 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 + * This function might seem a bit out of place. It is used when libusbx * 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. @@ -965,10 +997,11 @@ 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/compat/libusb-1.0/libusb/os/darwin_usb.c b/compat/libusb-1.0/libusb/os/darwin_usb.c index b31e818a..889c62a4 100644 --- a/compat/libusb-1.0/libusb/os/darwin_usb.c +++ b/compat/libusb-1.0/libusb/os/darwin_usb.c @@ -1,7 +1,7 @@ /* -*- Mode: C; indent-tabs-mode:nil -*- */ /* - * darwin backend for libusb 1.0 - * Copyright (C) 2008-2013 Nathan Hjelm + * darwin backend for libusbx 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 @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include "config.h" #include #include #include @@ -52,6 +52,11 @@ 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; @@ -62,7 +67,7 @@ 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, usb_device_t **device, UInt32 locationID); +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) { @@ -95,6 +100,8 @@ static const char *darwin_error_str (int result) { return "physical memory can not be wired down"; case kIOReturnNoResources: return "out of resources"; + case kIOUSBHighSpeedSplitError: + return "high speed split error"; default: return "unknown error"; } @@ -126,6 +133,21 @@ static int darwin_to_libusb (int result) { } } +/* 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_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; @@ -135,7 +157,7 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui int8_t i, iface; - usbi_info (HANDLE_CTX(dev_handle), "converting ep address 0x%02x to pipeRef and interface", ep); + usbi_dbg ("converting ep address 0x%02x to pipeRef and interface", ep); for (iface = 0 ; iface < USB_MAXINTERFACES ; iface++) { cInterface = &priv->interfaces[iface]; @@ -145,7 +167,7 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui if (cInterface->endpoint_addrs[i] == ep) { *pipep = i + 1; *ifcp = iface; - usbi_info (HANDLE_CTX(dev_handle), "pipe %d on interface %d matches", *pipep, *ifcp); + usbi_dbg ("pipe %d on interface %d matches", *pipep, *ifcp); return 0; } } @@ -188,60 +210,59 @@ static int usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 loca return IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, deviceIterator); } -static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32 *locationp) { - io_cf_plugin_ref_t *plugInInterface = NULL; - usb_device_t **device; - io_service_t usbDevice; - long result; - SInt32 score; +/* 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 (!IOIteratorIsValid (deviceIterator)) - return NULL; + if (cfNumber) { + if (CFGetTypeID(cfNumber) == CFNumberGetTypeID()) { + ret = CFNumberGetValue(cfNumber, type, p); + } + CFRelease (cfNumber); + } - while ((usbDevice = IOIteratorNext(deviceIterator))) { - result = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, - kIOCFPlugInInterfaceID, &plugInInterface, - &score); + return ret; +} - /* we are done with the usb_device_t */ - (void)IOObjectRelease(usbDevice); - if (kIOReturnSuccess == result && plugInInterface) - break; +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; - usbi_dbg ("libusb/darwin.c usb_get_next_device: could not set up plugin for service: %s\n", darwin_error_str (result)); - } + result = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, &plugInInterface, + &score); - if (!usbDevice) + 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); - /* get the location from the device */ - if (locationp) - (*(device))->GetLocationID(device, locationp); - return device; } static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) { struct libusb_context *ctx; - usb_device_t **device; - UInt32 location; + io_service_t service; usbi_mutex_lock(&active_contexts_lock); - while ((device = usb_get_next_device (add_devices, &location))) { + 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, device, location); + process_new_device (ctx, service);; } - /* release extra reference */ - (*device)->Release (device); + IOObjectRelease(service); } usbi_mutex_unlock(&active_contexts_lock); @@ -252,40 +273,27 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { struct libusb_context *ctx; io_service_t device; - bool locationValid; - UInt32 location; - CFTypeRef locationCF; + UInt64 session; + int ret; while ((device = IOIteratorNext (rem_devices)) != 0) { /* get the location from the i/o registry */ - locationCF = IORegistryEntryCreateCFProperty (device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0); - + ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session); IOObjectRelease (device); - - if (!locationCF) - continue; - - locationValid = CFGetTypeID(locationCF) == CFNumberGetTypeID() && - CFNumberGetValue(locationCF, kCFNumberSInt32Type, &location); - - CFRelease (locationCF); - - if (!locationValid) + if (!ret) continue; usbi_mutex_lock(&active_contexts_lock); list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { - usbi_dbg ("libusb/darwin.c darwin_devices_detached: notifying context %p of device disconnect", ctx); + usbi_dbg ("notifying context %p of device disconnect", ctx); - dev = usbi_get_device_by_session_id (ctx, location); - if (!dev) { - continue; + 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); } - - /* 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); } usbi_mutex_unlock(&active_contexts_lock); @@ -308,13 +316,11 @@ static void *darwin_event_thread_main (void *arg0) { and crash reports. */ #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 pthread_setname_np ("org.libusb.device-hotplug"); -#endif /* 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 @@ -324,7 +330,7 @@ static void *darwin_event_thread_main (void *arg0) { io_iterator_t libusb_rem_device_iterator; io_iterator_t libusb_add_device_iterator; - usbi_info (ctx, "creating hotplug event source"); + usbi_dbg ("creating hotplug event source"); runloop = CFRunLoopGetCurrent (); CFRetain (runloop); @@ -347,7 +353,7 @@ static void *darwin_event_thread_main (void *arg0) { } /* create notifications for attached devices */ - kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOFirstMatchNotification, + kresult = IOServiceAddMatchingNotification(libusb_notification_port, kIOFirstMatchNotification, IOServiceMatching(kIOUSBDeviceClassName), (IOServiceMatchingCallback)darwin_devices_attached, (void *)ctx, &libusb_add_device_iterator); @@ -362,7 +368,7 @@ static void *darwin_event_thread_main (void *arg0) { darwin_clear_iterator (libusb_rem_device_iterator); darwin_clear_iterator (libusb_add_device_iterator); - usbi_info (ctx, "darwin event thread ready to receive events"); + 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); @@ -373,7 +379,7 @@ static void *darwin_event_thread_main (void *arg0) { /* run the runloop */ CFRunLoopRun(); - usbi_info (ctx, "darwin event thread exiting"); + usbi_dbg ("darwin event thread exiting"); /* remove the notification cfsource */ CFRunLoopRemoveSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode); @@ -392,8 +398,19 @@ static void *darwin_event_thread_main (void *arg0) { pthread_exit (NULL); } +static void _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; + static int initted = 0; int rc; rc = darwin_scan_devices (ctx); @@ -404,6 +421,11 @@ static int darwin_init(struct libusb_context *ctx) { if (OSAtomicIncrement32Barrier(&initCount) == 1) { /* create the clocks that will be used */ + if (!initted) { + initted = 1; + atexit(_darwin_finalize); + } + 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); @@ -417,7 +439,7 @@ static int darwin_init(struct libusb_context *ctx) { pthread_mutex_unlock (&libusb_darwin_at_mutex); } - return 0; + return rc; } static void darwin_exit (void) { @@ -432,7 +454,7 @@ static void darwin_exit (void) { } static int darwin_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) { - struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv; + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); /* return cached copy */ memmove (buffer, &(priv->dev_descriptor), DEVICE_DESC_LENGTH); @@ -443,7 +465,7 @@ static int darwin_get_device_descriptor(struct libusb_device *dev, unsigned char } static int get_configuration_index (struct libusb_device *dev, int config_value) { - struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv; + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); UInt8 i, numConfig; IOUSBConfigurationDescriptorPtr desc; IOReturn kresult; @@ -461,11 +483,11 @@ static int get_configuration_index (struct libusb_device *dev, int config_value) } /* configuration not found */ - return LIBUSB_ERROR_OTHER; + 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_device_priv *priv = (struct darwin_device_priv *)dev->os_priv; + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); int config_index; if (0 == priv->active_config) @@ -479,9 +501,10 @@ static int darwin_get_active_config_descriptor(struct libusb_device *dev, unsign } 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_device_priv *priv = (struct darwin_device_priv *)dev->os_priv; + struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); IOUSBConfigurationDescriptorPtr desc; IOReturn kresult; + int ret; if (!priv || !priv->device) return LIBUSB_ERROR_OTHER; @@ -498,12 +521,16 @@ static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t confi *host_endian = 0; } - return darwin_to_libusb (kresult); + ret = darwin_to_libusb (kresult); + if (ret != LIBUSB_SUCCESS) + return ret; + + return len; } /* 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; +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; @@ -511,14 +538,14 @@ static int darwin_check_configuration (struct libusb_context *ctx, struct libusb io_iterator_t interface_iterator; io_service_t firstInterface; - if (priv->dev_descriptor.bNumConfigurations < 1) { + 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); - priv->first_config = (kIOReturnSuccess == kresult) ? configDesc->bConfigurationValue : 1; + 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 @@ -544,23 +571,23 @@ static int darwin_check_configuration (struct libusb_context *ctx, struct libusb IOObjectRelease (firstInterface); /* device is configured */ - if (priv->dev_descriptor.bNumConfigurations == 1) + if (dev->dev_descriptor.bNumConfigurations == 1) /* to avoid problems with some devices get the configurations value from the configuration descriptor */ - priv->active_config = priv->first_config; + dev->active_config = dev->first_config; else /* devices with more than one configuration should work with GetConfiguration */ - (*darwin_device)->GetConfiguration (darwin_device, &priv->active_config); + (*darwin_device)->GetConfiguration (darwin_device, &dev->active_config); } else /* not configured */ - priv->active_config = 0; + dev->active_config = 0; - usbi_info (ctx, "active config: %u, first config: %u", priv->active_config, priv->first_config); + 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) { - IOUSBDevRequest req; + IOUSBDevRequestTO req; memset (buffer, 0, buffer_size); @@ -571,37 +598,37 @@ static int darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 d req.wIndex = desc_index; req.wLength = buffer_size; req.pData = buffer; + req.noDataTimeout = 20; + req.completionTimeout = 100; - return (*device)->DeviceRequest (device, &req); + return (*device)->DeviceRequestTO (device, &req); } -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 = 2, delay = 30000; +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); - priv = (struct darwin_device_priv *)dev->os_priv; - - /* try to open the device (we can usually continue even if this fails) */ + /* 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); - /**** retrieve device descriptor ****/ do { - /* 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 = darwin_request_descriptor (device, kUSBDeviceDesc, 0, &priv->dev_descriptor, sizeof(priv->dev_descriptor)); + /**** retrieve device descriptor ****/ + ret = darwin_request_descriptor (device, kUSBDeviceDesc, 0, &dev->dev_descriptor, sizeof(dev->dev_descriptor)); - - if (kIOReturnOverrun == ret && kUSBDeviceDesc == priv->dev_descriptor.bDescriptorType) + if (kIOReturnOverrun == ret && kUSBDeviceDesc == dev->dev_descriptor.bDescriptorType) /* received an overrun error but we still received a device descriptor */ ret = kIOReturnSuccess; @@ -610,8 +637,8 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct li break; } - if (kIOReturnSuccess == ret && (0 == priv->dev_descriptor.bNumConfigurations || - 0 == priv->dev_descriptor.bcdUSB)) { + 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..."); @@ -629,16 +656,18 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct li if (kIOReturnSuccess != ret && is_open && try_unsuspend) { /* device may be suspended. unsuspend it and try again */ #if DeviceVersion >= 320 - UInt32 info; + UInt32 info = 0; /* IOUSBFamily 320+ provides a way to detect device suspension but earlier versions do not */ (void)(*device)->GetUSBDeviceInformation (device, &info); - try_unsuspend = info & (1 << kUSBInformationDeviceIsSuspendedBit); + /* note that the device was suspended */ + if (info & (1 << kUSBInformationDeviceIsSuspendedBit) || 0 == info) + try_unsuspend = 1; #endif if (try_unsuspend) { - /* resume the device */ + /* try to unsuspend the device */ ret2 = (*device)->USBDeviceSuspend (device, 0); if (kIOReturnSuccess != ret2) { /* prevent log spew from poorly behaving devices. this indicates the @@ -668,75 +697,179 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct li 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)); + 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. skipping device", idVendor, idProduct, darwin_error_str (ret)); - - return -1; + 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); } - 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) { + 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 (priv->dev_descriptor.idProduct)); - return -1; + idProduct, libusb_le16_to_cpu (dev->dev_descriptor.idProduct)); + return LIBUSB_ERROR_NO_DEVICE; } - return 0; + 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 process_new_device (struct libusb_context *ctx, usb_device_t **device, UInt32 locationID) { - struct libusb_device *dev = NULL; +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\n" 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%x against cached device with sessionID 0x%x", 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%x\n", 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; - UInt16 address; int ret = 0; do { - usbi_info (ctx, "allocating new device for location 0x%08x", locationID); + ret = darwin_get_cached_device (ctx, service, &cached_device); + + if (ret < 0 || !cached_device->can_enumerate) { + return ret; + } - dev = usbi_alloc_device(ctx, locationID); + /* 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%08x", + 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->device = device; - - /* increment the device's reference count (it is decremented in darwin_destroy_device) */ - (*device)->AddRef (device); - (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&address); + priv->dev = cached_device; + darwin_ref_cached_device (priv->dev); - ret = darwin_cache_device_descriptor (ctx, dev, device); - if (ret < 0) - break; - - /* check current active configuration (and cache the first configuration value-- which may be used by claim_interface) */ - ret = darwin_check_configuration (ctx, dev, device); - if (ret < 0) - break; + 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; - dev->bus_number = locationID >> 24; - dev->device_address = address; + /* need to add a reference to the parent device */ + if (dev->parent_dev) { + libusb_ref_device(dev->parent_dev); + } - (*device)->GetDeviceSpeed (device, &devSpeed); + (*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed); switch (devSpeed) { case kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break; @@ -749,16 +882,12 @@ static int process_new_device (struct libusb_context *ctx, usb_device_t **device usbi_warn (ctx, "Got unknown device speed %d", devSpeed); } - /* save our location, we'll need this later */ - priv->location = locationID; - 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); if (ret < 0) break; - usbi_info (ctx, "found device with address %d at %s", dev->device_address, priv->sys_path); + 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) { @@ -771,21 +900,18 @@ static int process_new_device (struct libusb_context *ctx, usb_device_t **device } static int darwin_scan_devices(struct libusb_context *ctx) { - io_iterator_t deviceIterator; - usb_device_t **device; - kern_return_t kresult; - UInt32 location; + 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 ((device = usb_get_next_device (deviceIterator, &location)) != NULL) { - (void) process_new_device (ctx, device, location); + while ((service = IOIteratorNext (deviceIterator))) { + (void) process_new_device (ctx, service); - /* process_new_device added a reference so we need to release the one - from QueryInterface */ - (*device)->Release (device); + IOObjectRelease(service); } IOObjectRelease(deviceIterator); @@ -795,14 +921,14 @@ static int darwin_scan_devices(struct libusb_context *ctx) { 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_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->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_err (HANDLE_CTX (dev_handle), "USBDeviceOpen: %s", darwin_error_str(kresult)); + usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceOpen: %s", darwin_error_str(kresult)); if (kIOReturnExclusiveAccess != kresult) { return darwin_to_libusb (kresult); @@ -845,14 +971,14 @@ static int darwin_open (struct libusb_device_handle *dev_handle) { usbi_add_pollfd(HANDLE_CTX(dev_handle), priv->fds[0], POLLIN); - usbi_info (HANDLE_CTX (dev_handle), "device open for access"); + 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_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); IOReturn kresult; int i; @@ -884,7 +1010,7 @@ static void darwin_close (struct libusb_device_handle *dev_handle) { 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_err (HANDLE_CTX (dev_handle), "USBDeviceClose: %s", darwin_error_str(kresult)); + usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceClose: %s", darwin_error_str(kresult)); } } } @@ -898,7 +1024,7 @@ static void darwin_close (struct libusb_device_handle *dev_handle) { } static int darwin_get_configuration(struct libusb_device_handle *dev_handle, int *config) { - struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); *config = (int) dpriv->active_config; @@ -906,7 +1032,7 @@ static int darwin_get_configuration(struct libusb_device_handle *dev_handle, int } static int darwin_set_configuration(struct libusb_device_handle *dev_handle, int config) { - struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); IOReturn kresult; int i; @@ -934,8 +1060,8 @@ static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_s IOUSBFindInterfaceRequest request; kern_return_t kresult; io_iterator_t interface_iterator; - CFTypeRef bInterfaceNumberCF; - int bInterfaceNumber; + UInt8 bInterfaceNumber; + int ret; *usbInterfacep = IO_OBJECT_NULL; @@ -951,17 +1077,10 @@ static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_s while ((*usbInterfacep = IOIteratorNext(interface_iterator))) { /* find the interface number */ - bInterfaceNumberCF = IORegistryEntryCreateCFProperty (*usbInterfacep, CFSTR("bInterfaceNumber"), - kCFAllocatorDefault, 0); - if (!bInterfaceNumberCF) { - continue; - } - - CFNumberGetValue(bInterfaceNumberCF, kCFNumberIntType, &bInterfaceNumber); - - CFRelease(bInterfaceNumberCF); + ret = get_ioregistry_value_number (*usbInterfacep, CFSTR("bInterfaceNumber"), kCFNumberSInt8Type, + &bInterfaceNumber); - if ((uint8_t) bInterfaceNumber == ifc) { + if (ret && bInterfaceNumber == ifc) { break; } @@ -987,7 +1106,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { u_int16_t dont_care2; int i; - usbi_info (HANDLE_CTX (dev_handle), "building table of endpoints."); + usbi_dbg ("building table of endpoints."); /* retrieve the total number of endpoints on this interface */ kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep); @@ -1007,7 +1126,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { return darwin_to_libusb (kresult); } - usbi_info (HANDLE_CTX (dev_handle), "interface: %i pipe %i: dir: %i number: %i", iface, i, direction, number); + usbi_dbg ("interface: %i pipe %i: dir: %i number: %i", iface, i, direction, number); cInterface->endpoint_addrs[i - 1] = ((direction << 7 & LIBUSB_ENDPOINT_DIR_MASK) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK)); } @@ -1018,7 +1137,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { } static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface) { - struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv; + 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; @@ -1116,7 +1235,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i /* add the cfSource to the async thread's run loop */ CFRunLoopAddSource(libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode); - usbi_info (HANDLE_CTX (dev_handle), "interface opened"); + usbi_dbg ("interface opened"); return 0; } @@ -1143,11 +1262,11 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, int kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface); if (kresult) - usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult)); + usbi_warn (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult)); kresult = (*(cInterface->interface))->Release(cInterface->interface); if (kresult != kIOReturnSuccess) - usbi_err (HANDLE_CTX (dev_handle), "Release: %s", darwin_error_str(kresult)); + usbi_warn (HANDLE_CTX (dev_handle), "Release: %s", darwin_error_str(kresult)); cInterface->interface = IO_OBJECT_NULL; @@ -1197,20 +1316,16 @@ static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned c cInterface = &priv->interfaces[iface]; -#if (InterfaceVersion < 190) - kresult = (*(cInterface->interface))->ClearPipeStall(cInterface->interface, pipeRef); -#else /* newer versions of darwin support clearing additional bits on the device's endpoint */ kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef); -#endif if (kresult) - usbi_err (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (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_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); IOUSBDeviceDescriptor descriptor; IOUSBConfigurationDescriptorPtr cached_configuration; IOUSBConfigurationDescriptor configuration; @@ -1262,7 +1377,7 @@ static int darwin_reset_device(struct libusb_device_handle *dev_handle) { } static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, int interface) { - struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); io_service_t usbInterface; CFTypeRef driver; IOReturn kresult; @@ -1303,11 +1418,12 @@ static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, static void darwin_destroy_device(struct libusb_device *dev) { struct darwin_device_priv *dpriv = (struct darwin_device_priv *) dev->os_priv; - if (dpriv->device) { - /* it is an internal error if the reference count of a device is < 0 after release */ - assert(0 <= (*(dpriv->device))->Release(dpriv->device)); - - dpriv->device = NULL; + 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); } } @@ -1317,7 +1433,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) { IOReturn ret; uint8_t transferType; - /* None of the values below are used in libusb for bulk transfers */ + /* None of the values below are used in libusbx for bulk transfers */ uint8_t direction, number, interval, pipeRef, iface; uint16_t maxPacketSize; @@ -1395,7 +1511,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { return LIBUSB_ERROR_NO_MEM; } - /* copy the frame list from the libusb descriptor (the structures differ only is member order) */ + /* copy the frame list from the libusbx 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; @@ -1422,6 +1538,9 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { 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; @@ -1458,7 +1577,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { 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_device_priv *dpriv = (struct darwin_device_priv *)transfer->dev_handle->dev->os_priv; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev); struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv; struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); @@ -1473,7 +1592,7 @@ static int submit_control_transfer(struct usbi_transfer *itransfer) { 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 */ + /* data is stored after the libusbx control block */ tpriv->req.pData = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; tpriv->req.completionTimeout = transfer->timeout; tpriv->req.noDataTimeout = transfer->timeout; @@ -1524,10 +1643,10 @@ static int darwin_submit_transfer(struct usbi_transfer *itransfer) { static int cancel_control_transfer(struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); - struct darwin_device_priv *dpriv = (struct darwin_device_priv *)transfer->dev_handle->dev->os_priv; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev); IOReturn kresult; - usbi_info (ITRANSFER_CTX (itransfer), "WARNING: aborting all transactions control pipe"); + usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions control pipe"); if (!dpriv->device) return LIBUSB_ERROR_NO_DEVICE; @@ -1539,7 +1658,7 @@ static int cancel_control_transfer(struct usbi_transfer *itransfer) { static int darwin_abort_transfers (struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); - struct darwin_device_priv *dpriv = (struct darwin_device_priv *)transfer->dev_handle->dev->os_priv; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev); struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv; struct darwin_interface *cInterface; uint8_t pipeRef, iface; @@ -1556,20 +1675,15 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) { if (!dpriv->device) return LIBUSB_ERROR_NO_DEVICE; - usbi_info (ITRANSFER_CTX (itransfer), "WARNING: aborting all transactions on interface %d pipe %d", iface, pipeRef); + usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions on interface %d pipe %d", iface, pipeRef); /* abort transactions */ (*(cInterface->interface))->AbortPipe (cInterface->interface, pipeRef); - usbi_info (ITRANSFER_CTX (itransfer), "calling clear pipe stall to clear the data toggle bit"); + usbi_dbg ("calling clear pipe stall to clear the data toggle bit"); - /* clear the data toggle bit */ -#if (InterfaceVersion < 190) - kresult = (*(cInterface->interface))->ClearPipeStall(cInterface->interface, pipeRef); -#else /* newer versions of darwin support clearing additional bits on the device's endpoint */ kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef); -#endif return darwin_to_libusb (kresult); } @@ -1607,7 +1721,7 @@ static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) struct darwin_msg_async_io_complete message = {.itransfer = itransfer, .result = result, .size = (UInt32) (uintptr_t) arg0}; - usbi_info (ITRANSFER_CTX (itransfer), "an async io operation has completed"); + 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) { @@ -1635,17 +1749,17 @@ static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_ case kIOReturnAborted: return LIBUSB_TRANSFER_CANCELLED; case kIOUSBPipeStalled: - usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: pipe is stalled"); + usbi_dbg ("transfer error: pipe is stalled"); return LIBUSB_TRANSFER_STALL; case kIOReturnOverrun: - usbi_err (ITRANSFER_CTX (itransfer), "transfer error: data overrun"); + usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: data overrun"); return LIBUSB_TRANSFER_OVERFLOW; case kIOUSBTransactionTimeout: - usbi_err (ITRANSFER_CTX (itransfer), "transfer error: timed out"); + usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: timed out"); itransfer->flags |= USBI_TRANSFER_TIMED_OUT; return LIBUSB_TRANSFER_TIMED_OUT; default: - usbi_err (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result); + usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result); return LIBUSB_TRANSFER_ERROR; } } @@ -1664,7 +1778,7 @@ static void darwin_handle_callback (struct usbi_transfer *itransfer, kern_return return; } - usbi_info (ITRANSFER_CTX (itransfer), "handling %s completion with kernel status %d", + usbi_dbg ("handling %s completion with kernel status %d", isControl ? "control" : isBulk ? "bulk" : isIsoc ? "isoc" : "interrupt", result); if (kIOReturnSuccess == result || kIOReturnUnderrun == result) { @@ -1694,7 +1808,7 @@ static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL for (i = 0; i < nfds && num_ready > 0; i++) { struct pollfd *pollfd = &fds[i]; - usbi_info (ctx, "checking fd %i with revents = %x", pollfd->fd, pollfd->revents); + usbi_dbg ("checking fd %i with revents = %x", pollfd->fd, pollfd->revents); if (!pollfd->revents) continue; @@ -1748,6 +1862,7 @@ static int darwin_clock_gettime(int clk_id, struct timespec *tp) { const struct usbi_os_backend darwin_backend = { .name = "Darwin", + .caps = 0, .init = darwin_init, .exit = darwin_exit, .get_device_list = NULL, /* not needed */ @@ -1785,4 +1900,3 @@ const struct usbi_os_backend darwin_backend = { .transfer_priv_size = sizeof(struct darwin_transfer_priv), .add_iso_packet_size = 0, }; - diff --git a/compat/libusb-1.0/libusb/os/darwin_usb.h b/compat/libusb-1.0/libusb/os/darwin_usb.h index 368dec25..53b8542b 100644 --- a/compat/libusb-1.0/libusb/os/darwin_usb.h +++ b/compat/libusb-1.0/libusb/os/darwin_usb.h @@ -1,6 +1,6 @@ /* - * darwin backend for libusb 1.0 - * Copyright (C) 2008-2013 Nathan Hjelm + * darwin backend for libusbx 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 @@ -28,7 +28,19 @@ #include /* IOUSBInterfaceInferface */ -#if defined (kIOUSBInterfaceInterfaceID300) +#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 @@ -46,24 +58,6 @@ #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220 #define InterfaceVersion 220 -#elif defined (kIOUSBInterfaceInterfaceID197) - -#define usb_interface_t IOUSBInterfaceInterface197 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID197 -#define InterfaceVersion 197 - -#elif defined (kIOUSBInterfaceInterfaceID190) - -#define usb_interface_t IOUSBInterfaceInterface190 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID190 -#define InterfaceVersion 190 - -#elif defined (kIOUSBInterfaceInterfaceID182) - -#define usb_interface_t IOUSBInterfaceInterface182 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID182 -#define InterfaceVersion 182 - #else #error "IOUSBFamily is too old. Please upgrade your OS" @@ -95,24 +89,11 @@ #define DeviceInterfaceID kIOUSBDeviceInterfaceID245 #define DeviceVersion 245 -#elif defined (kIOUSBDeviceInterfaceID197) - +#elif defined (kIOUSBDeviceInterfaceID220) #define usb_device_t IOUSBDeviceInterface197 #define DeviceInterfaceID kIOUSBDeviceInterfaceID197 #define DeviceVersion 197 -#elif defined (kIOUSBDeviceInterfaceID187) - -#define usb_device_t IOUSBDeviceInterface187 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID187 -#define DeviceVersion 187 - -#elif defined (kIOUSBDeviceInterfaceID182) - -#define usb_device_t IOUSBDeviceInterface182 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID182 -#define DeviceVersion 182 - #else #error "IOUSBFamily is too old. Please upgrade your OS" @@ -127,13 +108,23 @@ typedef IOCFPlugInInterface *io_cf_plugin_ref_t; typedef IONotificationPortRef io_notification_port_t; /* private structures */ -struct darwin_device_priv { +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; + 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 { @@ -156,11 +147,7 @@ struct darwin_transfer_priv { int num_iso_packets; /* Control */ -#if !defined (LIBUSB_NO_TIMEOUT_DEVICE) IOUSBDevRequestTO req; -#else - IOUSBDevRequest req; -#endif /* Bulk */ }; diff --git a/compat/libusb-1.0/libusb/os/linux_netlink.c b/compat/libusb-1.0/libusb/os/linux_netlink.c index 20c652f8..f1c1be1b 100644 --- a/compat/libusb-1.0/libusb/os/linux_netlink.c +++ b/compat/libusb-1.0/libusb/os/linux_netlink.c @@ -21,6 +21,10 @@ */ #include "config.h" +#include "libusb.h" +#include "libusbi.h" +#include "linux_usbfs.h" + #include #include #include @@ -30,46 +34,113 @@ #include #include #include + +#ifdef HAVE_ASM_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H #include -#include +#endif -#include "libusb.h" -#include "libusbi.h" -#include "linux_usbfs.h" +#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; - linux_netlink_socket = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT); +#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; } @@ -79,22 +150,30 @@ int linux_netlink_start_event_monitor(void) 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; } - r = close(linux_netlink_socket); - if (0 > r) { - usbi_err(NULL, "error closing netlink socket. %s", strerror(errno)); - return LIBUSB_ERROR_OTHER; + /* 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); - pthread_cancel(libusb_linux_event_thread); - + 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; } @@ -127,10 +206,12 @@ static int linux_netlink_parse(char *buffer, size_t len, int *detached, const ch *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"); + usbi_dbg("unknown device action %s", tmp); return -1; } @@ -180,52 +261,85 @@ static int linux_netlink_parse(char *buffer, size_t len, int *detached, const ch return 0; } -static void *linux_netlink_event_thread_main(void *arg) +static int linux_netlink_read_message(void) { - struct pollfd fds = {.fd = linux_netlink_socket, - .events = POLLIN}; 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; - /* silence compiler warning */ - (void) arg; + /* 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; + } - while (1 == poll(&fds, 1, -1)) { - const char *sys_name = NULL; + /* TODO -- authenticate this message is from the kernel or udevd */ - if (POLLIN != fds.revents) { - break; - } + r = linux_netlink_parse(buffer, len, &detached, &sys_name, + &busnum, &devaddr); + if (r) + return r; - /* read netlink message */ - memset(buffer, 0, sizeof(buffer)); - len = recvmsg(linux_netlink_socket, &meh, 0); - if (len < 32) { - usbi_dbg("error recieving message from netlink"); - continue; - } + usbi_dbg("netlink hotplug found device busnum: %hhu, devaddr: %hhu, sys_name: %s, removed: %s", + busnum, devaddr, sys_name, detached ? "yes" : "no"); - /* TODO -- authenticate this message is from the kernel or udevd */ + /* 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); - r = linux_netlink_parse(buffer, len, &detached, &sys_name, - &busnum, &devaddr); - if (r) - continue; + return 0; +} - usbi_dbg("netlink hotplug found device busnum: %hhu, devaddr: %hhu, sys_name: %s, removed: %s", - busnum, devaddr, sys_name, detached ? "yes" : "no"); +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 }, + }; - /* signal device is available (or not) to all contexts */ - if (detached) - linux_hotplug_disconnected(busnum, devaddr, sys_name); - else - linux_hotplug_enumerate(busnum, devaddr, sys_name); + /* 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; + 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/compat/libusb-1.0/libusb/os/linux_udev.c b/compat/libusb-1.0/libusb/os/linux_udev.c index abe1e667..99ac9434 100644 --- a/compat/libusb-1.0/libusb/os/linux_udev.c +++ b/compat/libusb-1.0/libusb/os/linux_udev.c @@ -20,7 +20,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include "config.h" + +#include #include #include #include @@ -44,84 +46,148 @@ /* 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(void); +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; - if (NULL == udev_ctx) { - udev_ctx = udev_new(); - if (!udev_ctx) { - return LIBUSB_ERROR_OTHER; - } + assert(udev_ctx == NULL); + udev_ctx = udev_new(); + if (!udev_ctx) { + usbi_err(NULL, "could not create udev context"); + return LIBUSB_ERROR_OTHER; + } + + 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; } - udev_monitor = udev_monitor_new_from_netlink(udev_ctx, "udev"); - if (!udev_monitor) { - usbi_err(NULL, "could not initialize udev monitor"); - return LIBUSB_ERROR_OTHER; - } + 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; + } - 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"); - return LIBUSB_ERROR_OTHER; - } + 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; + } - if (udev_monitor_enable_receiving(udev_monitor)) { - usbi_err(NULL, "failed to enable the udev monitor"); - return LIBUSB_ERROR_OTHER; - } + r = usbi_pipe(udev_control_pipe); + if (r) { + usbi_err(NULL, "could not create udev control pipe"); + goto err_free_monitor; + } - udev_monitor_fd = udev_monitor_get_fd(udev_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; + } - pthread_create(&linux_event_thread, NULL, linux_udev_event_thread_main, NULL); + return LIBUSB_SUCCESS; - 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); + udev_ctx = NULL; + return LIBUSB_ERROR_OTHER; } int linux_udev_stop_event_monitor(void) { - if (-1 == udev_monitor_fd) { - /* this should never happen */ - return LIBUSB_ERROR_OTHER; - } + char dummy = 1; + int r; - /* Cancel the event thread. This is the only way to garauntee the thread - exits since closing the monitor fd won't necessarily cause poll - to return. */ - pthread_cancel(linux_event_thread); + 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_unref(udev_monitor); udev_monitor = NULL; - udev_monitor_fd = -1; + udev_monitor_fd = -1; /* Clean up the udev context */ udev_unref(udev_ctx); udev_ctx = NULL; - return LIBUSB_SUCCESS; + /* 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 __attribute__((unused)) *arg) +static void *linux_udev_event_thread_main(void *arg) { - struct pollfd fds = {.fd = udev_monitor_fd, - .events = POLLIN}; + 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 (1 == poll(&fds, 1, -1)) { - if (NULL == udev_monitor || POLLIN != fds.revents) { + 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; } - - udev_hotplug_event(); + 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"); @@ -144,30 +210,19 @@ static int udev_device_info(struct libusb_context *ctx, int detached, return LIBUSB_ERROR_OTHER; } - return linux_get_device_address(ctx, detached, busnum, devaddr, + return linux_get_device_address(ctx, detached, busnum, devaddr, dev_node, *sys_name); } -static void udev_hotplug_event(void) +static void udev_hotplug_event(struct udev_device* udev_dev) { - struct udev_device* udev_dev; const char* udev_action; const char* sys_name = NULL; uint8_t busnum = 0, devaddr = 0; int detached; int r; - if (NULL == udev_monitor) { - return; - } - do { - udev_dev = udev_monitor_receive_device(udev_monitor); - if (!udev_dev) { - usbi_err(NULL, "failed to read data from udev monitor socket."); - return; - } - udev_action = udev_device_get_action(udev_dev); if (!udev_action) { break; @@ -185,7 +240,7 @@ static void udev_hotplug_event(void) if (strncmp(udev_action, "add", 3) == 0) { linux_hotplug_enumerate(busnum, devaddr, sys_name); } else if (detached) { - linux_hotplug_disconnected(busnum, devaddr, sys_name); + linux_device_disconnected(busnum, devaddr, sys_name); } else { usbi_err(NULL, "ignoring udev action %s", udev_action); } @@ -202,12 +257,7 @@ int linux_udev_scan_devices(struct libusb_context *ctx) const char *sys_name; int r; - if (NULL == udev_ctx) { - udev_ctx = udev_new(); - if (!udev_ctx) { - return LIBUSB_ERROR_OTHER; - } - } + assert(udev_ctx != NULL); enumerator = udev_enumerate_new(udev_ctx); if (NULL == enumerator) { @@ -237,6 +287,20 @@ int linux_udev_scan_devices(struct libusb_context *ctx) udev_enumerate_unref(enumerator); - return LIBUSB_SUCCESS; + 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/compat/libusb-1.0/libusb/os/linux_usbfs.c b/compat/libusb-1.0/libusb/os/linux_usbfs.c index 57603e60..142fa2b4 100644 --- a/compat/libusb-1.0/libusb/os/linux_usbfs.c +++ b/compat/libusb-1.0/libusb/os/linux_usbfs.c @@ -1,9 +1,10 @@ /* -*- 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 + * Linux usbfs backend for libusbx + * 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 @@ -20,7 +21,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include "config.h" + +#include #include #include #include @@ -45,23 +48,23 @@ * * 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 was added in 2.6.23. - * - The "busnum" file was added in 2.6.22 + * - 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. - * We assume this is the case if we see the busnum file (indicates 2.6.22+). * The busnum file is important as that is the only way we can relate sysfs * devices to usbfs nodes. * - * If we also have descriptors, we can obtain the device descriptor and active + * If we also have all descriptors, we can obtain the device descriptor and * configuration without touching usbfs at all. - * - * The descriptors file originally only contained the active configuration - * descriptor alongside the device descriptor, but all configurations are - * included as of Linux 2.6.26. */ /* endianness for multi-byte fields: @@ -69,9 +72,11 @@ * 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. */ -const char *usbfs_path = NULL; +static const char *usbfs_path = NULL; /* use usbdev*.* device names in /dev instead of the usbfs bus directories */ static int usbdev_names = 0; @@ -103,22 +108,29 @@ static int supports_flag_zero_packet = -1; * systems. appropriate choice made at initialization time. */ static clockid_t monotonic_clkid = -1; -/* do we have a busnum to relate devices? this also implies that we can read +/* 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 = 0; +static int sysfs_can_relate_devices = -1; -/* do we have a descriptors file? */ -static int sysfs_has_descriptors = 0; +/* 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 volatile int init_count = 0; -/* lock for init_count */ -static pthread_mutex_t hotplug_lock = PTHREAD_MUTEX_INITIALIZER; +/* 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); @@ -126,8 +138,9 @@ static int linux_default_scan_devices (struct libusb_context *ctx); struct linux_device_priv { char *sysfs_dir; - unsigned char *dev_descriptor; - unsigned char *config_descriptor; + unsigned char *descriptors; + int descriptors_len; + int active_config; /* cache val for !sysfs_can_relate_devices */ }; struct linux_device_handle_priv { @@ -166,14 +179,36 @@ struct linux_transfer_priv { int iso_packet_offset; }; -static void _get_usbfs_path(struct libusb_device *dev, char *path) +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; + 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 (!silent) { + usbi_err(ctx, "libusbx couldn't open USB device %s: %s", + path, strerror(errno)); + if (errno == EACCES && mode == O_RDWR) + usbi_err(ctx, "libusbx 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) @@ -316,22 +351,6 @@ static int kernel_version_ge(int major, int minor, int sublevel) return ksublevel >= sublevel; } -/* 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 stat statbuf; @@ -370,137 +389,111 @@ static int op_init(struct libusb_context *ctx) if (supports_flag_zero_packet) usbi_dbg("zero length packet flag supported"); - 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; + 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; } + } - /* 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; + 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; } - closedir(devices); + } - /* Only use sysfs descriptors if the rest of - sysfs will work for libusb. */ - if (!sysfs_can_relate_devices) + 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; - } else { - usbi_dbg("sysfs usb info not available"); - sysfs_has_descriptors = 0; - sysfs_can_relate_devices = 0; - } - - pthread_mutex_lock(&hotplug_lock); - if (!init_count++) { - /* start up hotplug event handler */ - r = linux_start_event_monitor(); - if (LIBUSB_SUCCESS != r) { - usbi_err(ctx, "error starting hotplug event monitor"); - return r; - } - } - pthread_mutex_unlock(&hotplug_lock); - - r = linux_scan_devices(ctx); - if (LIBUSB_SUCCESS != r) { - return r; - } + } + } + + 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) { - if (!init_count) { - /* should not happen */ - return; - } - - pthread_mutex_lock(&hotplug_lock); - if (!--init_count) { - /* tear down event handler */ - (void)linux_stop_event_monitor(); - } - pthread_mutex_unlock(&hotplug_lock); + 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(); + return linux_udev_start_event_monitor(); #else - return linux_netlink_start_event_monitor(); + return linux_netlink_start_event_monitor(); #endif } static int linux_stop_event_monitor(void) { #if defined(USE_UDEV) - return linux_udev_stop_event_monitor(); + return linux_udev_stop_event_monitor(); #else - return linux_netlink_stop_event_monitor(); + 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) - return linux_udev_scan_devices(ctx); + ret = linux_udev_scan_devices(ctx); #else - return linux_default_scan_devices(ctx); + ret = linux_default_scan_devices(ctx); #endif + + usbi_mutex_static_unlock(&linux_hotplug_lock); + + return ret; } -static int usbfs_get_device_descriptor(struct libusb_device *dev, - unsigned char *buffer) +static void op_hotplug_poll(void) { - struct linux_device_priv *priv = _device_priv(dev); - - /* return cached copy */ - memcpy(buffer, priv->dev_descriptor, DEVICE_DESC_LENGTH); - return 0; +#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) @@ -556,53 +549,14 @@ static int __read_sysfs_attr(struct libusb_context *ctx, return value; } -static int sysfs_get_device_descriptor(struct libusb_device *dev, - unsigned char *buffer) -{ - int fd; - ssize_t r; - - /* sysfs provides access to an in-memory copy of the device descriptor, - * so we use that rather than keeping our own copy */ - - fd = _open_sysfs_attr(dev, "descriptors"); - if (fd < 0) - return fd; - - r = read(fd, buffer, DEVICE_DESC_LENGTH);; - close(fd); - if (r < 0) { - usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d", fd, errno); - return LIBUSB_ERROR_IO; - } else if (r < DEVICE_DESC_LENGTH) { - usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, DEVICE_DESC_LENGTH); - return LIBUSB_ERROR_IO; - } - - return 0; -} - static int op_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) -{ - if (sysfs_has_descriptors) { - *host_endian = 0; - return sysfs_get_device_descriptor(dev, buffer); - } else { - *host_endian = 1; - return usbfs_get_device_descriptor(dev, buffer); - } -} - -static int usbfs_get_active_config_descriptor(struct libusb_device *dev, - unsigned char *buffer, size_t len) { struct linux_device_priv *priv = _device_priv(dev); - if (!priv->config_descriptor) - return LIBUSB_ERROR_NOT_FOUND; /* device is unconfigured */ - /* retrieve cached copy */ - memcpy(buffer, priv->config_descriptor, len); + *host_endian = sysfs_has_descriptors ? 0 : 1; + memcpy(buffer, priv->descriptors, DEVICE_DESC_LENGTH); + return 0; } @@ -649,294 +603,220 @@ static int sysfs_get_active_config(struct libusb_device *dev, int *config) return 0; } -/* takes a usbfs/descriptors fd seeked to the start of a configuration, and - * seeks to the next one. */ -static int seek_to_next_config(struct libusb_context *ctx, int fd, - int host_endian) +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) { - struct libusb_config_descriptor config; - unsigned char tmp[6]; - off_t off; - ssize_t r; + int sysfs_attr; - /* read first 6 bytes of descriptor */ - r = read(fd, tmp, sizeof(tmp)); - if (r < 0) { - usbi_err(ctx, "read failed ret=%d errno=%d", r, errno); - return LIBUSB_ERROR_IO; - } else if (r < (ssize_t)sizeof(tmp)) { - usbi_err(ctx, "short descriptor read %d/%d", r, sizeof(tmp)); - return LIBUSB_ERROR_IO; - } + 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; + } - /* seek forward to end of config */ - usbi_parse_descriptor(tmp, "bbwbb", &config, host_endian); - off = lseek(fd, config.wTotalLength - sizeof(tmp), SEEK_CUR); - if (off < 0) { - usbi_err(ctx, "seek failed ret=%d errno=%d", off, errno); - return LIBUSB_ERROR_IO; + /* 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; } - return 0; -} + usbi_dbg("scan %s", sys_name); -static int sysfs_get_active_config_descriptor(struct libusb_device *dev, - unsigned char *buffer, size_t len) -{ - int fd; - ssize_t r; - off_t off; - int to_copy; - int config; - unsigned char tmp[6]; + 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; - r = sysfs_get_active_config(dev, &config); - if (r < 0) - return r; - if (config == -1) - return LIBUSB_ERROR_NOT_FOUND; + 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; - usbi_dbg("active configuration %d", config); + *devaddr = (uint8_t) sysfs_attr; - /* sysfs provides access to an in-memory copy of the device descriptor, - * so we use that rather than keeping our own copy */ + usbi_dbg("bus=%d dev=%d", *busnum, *devaddr); - fd = _open_sysfs_attr(dev, "descriptors"); - if (fd < 0) - return fd; + return LIBUSB_SUCCESS; +} - /* device might have been unconfigured since we read bConfigurationValue, - * so first check that there is any config descriptor data at all... */ - off = lseek(fd, 0, SEEK_END); - if (off < 1) { - usbi_err(DEVICE_CTX(dev), "end seek failed, ret=%d errno=%d", - off, errno); - close(fd); - return LIBUSB_ERROR_IO; - } else if (off == DEVICE_DESC_LENGTH) { - close(fd); - return LIBUSB_ERROR_NOT_FOUND; - } +/* 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; - off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET); - if (off < 0) { - usbi_err(DEVICE_CTX(dev), "seek failed, ret=%d errno=%d", off, errno); - close(fd); - return LIBUSB_ERROR_IO; - } + for (i = 0; size >= 0; i += header.bLength, size -= header.bLength) { + if (size == 0) + return LIBUSB_ERROR_NOT_FOUND; - /* unbounded loop: we expect the descriptor to be present under all - * circumstances */ - while (1) { - r = read(fd, tmp, sizeof(tmp)); - if (r < 0) { - usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d", - fd, errno); - return LIBUSB_ERROR_IO; - } else if (r < (ssize_t)sizeof(tmp)) { - usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, sizeof(tmp)); + if (size < 2) { + usbi_err(ctx, "short descriptor read %d/2", size); return LIBUSB_ERROR_IO; } + usbi_parse_descriptor(buffer + i, "bb", &header, 0); - /* check bConfigurationValue */ - if (tmp[5] == config) - break; + if (i && header.bDescriptorType == descriptor_type) + return i; + } + usbi_err(ctx, "bLength overflow by %d bytes", -size); + return LIBUSB_ERROR_IO; +} - /* try the next descriptor */ - off = lseek(fd, 0 - sizeof(tmp), SEEK_CUR); - if (off < 0) - 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; - r = seek_to_next_config(DEVICE_CTX(dev), fd, 0); - if (r < 0) - return r; + 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; } - to_copy = (len < sizeof(tmp)) ? len : sizeof(tmp); - memcpy(buffer, tmp, to_copy); - if (len > sizeof(tmp)) { - r = read(fd, buffer + sizeof(tmp), len - sizeof(tmp)); - if (r < 0) { - usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d", - fd, errno); - r = LIBUSB_ERROR_IO; - } else if (r == 0) { - usbi_dbg("device is unconfigured"); - r = LIBUSB_ERROR_NOT_FOUND; - } else if ((size_t)r < len - sizeof(tmp)) { - usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, len); - r = LIBUSB_ERROR_IO; - } - } else { - r = 0; + 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; } - close(fd); - return r; + /* + * 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; + } } -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) +static int op_get_config_descriptor_by_value(struct libusb_device *dev, + uint8_t value, unsigned char **buffer, int *host_endian) { - int retbus, retdev; - - 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); + 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; - *busnum = retbus = __read_sysfs_attr(ctx, sys_name, "busnum"); - if (retbus < 0) - return retbus; - - *devaddr = retdev = __read_sysfs_attr(ctx, sys_name, "devnum"); - if (retdev < 0) - return retdev; + *buffer = NULL; + /* Unlike the device desc. config descs. are always in raw format */ + *host_endian = 0; - usbi_dbg("bus=%d dev=%d", *busnum, *devaddr); - if (retbus > 255 || retdev > 255) - return LIBUSB_ERROR_INVALID_PARAM; + /* Skip device header */ + descriptors += DEVICE_DESC_LENGTH; + size -= DEVICE_DESC_LENGTH; - return LIBUSB_SUCCESS; + /* 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) { - *host_endian = *host_endian; - if (sysfs_has_descriptors) { - return sysfs_get_active_config_descriptor(dev, buffer, len); - } else { - return usbfs_get_active_config_descriptor(dev, buffer, len); - } -} + int r, config; + unsigned char *config_desc; -/* takes a usbfs fd, attempts to find the requested config and copy a certain - * amount of it into an output buffer. */ -static int get_config_descriptor(struct libusb_context *ctx, int fd, - uint8_t config_index, unsigned char *buffer, size_t len) -{ - off_t off; - ssize_t r; - - off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET); - if (off < 0) { - usbi_err(ctx, "seek failed ret=%d errno=%d", off, errno); - return LIBUSB_ERROR_IO; - } - - /* might need to skip some configuration descriptors to reach the - * requested configuration */ - while (config_index > 0) { - r = seek_to_next_config(ctx, fd, 1); + if (sysfs_can_relate_devices) { + r = sysfs_get_active_config(dev, &config); if (r < 0) return r; - config_index--; + } else { + /* Use cached bConfigurationValue */ + struct linux_device_priv *priv = _device_priv(dev); + config = priv->active_config; } + if (config == -1) + return LIBUSB_ERROR_NOT_FOUND; - /* read the rest of the descriptor */ - r = read(fd, buffer, len); - if (r < 0) { - usbi_err(ctx, "read failed ret=%d errno=%d", r, errno); - return LIBUSB_ERROR_IO; - } else if ((size_t)r < len) { - usbi_err(ctx, "short output read %d/%d", r, len); - return LIBUSB_ERROR_IO; - } + r = op_get_config_descriptor_by_value(dev, config, &config_desc, + host_endian); + if (r < 0) + return r; - return 0; + 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) { - char filename[PATH_MAX]; - int fd; - int r; - - *host_endian = *host_endian; - /* always read from usbfs: sysfs only has the active descriptor - * this will involve waking the device up, but oh well! */ - - /* FIXME: the above is no longer true, new kernels have all descriptors - * in the descriptors file. but its kinda hard to detect if the kernel - * is sufficiently new. */ - - _get_usbfs_path(dev, filename); - 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; - } + struct linux_device_priv *priv = _device_priv(dev); + unsigned char *descriptors = priv->descriptors; + int i, r, size = priv->descriptors_len; - r = get_config_descriptor(DEVICE_CTX(dev), fd, config_index, buffer, len); - close(fd); - return r; -} + /* Unlike the device desc. config descs. are always in raw format */ + *host_endian = 0; -/* cache the active config descriptor in memory. a value of -1 means that - * we aren't sure which one is active, so just assume the first one. - * only for usbfs. */ -static int cache_active_config(struct libusb_device *dev, int fd, - int active_config) -{ - struct linux_device_priv *priv = _device_priv(dev); - struct libusb_config_descriptor config; - unsigned char tmp[8]; - unsigned char *buf; - int idx; - int r; + /* Skip device header */ + descriptors += DEVICE_DESC_LENGTH; + size -= DEVICE_DESC_LENGTH; - if (active_config == -1) { - idx = 0; - } else { - r = usbi_get_config_index_by_value(dev, active_config, &idx); + /* 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 (idx == -1) - return LIBUSB_ERROR_NOT_FOUND; - } - - r = get_config_descriptor(DEVICE_CTX(dev), fd, idx, tmp, sizeof(tmp)); - if (r < 0) { - usbi_err(DEVICE_CTX(dev), "first read error %d", r); - return r; - } - - usbi_parse_descriptor(tmp, "bbw", &config, 0); - buf = malloc(config.wTotalLength); - if (!buf) - return LIBUSB_ERROR_NO_MEM; - - r = get_config_descriptor(DEVICE_CTX(dev), fd, idx, buf, - config.wTotalLength); - if (r < 0) { - free(buf); - return r; + if (i == config_index) + break; + size -= r; + descriptors += r; } - if (priv->config_descriptor) - free(priv->config_descriptor); - priv->config_descriptor = buf; - return 0; + len = MIN(len, r); + memcpy(buffer, descriptors, len); + return len; } /* send a control message to retrieve active configuration */ @@ -973,11 +853,9 @@ 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); - unsigned char *dev_buf; - char path[PATH_MAX]; + struct libusb_context *ctx = DEVICE_CTX(dev); + int descriptors_size = 512; /* Begin with a 1024 byte alloc */ int fd, speed; - int active_config = 0; - int device_configured = 1; ssize_t r; dev->bus_number = busnum; @@ -1004,109 +882,160 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum, } } + /* cache descriptors in memory */ if (sysfs_has_descriptors) - return 0; - - /* cache device descriptor in memory so that we can retrieve it later - * without waking the device up (op_get_device_descriptor) */ + fd = _open_sysfs_attr(dev, "descriptors"); + else + fd = _get_usbfs_fd(dev, O_RDONLY, 0); + if (fd < 0) + return fd; - priv->dev_descriptor = NULL; - priv->config_descriptor = NULL; + 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 (sysfs_can_relate_devices) { - int tmp = sysfs_get_active_config(dev, &active_config); - if (tmp < 0) - return tmp; - if (active_config == -1) - device_configured = 0; + if (priv->descriptors_len < DEVICE_DESC_LENGTH) { + usbi_err(ctx, "short descriptor read (%d)", + priv->descriptors_len); + return LIBUSB_ERROR_IO; } - _get_usbfs_path(dev, path); - fd = open(path, O_RDWR); - if (fd < 0 && errno == EACCES) { - fd = open(path, O_RDONLY); - /* if we only have read-only access to the device, we cannot - * send a control message to determine the active config. just - * assume the first one is active. */ - active_config = -1; - } + if (sysfs_can_relate_devices) + return LIBUSB_SUCCESS; + /* cache active config */ + fd = _get_usbfs_fd(dev, O_RDWR, 1); if (fd < 0) { - usbi_err(DEVICE_CTX(dev), "open failed, ret=%d errno=%d", fd, errno); - return LIBUSB_ERROR_IO; + /* 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; } - if (!sysfs_can_relate_devices) { - if (active_config == -1) { - /* if we only have read-only access to the device, we cannot - * send a control message to determine the active config. just - * assume the first one is active. */ - usbi_warn(DEVICE_CTX(dev), "access to %s is read-only; cannot " - "determine active configuration descriptor", path); - } else { - active_config = usbfs_get_active_config(dev, fd); - if (active_config == LIBUSB_ERROR_IO) { - /* buggy devices sometimes fail to report their active config. - * assume unconfigured and continue the probing */ - usbi_warn(DEVICE_CTX(dev), "couldn't query active " - "configuration, assumung unconfigured"); - device_configured = 0; - } else if (active_config < 0) { - close(fd); - return active_config; - } else if (active_config == 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"); - device_configured = 0; - } + 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; } } - dev_buf = malloc(DEVICE_DESC_LENGTH); - if (!dev_buf) { - close(fd); - 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); - r = read(fd, dev_buf, DEVICE_DESC_LENGTH); - if (r < 0) { - usbi_err(DEVICE_CTX(dev), - "read descriptor failed ret=%d errno=%d", fd, errno); - free(dev_buf); - close(fd); - return LIBUSB_ERROR_IO; - } else if (r < DEVICE_DESC_LENGTH) { - usbi_err(DEVICE_CTX(dev), "short descriptor read (%d)", r); - free(dev_buf); - close(fd); - return LIBUSB_ERROR_IO; + 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; } - /* bit of a hack: set num_configurations now because cache_active_config() - * calls usbi_get_config_index_by_value() which uses it */ - dev->num_configurations = dev_buf[DEVICE_DESC_LENGTH - 1]; + usbi_dbg("Dev %p (%s) has parent %p (%s) port %d", dev, sysfs_dir, + dev->parent_dev, parent_sysfs_dir, dev->port_number); - if (device_configured) { - r = cache_active_config(dev, fd, active_config); - if (r < 0) { - close(fd); - free(dev_buf); - return r; - } - } + free (parent_sysfs_dir); - close(fd); - priv->dev_descriptor = dev_buf; - return 0; + return LIBUSB_SUCCESS; } int linux_enumerate_device(struct libusb_context *ctx, - uint8_t busnum, uint8_t devaddr, - const char *sysfs_dir) + uint8_t busnum, uint8_t devaddr, const char *sysfs_dir) { unsigned long session_id; struct libusb_device *dev; @@ -1119,6 +1048,12 @@ int linux_enumerate_device(struct libusb_context *ctx, usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr, session_id); + if (usbi_get_device_by_session_id(ctx, session_id)) { + /* device already exists in the context */ + usbi_dbg("session_id %ld already exists", session_id); + return LIBUSB_SUCCESS; + } + usbi_dbg("allocating new device for %d/%d (session %ld)", busnum, devaddr, session_id); dev = usbi_alloc_device(ctx, session_id); @@ -1131,6 +1066,10 @@ int linux_enumerate_device(struct libusb_context *ctx, 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); @@ -1144,30 +1083,29 @@ void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_na { struct libusb_context *ctx; + usbi_mutex_static_lock(&active_contexts_lock); list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { - if (usbi_get_device_by_session_id(ctx, busnum << 8 | devaddr)) { - /* device already exists in the context */ - usbi_dbg("device already exists in context"); - continue; - } - linux_enumerate_device(ctx, busnum, devaddr, sys_name); } + usbi_mutex_static_unlock(&active_contexts_lock); } -void linux_hotplug_disconnected(uint8_t busnum, uint8_t devaddr, const char *sys_name) +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, busnum << 8 | devaddr); + dev = usbi_get_device_by_session_id (ctx, session_id); if (NULL != dev) { usbi_disconnect_device (dev); } else { - usbi_err(ctx, "device not found for session %x %s", busnum << 8 | devaddr, sys_name); + usbi_dbg("device not found for session %x", session_id); } } + usbi_mutex_static_unlock(&active_contexts_lock); } #if !defined(USE_UDEV) @@ -1257,6 +1195,7 @@ static int usbfs_get_device_list(struct libusb_context *ctx) return r; } +#endif static int sysfs_scan_device(struct libusb_context *ctx, const char *devname) { @@ -1272,6 +1211,7 @@ static int sysfs_scan_device(struct libusb_context *ctx, const char *devname) devname); } +#if !defined(USE_UDEV) static int sysfs_get_device_list(struct libusb_context *ctx) { DIR *devices = opendir(SYSFS_DEVICE_PATH); @@ -1322,37 +1262,30 @@ static int linux_default_scan_devices (struct libusb_context *ctx) static int op_open(struct libusb_device_handle *handle) { struct linux_device_handle_priv *hpriv = _device_handle_priv(handle); - char filename[PATH_MAX]; int r; - _get_usbfs_path(handle->dev, filename); - usbi_dbg("opening %s", filename); - hpriv->fd = open(filename, O_RDWR); + hpriv->fd = _get_usbfs_fd(handle->dev, O_RDWR, 0); if (hpriv->fd < 0) { - if (errno == EACCES) { - usbi_err(HANDLE_CTX(handle), "libusb couldn't open USB device %s: " - "Permission denied.", filename); - usbi_err(HANDLE_CTX(handle), - "libusb requires write access to USB device nodes."); - return LIBUSB_ERROR_ACCESS; - } else if (errno == ENOENT) { - usbi_err(HANDLE_CTX(handle), "libusb couldn't open USB device %s: " - "No such file or directory.", filename); - return LIBUSB_ERROR_NO_DEVICE; - } else { - usbi_err(HANDLE_CTX(handle), - "open failed, code %d errno %d", hpriv->fd, errno); - return LIBUSB_ERROR_IO; + 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("%s: getcap not available", filename); + usbi_dbg("getcap not available"); else - usbi_err(HANDLE_CTX(handle), - "%s: getcap failed (%d)", filename, errno); + usbi_err(HANDLE_CTX(handle), "getcap failed (%d)", errno); hpriv->caps = 0; if (supports_flag_zero_packet) hpriv->caps |= USBFS_CAP_ZERO_PACKET; @@ -1374,10 +1307,13 @@ static int op_get_configuration(struct libusb_device_handle *handle, int *config) { int r; - if (sysfs_can_relate_devices != 1) - return LIBUSB_ERROR_NOT_SUPPORTED; - r = sysfs_get_active_config(handle->dev, config); + 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; @@ -1406,25 +1342,13 @@ static int op_set_configuration(struct libusb_device_handle *handle, int config) return LIBUSB_ERROR_OTHER; } - if (!sysfs_has_descriptors) { - /* update our cached active config descriptor */ - if (config == -1) { - if (priv->config_descriptor) { - free(priv->config_descriptor); - priv->config_descriptor = NULL; - } - } else { - r = cache_active_config(handle->dev, fd, config); - if (r < 0) - usbi_warn(HANDLE_CTX(handle), - "failed to update cached config descriptor, error %d", r); - } - } + /* update our cached active config descriptor */ + priv->active_config = config; - return 0; + return LIBUSB_SUCCESS; } -static int op_claim_interface(struct libusb_device_handle *handle, int iface) +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); @@ -1443,7 +1367,7 @@ static int op_claim_interface(struct libusb_device_handle *handle, int iface) return 0; } -static int op_release_interface(struct libusb_device_handle *handle, int iface) +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); @@ -1514,7 +1438,7 @@ static int op_reset_device(struct libusb_device_handle *handle) 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); + release_interface(handle, i); } } @@ -1535,11 +1459,18 @@ static int op_reset_device(struct libusb_device_handle *handle) /* 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); + /* + * 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", i); + "failed to re-claim interface %d after reset: %s", + i, libusb_error_name(r)); handle->claimed_interfaces &= ~(1L << i); + ret = LIBUSB_ERROR_NOT_FOUND; } } } @@ -1568,7 +1499,7 @@ static int op_kernel_driver_active(struct libusb_device_handle *handle, return LIBUSB_ERROR_OTHER; } - return 1; + return (strcmp(getdrv.driver, "usbfs") == 0) ? 0 : 1; } static int op_detach_kernel_driver(struct libusb_device_handle *handle, @@ -1576,12 +1507,18 @@ static int op_detach_kernel_driver(struct libusb_device_handle *handle, { 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) @@ -1631,15 +1568,69 @@ static int op_attach_kernel_driver(struct libusb_device_handle *handle, 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 (!sysfs_has_descriptors) { - if (priv->dev_descriptor) - free(priv->dev_descriptor); - if (priv->config_descriptor) - free(priv->config_descriptor); - } + if (priv->descriptors) + free(priv->descriptors); if (priv->sysfs_dir) free(priv->sysfs_dir); } @@ -1772,10 +1763,9 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer, usbi_dbg("need %d urbs for new transfer with length %d", num_urbs, transfer->length); alloc_size = num_urbs * sizeof(struct usbfs_urb); - urbs = malloc(alloc_size); + urbs = calloc(1, alloc_size); if (!urbs) return LIBUSB_ERROR_NO_MEM; - memset(urbs, 0, alloc_size); tpriv->urbs = urbs; tpriv->num_urbs = num_urbs; tpriv->num_retired = 0; @@ -1788,8 +1778,8 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer, urb->type = urb_type; 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) + /* 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; @@ -1830,7 +1820,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer, * 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 + * discarded URBs are reaped, otherwise libusbx will be using * freed memory. * - the earlier URBs may have completed successfully and we do * not want to throw away any data. @@ -1908,10 +1898,9 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) usbi_dbg("need %d 32k URBs for transfer", num_urbs); alloc_size = num_urbs * sizeof(*urbs); - urbs = malloc(alloc_size); + urbs = calloc(1, alloc_size); if (!urbs) return LIBUSB_ERROR_NO_MEM; - memset(urbs, 0, alloc_size); tpriv->iso_urbs = urbs; tpriv->num_urbs = num_urbs; @@ -1945,12 +1934,11 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) alloc_size = sizeof(*urb) + (urb_packet_offset * sizeof(struct usbfs_iso_packet_desc)); - urb = malloc(alloc_size); + urb = calloc(1, alloc_size); if (!urb) { free_iso_urbs(tpriv); return LIBUSB_ERROR_NO_MEM; } - memset(urb, 0, alloc_size); urbs[i] = urb; /* populate packet lengths */ @@ -1994,7 +1982,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) * 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 + * discarded URBs are reaped, otherwise libusbx will be using * freed memory. * - the earlier URBs may have completed successfully and we do * not want to throw away any data. @@ -2034,10 +2022,9 @@ static int submit_control_transfer(struct usbi_transfer *itransfer) if (transfer->length - LIBUSB_CONTROL_SETUP_SIZE > MAX_CTRL_BUFFER_LENGTH) return LIBUSB_ERROR_INVALID_PARAM; - urb = malloc(sizeof(struct usbfs_urb)); + urb = calloc(1, sizeof(struct usbfs_urb)); if (!urb) return LIBUSB_ERROR_NO_MEM; - memset(urb, 0, sizeof(struct usbfs_urb)); tpriv->urbs = urb; tpriv->num_urbs = 1; tpriv->reap_action = NORMAL; @@ -2170,7 +2157,7 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer, * * 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 + * (closing any holes), so that libusbx reports the total amount of * transferred data and presents it in a contiguous chunk. */ if (urb->actual_length > 0) { @@ -2525,6 +2512,13 @@ static int op_handle_events(struct libusb_context *ctx, 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; } @@ -2565,12 +2559,15 @@ static clockid_t op_get_timerfd_clockid(void) 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, @@ -2606,4 +2603,3 @@ const struct usbi_os_backend linux_usbfs_backend = { .transfer_priv_size = sizeof(struct linux_transfer_priv), .add_iso_packet_size = 0, }; - diff --git a/compat/libusb-1.0/libusb/os/linux_usbfs.h b/compat/libusb-1.0/libusb/os/linux_usbfs.h index 661a9c3e..1f5b191f 100644 --- a/compat/libusb-1.0/libusb/os/linux_usbfs.h +++ b/compat/libusb-1.0/libusb/os/linux_usbfs.h @@ -1,7 +1,7 @@ /* * usbfs header structures - * Copyright (C) 2007 Daniel Drake - * Copyright (c) 2001 Johannes Erdfelt + * 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 @@ -123,6 +123,15 @@ struct usbfs_hub_portinfo { #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]; +}; + #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) @@ -145,24 +154,28 @@ struct usbfs_hub_portinfo { #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) + +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_hotplug_disconnected(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); + 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); + uint8_t busnum, uint8_t devaddr, const char *sysfs_dir); #endif diff --git a/compat/libusb-1.0/libusb/os/netbsd_usb.c b/compat/libusb-1.0/libusb/os/netbsd_usb.c new file mode 100644 index 00000000..1e8d6023 --- /dev/null +++ b/compat/libusb-1.0/libusb/os/netbsd_usb.c @@ -0,0 +1,734 @@ +/* + * 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, /* 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) { + dev = libusb_ref_device(dev); + } else { + 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; + } + + 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, libusbx 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/compat/libusb-1.0/libusb/os/openbsd_usb.c b/compat/libusb-1.0/libusb/os/openbsd_usb.c index e31941bf..2997e536 100644 --- a/compat/libusb-1.0/libusb/os/openbsd_usb.c +++ b/compat/libusb-1.0/libusb/os/openbsd_usb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Martin Pieuchot + * 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 @@ -32,8 +32,8 @@ #include "libusbi.h" struct device_priv { - char devnode[16]; - int fd; + 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 */ @@ -82,22 +82,28 @@ 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 *, 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, @@ -128,75 +134,105 @@ const struct usbi_os_backend openbsd_backend = { 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 devnode[16]; - int fd, err, i; + char devices[USB_MAX_DEVICES]; + char busnode[16]; + char *udevname; + int fd, addr, i, j; 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); + for (i = 0; i < 8; i++) { + snprintf(busnode, sizeof(busnode), USBDEV "%d", i); - if ((fd = open(devnode, O_RDONLY)) < 0) { + if ((fd = open(busnode, O_RDWR)) < 0) { if (errno != ENOENT && errno != ENXIO) - usbi_err(ctx, "could not open %s", devnode); + usbi_err(ctx, "could not open %s", busnode); 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; + 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); } - dpriv->cdesc = NULL; - if (_cache_active_config_descriptor(dev, fd)) { - err = errno; - goto error; + ddd = discovered_devs_append(*discdevs, dev); + if (ddd == NULL) { + close(fd); + return (LIBUSB_ERROR_NO_MEM); } - if ((err = usbi_sanitize_device(dev))) - goto error; + *discdevs = ddd; + devices[addr] = 1; } - close(fd); - if (discovered_devs_append(*discdevs, dev) == NULL) - return (LIBUSB_ERROR_NO_MEM); + close(fd); } return (LIBUSB_SUCCESS); - -error: - close(fd); - libusb_unref_device(dev); - return _errno_to_libusb(err); } int @@ -204,15 +240,21 @@ 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]; - dpriv->fd = open(dpriv->devnode, O_RDWR); - if (dpriv->fd < 0) { - dpriv->fd = open(dpriv->devnode, O_RDONLY); + 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", dpriv->devnode, dpriv->fd); + usbi_dbg("open %s: fd %d", devnode, dpriv->fd); + } if (pipe(hpriv->pipe) < 0) return _errno_to_libusb(errno); @@ -226,10 +268,12 @@ 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; - usbi_dbg("close: fd %d", dpriv->fd); + if (dpriv->devname) { + usbi_dbg("close: fd %d", dpriv->fd); - close(dpriv->fd); - dpriv->fd = -1; + close(dpriv->fd); + dpriv->fd = -1; + } usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->pipe[0]); @@ -257,9 +301,8 @@ 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 *ucd = (usb_config_descriptor_t *)dpriv->cdesc; - ucd = (usb_config_descriptor_t *) dpriv->cdesc; len = MIN(len, UGETW(ucd->wTotalLength)); usbi_dbg("len %d", len); @@ -268,58 +311,48 @@ obsd_get_active_config_descriptor(struct libusb_device *dev, *host_endian = 0; - return (LIBUSB_SUCCESS); + return (len); } int obsd_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; + struct usb_device_fdesc udf; int fd, err; - usbi_dbg("index %d, len %d", idx, len); + if ((fd = _bus_open(dev->bus_number)) < 0) + return _errno_to_libusb(errno); - /* 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); - } + 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; - ufd.ufd_config_index = idx; - ufd.ufd_size = len; - ufd.ufd_data = buf; + usbi_dbg("index %d, len %d", udf.udf_config_index, len); - if ((ioctl(fd, USB_GET_FULL_DESC, &ufd)) < 0) { + if (ioctl(fd, USB_DEVICE_GET_FDESC, &udf) < 0) { err = errno; - if (dpriv->fd < 0) - close(fd); + close(fd); return _errno_to_libusb(err); } - - if (dpriv->fd < 0) - close(fd); + close(fd); *host_endian = 0; - return (LIBUSB_SUCCESS); + 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; - usbi_dbg(""); + *config = ucd->bConfigurationValue; - if (ioctl(dpriv->fd, USB_GET_CONFIG, config) < 0) - return _errno_to_libusb(errno); - - usbi_dbg("configuration %d", *config); + usbi_dbg("bConfigurationValue %d", *config); return (LIBUSB_SUCCESS); } @@ -329,12 +362,15 @@ obsd_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 (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, dpriv->fd); + return _cache_active_config_descriptor(handle->dev); } int @@ -369,6 +405,9 @@ obsd_set_interface_altsetting(struct libusb_device_handle *handle, int iface, 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)); @@ -385,19 +424,27 @@ obsd_set_interface_altsetting(struct libusb_device_handle *handle, int iface, int obsd_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; + 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(dpriv->fd, USB_DO_REQUEST, &req) < 0) - return _errno_to_libusb(errno); + if (ioctl(fd, USB_REQUEST, &req) < 0) { + err = errno; + close(fd); + return _errno_to_libusb(err); + } + close(fd); return (LIBUSB_SUCCESS); } @@ -418,6 +465,7 @@ obsd_destroy_device(struct libusb_device *dev) usbi_dbg(""); free(dpriv->cdesc); + free(dpriv->devname); } int @@ -557,6 +605,8 @@ obsd_clock_gettime(int clkid, struct timespec *tp) int _errno_to_libusb(int err) { + usbi_dbg("error: %s (%d)", strerror(err), err); + switch (err) { case EIO: return (LIBUSB_ERROR_IO); @@ -566,52 +616,64 @@ _errno_to_libusb(int err) return (LIBUSB_ERROR_NO_DEVICE); case ENOMEM: return (LIBUSB_ERROR_NO_MEM); + case ETIMEDOUT: + return (LIBUSB_ERROR_TIMEOUT); } - usbi_dbg("error: %s", strerror(err)); - return (LIBUSB_ERROR_OTHER); } int -_cache_active_config_descriptor(struct libusb_device *dev, int fd) +_cache_active_config_descriptor(struct libusb_device *dev) { struct device_priv *dpriv = (struct device_priv *)dev->os_priv; - struct usb_config_desc ucd; - struct usb_full_desc ufd; + struct usb_device_cdesc udc; + struct usb_device_fdesc udf; unsigned char* buf; - int len; + int fd, len, err; - usbi_dbg("fd %d", fd); + if ((fd = _bus_open(dev->bus_number)) < 0) + return _errno_to_libusb(errno); - ucd.ucd_config_index = USB_CURRENT_CONFIG_INDEX; + usbi_dbg("fd %d, addr %d", fd, dev->device_address); - if ((ioctl(fd, USB_GET_CONFIG_DESC, &ucd)) < 0) + 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", ucd.ucd_desc.bLength); + usbi_dbg("active bLength %d", udc.udc_desc.bLength); - len = UGETW(ucd.ucd_desc.wTotalLength); + len = UGETW(udc.udc_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; + 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", ufd.ufd_config_index, len); + usbi_dbg("index %d, len %d", udf.udf_config_index, len); - if ((ioctl(fd, USB_GET_FULL_DESC, &ufd)) < 0) { + if (ioctl(fd, USB_DEVICE_GET_FDESC, &udf) < 0) { + err = errno; + close(fd); free(buf); - return _errno_to_libusb(errno); + return _errno_to_libusb(err); } + close(fd); if (dpriv->cdesc) free(dpriv->cdesc); dpriv->cdesc = buf; - return (0); + return (LIBUSB_SUCCESS); } int @@ -626,15 +688,16 @@ _sync_control_transfer(struct usbi_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", + 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 */ + /* Don't use USETW, libusbx 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; @@ -643,11 +706,30 @@ _sync_control_transfer(struct usbi_transfer *itransfer) 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 (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 ((ioctl(dpriv->fd, USB_DO_REQUEST, &req)) < 0) - return _errno_to_libusb(errno); + 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; @@ -661,7 +743,7 @@ _access_endpoint(struct libusb_transfer *transfer) { struct handle_priv *hpriv; struct device_priv *dpriv; - char *s, devnode[16]; + char devnode[16]; int fd, endpt; mode_t mode; @@ -674,10 +756,9 @@ _access_endpoint(struct libusb_transfer *transfer) 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); + /* 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)) @@ -694,9 +775,14 @@ 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 @@ -725,3 +811,13 @@ _sync_gen_transfer(struct usbi_transfer *itransfer) 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/compat/libusb-1.0/libusb/os/poll_posix.c b/compat/libusb-1.0/libusb/os/poll_posix.c new file mode 100644 index 00000000..eeaf5dce --- /dev/null +++ b/compat/libusb-1.0/libusb/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/compat/libusb-1.0/libusb/os/poll_posix.h b/compat/libusb-1.0/libusb/os/poll_posix.h index 0e5e7f5b..5b4b2c90 100644 --- a/compat/libusb-1.0/libusb/os/poll_posix.h +++ b/compat/libusb-1.0/libusb/os/poll_posix.h @@ -4,7 +4,8 @@ #define usbi_write write #define usbi_read read #define usbi_close close -#define usbi_pipe pipe #define usbi_poll poll +int usbi_pipe(int pipefd[2]); + #endif /* LIBUSB_POLL_POSIX_H */ diff --git a/compat/libusb-1.0/libusb/os/poll_windows.c b/compat/libusb-1.0/libusb/os/poll_windows.c index 7f4d9c4e..abe87618 100644 --- a/compat/libusb-1.0/libusb/os/poll_windows.c +++ b/compat/libusb-1.0/libusb/os/poll_windows.c @@ -1,6 +1,7 @@ /* * poll_windows: poll compatibility wrapper for Windows - * Copyright (C) 2009-2010 Pete Batard + * 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. * @@ -21,7 +22,7 @@ */ /* - * poll() and pipe() Windows compatibility layer for libusb 1.0 + * poll() and pipe() Windows compatibility layer for libusbx 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. @@ -31,7 +32,7 @@ * 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 _O_RDONLY and once in _O_WRONLY mode to obtain 2 separate + * 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 * @@ -40,12 +41,10 @@ * context. */ #include -#include #include #include -#include -#include +#include "libusbi.h" // Uncomment to debug the polling layer //#define DEBUG_POLL_WINDOWS @@ -53,8 +52,8 @@ #define poll_dbg usbi_dbg #else // MSVC++ < 2005 cannot use a variadic argument and non MSVC -// compilers produce warnings if parenthesis are omitted. -#if defined(_MSC_VER) && _MSC_VER < 1400 +// compilers produce warnings if parenthesis are ommitted. +#if defined(_MSC_VER) && (_MSC_VER < 1400) #define poll_dbg #else #define poll_dbg(...) @@ -65,20 +64,10 @@ #pragma warning(disable:28719) #endif -#if defined(__CYGWIN__) -// cygwin produces a warning unless these prototypes are defined -extern int _open(char* name, int flags); -extern int _close(int fd); -extern int _snprintf(char *buffer, size_t count, const char *format, ...); -#define NUL_DEVICE "/dev/null" -#else -#define NUL_DEVICE "NUL" -#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, RW_NONE}; +const struct winfd INVALID_WINFD = {-1, INVALID_HANDLE_VALUE, NULL, NULL, NULL, RW_NONE}; struct winfd poll_fd[MAX_FDS]; // internal fd data struct { @@ -93,12 +82,25 @@ BOOLEAN is_polling_set = FALSE; LONG pipe_number = 0; static volatile LONG compat_spinlock = 0; +#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 CancelIoEx_Available (pCancelIoEx != NULL) -static __inline BOOL cancel_io(int _index) +#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; @@ -108,7 +110,12 @@ static __inline BOOL cancel_io(int _index) || (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL) ) { return TRUE; } - if (CancelIoEx_Available) { + 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()) { @@ -117,6 +124,30 @@ static __inline BOOL cancel_io(int _index) 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) @@ -127,10 +158,7 @@ void init_polling(void) SleepEx(0, TRUE); } if (!is_polling_set) { - pCancelIoEx = (BOOL (__stdcall *)(HANDLE,LPOVERLAPPED)) - GetProcAddress(GetModuleHandleA("KERNEL32"), "CancelIoEx"); - usbi_dbg("Will use CancelIo%s for I/O cancellation", - CancelIoEx_Available?"Ex":""); + setup_cancel_io(); for (i=0; ihEvent; - if (event_handle != NULL) { - ResetEvent(event_handle); - } - memset(overlapped, 0, sizeof(OVERLAPPED)); - overlapped->hEvent = event_handle; -} - void exit_polling(void) { int i; @@ -221,12 +235,8 @@ void exit_polling(void) // terminating, and we should be able to access the fd // mutex lock before too long EnterCriticalSection(&_poll_fd[i].mutex); - if ( (poll_fd[i].fd > 0) && (poll_fd[i].handle != INVALID_HANDLE_VALUE) && (poll_fd[i].handle != 0) - && (GetFileType(poll_fd[i].handle) == FILE_TYPE_UNKNOWN) ) { - _close(poll_fd[i].fd); - } free_overlapped(poll_fd[i].overlapped); - if (!CancelIoEx_Available) { + if (Use_Duplicate_Handles) { // Close duplicate handle if (_poll_fd[i].original_handle != INVALID_HANDLE_VALUE) { CloseHandle(poll_fd[i].handle); @@ -237,12 +247,12 @@ void exit_polling(void) DeleteCriticalSection(&_poll_fd[i].mutex); } } - compat_spinlock = 0; + InterlockedExchange((LONG *)&compat_spinlock, 0); } /* * Create a fake pipe. - * As libusb only uses pipes for signaling, all we need from a pipe is an + * As libusbx only uses pipes for signaling, all we need from a pipe is an * event. To that extent, we create a single wfd and overlapped as a means * to access that event. */ @@ -253,7 +263,8 @@ int usbi_pipe(int filedes[2]) CHECK_INIT_POLLING; - overlapped = (OVERLAPPED*) calloc(1, sizeof(OVERLAPPED)); + overlapped = create_overlapped(); + if (overlapped == NULL) { return -1; } @@ -261,22 +272,6 @@ int usbi_pipe(int filedes[2]) overlapped->Internal = STATUS_PENDING; overlapped->InternalHigh = 0; - // Read end of the "pipe" - filedes[0] = _open(NUL_DEVICE, _O_WRONLY); - if (filedes[0] < 0) { - usbi_err(NULL, "could not create pipe: errno %d", errno); - goto out1; - } - // We can use the same handle for both ends - filedes[1] = filedes[0]; - poll_dbg("pipe filedes = %d", filedes[0]); - - // Note: manual reset must be true (second param) as the reset occurs in read - overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - if(!overlapped->hEvent) { - goto out2; - } - for (i=0; ihEvent); -out2: - _close(filedes[0]); -out1: - free(overlapped); + free_overlapped(overlapped); return -1; } @@ -319,9 +315,9 @@ out1: * 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 winfd usbi_create_fd(HANDLE handle, int access_mode, struct usbi_transfer *itransfer, cancel_transfer *cancel_fn) { - int i, fd; + int i; struct winfd wfd = INVALID_WINFD; OVERLAPPED* overlapped = NULL; @@ -331,27 +327,22 @@ struct winfd usbi_create_fd(HANDLE handle, int access_mode) return INVALID_WINFD; } - if ((access_mode != _O_RDONLY) && (access_mode != _O_WRONLY)) { - usbi_warn(NULL, "only one of _O_RDONLY or _O_WRONLY are supported.\n" + 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 == _O_RDONLY) { + if (access_mode == RW_READ) { wfd.rw = RW_READ; } else { wfd.rw = RW_WRITE; } - // Ensure that we get a non system conflicting unique fd, using - // the same fd attribution system as the pipe ends - fd = _open(NUL_DEVICE, _O_WRONLY); - if (fd < 0) { - return INVALID_WINFD; - } - overlapped = create_overlapped(); if(overlapped == NULL) { - _close(fd); return INVALID_WINFD; } @@ -363,10 +354,11 @@ struct winfd usbi_create_fd(HANDLE handle, int access_mode) LeaveCriticalSection(&_poll_fd[i].mutex); continue; } - wfd.fd = fd; + // 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 (!CancelIoEx_Available) { + if (Use_Duplicate_Handles) { _poll_fd[i].thread_id = GetCurrentThreadId(); if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), &wfd.handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) { @@ -387,21 +379,15 @@ struct winfd usbi_create_fd(HANDLE handle, int access_mode) } } free_overlapped(overlapped); - _close(fd); return INVALID_WINFD; } -void _free_index(int _index) +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 fake handle for devices - if ( (poll_fd[_index].handle != INVALID_HANDLE_VALUE) && (poll_fd[_index].handle != 0) - && (GetFileType(poll_fd[_index].handle) == FILE_TYPE_UNKNOWN) ) { - _close(poll_fd[_index].fd); - } // close the duplicate handle (if we have an actual duplicate) - if (!CancelIoEx_Available) { + if (Use_Duplicate_Handles) { if (_poll_fd[_index].original_handle != INVALID_HANDLE_VALUE) { CloseHandle(poll_fd[_index].handle); } @@ -417,17 +403,18 @@ void _free_index(int _index) * * Note that the associated Windows handle is not closed by this call */ -void usbi_free_fd(int fd) +void usbi_free_fd(struct winfd *wfd) { int _index; CHECK_INIT_POLLING; - _index = _fd_to_index_and_lock(fd); + _index = _fd_to_index_and_lock(wfd->fd); if (_index < 0) { return; } _free_index(_index); + *wfd = INVALID_WINFD; LeaveCriticalSection(&_poll_fd[_index].mutex); } @@ -441,7 +428,7 @@ struct winfd fd_to_winfd(int fd) CHECK_INIT_POLLING; - if (fd <= 0) + if (fd < 0) return INVALID_WINFD; for (i=0; ihEvent); - free(poll_fd[_index].overlapped); - } - r = _close(poll_fd[_index].fd); - if (r != 0) { - errno = EIO; - } + free_overlapped(poll_fd[_index].overlapped); poll_fd[_index] = INVALID_WINFD; LeaveCriticalSection(&_poll_fd[_index].mutex); } @@ -672,6 +651,7 @@ int usbi_close(int fd) ssize_t usbi_write(int fd, const void *buf, size_t count) { int _index; + UNUSED(buf); CHECK_INIT_POLLING; @@ -708,6 +688,7 @@ ssize_t usbi_read(int fd, void *buf, size_t count) { int _index; ssize_t r = -1; + UNUSED(buf); CHECK_INIT_POLLING; diff --git a/compat/libusb-1.0/libusb/os/poll_windows.h b/compat/libusb-1.0/libusb/os/poll_windows.h index d3bda477..deed2064 100644 --- a/compat/libusb-1.0/libusb/os/poll_windows.h +++ b/compat/libusb-1.0/libusb/os/poll_windows.h @@ -1,6 +1,7 @@ /* * Windows compat: POSIX compatibility wrapper - * Copyright (C) 2009-2010 Pete Batard + * 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. * @@ -21,8 +22,6 @@ */ #pragma once -#include - #if defined(_MSC_VER) // disable /W4 MSVC warnings that are benign #pragma warning(disable:4127) // conditional expression is constant @@ -33,12 +32,17 @@ #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) enum windows_version { WINDOWS_UNSUPPORTED, + WINDOWS_CE, WINDOWS_XP, WINDOWS_2003, // also includes XP 64 WINDOWS_VISTA_AND_LATER, @@ -68,10 +72,14 @@ enum rw_type { }; // 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; @@ -84,8 +92,9 @@ int usbi_close(int fd); void init_polling(void); void exit_polling(void); -struct winfd usbi_create_fd(HANDLE handle, int access_mode); -void usbi_free_fd(int fd); +struct winfd 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); @@ -114,4 +123,3 @@ do { \ } \ } while (0) #endif - diff --git a/compat/libusb-1.0/libusb/os/threads_posix.c b/compat/libusb-1.0/libusb/os/threads_posix.c index 60c57cf8..edf70636 100644 --- a/compat/libusb-1.0/libusb/os/threads_posix.c +++ b/compat/libusb-1.0/libusb/os/threads_posix.c @@ -1,8 +1,8 @@ /* - * libusb synchronization using POSIX Threads + * libusbx synchronization using POSIX Threads * - * Copyright (C) 2011 Vitali Lovich - * Copyright (C) 2011 Peter Stuge + * 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 @@ -19,14 +19,19 @@ * 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 +#if defined(__linux__) || defined(__OpenBSD__) +# if defined(__linux__) +# define _GNU_SOURCE +# else +# define _BSD_SOURCE # endif -#else -#define _XOPEN_SOURCE 500 -#endif /* _XOPEN_SOURCE */ +# include +# include +#elif defined(__APPLE__) +# include +#elif defined(__CYGWIN__) +# include +#endif #include "threads_posix.h" @@ -41,6 +46,7 @@ int usbi_mutex_init_recursive(pthread_mutex_t *mutex, pthread_mutexattr_t *attr) 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; @@ -53,3 +59,24 @@ finish: 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/compat/libusb-1.0/libusb/os/threads_posix.h b/compat/libusb-1.0/libusb/os/threads_posix.h index 97522089..0b6a71a5 100644 --- a/compat/libusb-1.0/libusb/os/threads_posix.h +++ b/compat/libusb-1.0/libusb/os/threads_posix.h @@ -1,7 +1,7 @@ /* - * libusb synchronization using POSIX Threads + * libusbx synchronization using POSIX Threads * - * Copyright (C) 2010 Peter Stuge + * 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 @@ -45,4 +45,6 @@ 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/compat/libusb-1.0/libusb/os/threads_windows.c b/compat/libusb-1.0/libusb/os/threads_windows.c index b92b645d..cad27e9e 100644 --- a/compat/libusb-1.0/libusb/os/threads_windows.c +++ b/compat/libusb-1.0/libusb/os/threads_windows.c @@ -1,7 +1,7 @@ /* - * libusb synchronization on Microsoft Windows + * libusbx synchronization on Microsoft Windows * - * Copyright (C) 2010 Michael Plante + * 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 @@ -25,9 +25,11 @@ #include "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)); @@ -79,10 +81,9 @@ int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex) { 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); @@ -90,16 +91,14 @@ int usbi_cond_init(usbi_cond_t *cond, } 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, *prev_pos = NULL; + 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(pos, &cond->not_waiting, list, struct usbi_cond_perthread) { - free(prev_pos); + list_for_each_entry_safe(pos, next_pos, &cond->not_waiting, list, struct usbi_cond_perthread) { CloseHandle(pos->event); list_del(&pos->list); - prev_pos = pos; + free(pos); } - free(prev_pos); return 0; } @@ -129,7 +128,7 @@ int usbi_cond_signal(usbi_cond_t *cond) { // The wait function will remove its respective item from the list. return SetEvent(pos->event) ? 0 : ((errno=EINVAL)); } -static int __inline usbi_cond_intwait(usbi_cond_t *cond, +__inline static int usbi_cond_intwait(usbi_cond_t *cond, usbi_mutex_t *mutex, DWORD timeout_ms) { struct usbi_cond_perthread *pos; @@ -182,9 +181,11 @@ int usbi_cond_timedwait(usbi_cond_t *cond, struct timeval targ_time, cur_time, delta_time; struct timespec cur_time_ns; DWORD millis; - extern const uint64_t epoch_time; - GetSystemTimeAsFileTime(&filetime); + // 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; @@ -206,3 +207,6 @@ int usbi_cond_timedwait(usbi_cond_t *cond, return usbi_cond_intwait(cond, mutex, millis); } +int usbi_get_tid(void) { + return GetCurrentThreadId(); +} diff --git a/compat/libusb-1.0/libusb/os/threads_windows.h b/compat/libusb-1.0/libusb/os/threads_windows.h index e486df92..df8a0eeb 100644 --- a/compat/libusb-1.0/libusb/os/threads_windows.h +++ b/compat/libusb-1.0/libusb/os/threads_windows.h @@ -1,7 +1,7 @@ /* - * libusb synchronization on Microsoft Windows + * libusbx synchronization on Microsoft Windows * - * Copyright (C) 2010 Michael Plante + * 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 @@ -21,8 +21,6 @@ #ifndef LIBUSB_THREADS_WINDOWS_H #define LIBUSB_THREADS_WINDOWS_H -#include - #define usbi_mutex_static_t volatile LONG #define USBI_MUTEX_INITIALIZER 0 @@ -84,5 +82,6 @@ int usbi_cond_timedwait(usbi_cond_t *cond, int usbi_cond_broadcast(usbi_cond_t *cond); int usbi_cond_signal(usbi_cond_t *cond); -#endif /* LIBUSB_THREADS_WINDOWS_H */ +int usbi_get_tid(void); +#endif /* LIBUSB_THREADS_WINDOWS_H */ diff --git a/compat/libusb-1.0/libusb/os/wince_usb.c b/compat/libusb-1.0/libusb/os/wince_usb.c new file mode 100644 index 00000000..90c129bd --- /dev/null +++ b/compat/libusb-1.0/libusb/os/wince_usb.c @@ -0,0 +1,1026 @@ +/* + * Windows CE backend for libusbx 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 +enum windows_version 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); + libusb_ref_device(dev); + // 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); + 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; + 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, + + 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/compat/libusb-1.0/libusb/os/wince_usb.h b/compat/libusb-1.0/libusb/os/wince_usb.h new file mode 100644 index 00000000..3db9693a --- /dev/null +++ b/compat/libusb-1.0/libusb/os/wince_usb.h @@ -0,0 +1,131 @@ +/* + * Windows CE backend for libusbx 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 libusbx 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/compat/libusb-1.0/libusb/os/windows_common.h b/compat/libusb-1.0/libusb/os/windows_common.h new file mode 100644 index 00000000..1da72bd9 --- /dev/null +++ b/compat/libusb-1.0/libusb/os/windows_common.h @@ -0,0 +1,108 @@ +/* + * Windows backend common header for libusbx 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/compat/libusb-1.0/libusb/os/windows_usb.c b/compat/libusb-1.0/libusb/os/windows_usb.c index afc4dfce..1a9dae12 100644 --- a/compat/libusb-1.0/libusb/os/windows_usb.c +++ b/compat/libusb-1.0/libusb/os/windows_usb.c @@ -1,8 +1,9 @@ /* - * windows backend for libusb 1.0 - * Copyright (c) 2009-2010 Pete Batard + * windows backend for libusbx 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 * @@ -29,60 +30,71 @@ #include #include #include +#include #include #include -#include +#include "libusbi.h" #include "poll_windows.h" #include "windows_usb.h" -// The following prevents "banned API" errors when using the MS's WDK OACR/Prefast -#if defined(_PREFAST_) -#pragma warning(disable:28719) -#endif - // 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; } -extern void usbi_fd_notification(struct libusb_context *ctx); - // 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); -// WinUSB API prototypes -static int winusb_init(struct libusb_context *ctx); -static int winusb_exit(void); -static int winusb_open(struct libusb_device_handle *dev_handle); -static void winusb_close(struct libusb_device_handle *dev_handle); -static int winusb_configure_endpoints(struct libusb_device_handle *dev_handle, int iface); -static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int iface); -static int winusb_release_interface(struct libusb_device_handle *dev_handle, int iface); -static int winusb_submit_control_transfer(struct usbi_transfer *itransfer); -static int winusb_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting); -static int winusb_submit_bulk_transfer(struct usbi_transfer *itransfer); -static int winusb_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint); -static int winusb_abort_transfers(struct usbi_transfer *itransfer); -static int winusb_abort_control(struct usbi_transfer *itransfer); -static int winusb_reset_device(struct libusb_device_handle *dev_handle); -static int winusb_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size); +// 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(struct libusb_context *ctx); -static int composite_exit(void); -static int composite_open(struct libusb_device_handle *dev_handle); -static void composite_close(struct libusb_device_handle *dev_handle); -static int composite_claim_interface(struct libusb_device_handle *dev_handle, int iface); -static int composite_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting); -static int composite_release_interface(struct libusb_device_handle *dev_handle, int iface); -static int composite_submit_control_transfer(struct usbi_transfer *itransfer); -static int composite_submit_bulk_transfer(struct usbi_transfer *itransfer); -static int composite_submit_iso_transfer(struct usbi_transfer *itransfer); -static int composite_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint); -static int composite_abort_transfers(struct usbi_transfer *itransfer); -static int composite_abort_control(struct usbi_transfer *itransfer); -static int composite_reset_device(struct libusb_device_handle *dev_handle); -static int composite_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size); +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 @@ -101,8 +113,12 @@ volatile LONG request_count[2] = {0, 1}; // last one must be > 0 HANDLE timer_request[2] = { NULL, NULL }; HANDLE timer_response = NULL; // API globals -bool api_winusb_available = false; -#define CHECK_WINUSB_AVAILABLE do { if (!api_winusb_available) return LIBUSB_ERROR_ACCESS; } while (0) +#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)) { @@ -111,7 +127,7 @@ static inline BOOLEAN guid_eq(const GUID *guid1, const GUID *guid2) { return false; } -#if defined(ENABLE_DEBUG_LOGGING) || (defined(_MSC_VER) && _MSC_VER < 1400) +#if defined(ENABLE_LOGGING) static char* guid_to_string(const GUID* guid) { static char guid_string[MAX_GUID_STRING_LENGTH]; @@ -129,17 +145,18 @@ static char* guid_to_string(const GUID* guid) * 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; - size_t i; + ssize_t i; uint32_t error_code, format_error; error_code = retval?retval:GetLastError(); - safe_sprintf(err_string, ERR_BUFFER_SIZE, "[%d] ", error_code); + safe_sprintf(err_string, ERR_BUFFER_SIZE, "[%u] ", error_code); size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &err_string[safe_strlen(err_string)], @@ -153,12 +170,13 @@ static char err_string[ERR_BUFFER_SIZE]; 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; ((err_string[i]==0x0A) || (err_string[i]==0x0D)); i--) { + 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. @@ -184,7 +202,7 @@ static char* sanitize_path(const char* path) size += add_root; } - if ((ret_path = (char*)calloc(size, 1)) == NULL) + if ((ret_path = (char*) calloc(size, 1)) == NULL) return NULL; safe_strcpy(&ret_path[add_root], size-add_root, path); @@ -221,6 +239,7 @@ static int init_dlls(void) DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiDestroyDeviceInfoList, TRUE); DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiOpenDevRegKey, TRUE); DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiGetDeviceRegistryPropertyA, TRUE); + DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiOpenDeviceInterfaceRegKey, TRUE); DLL_LOAD_PREFIXED(AdvAPI32.dll, p, RegQueryValueExW, TRUE); DLL_LOAD_PREFIXED(AdvAPI32.dll, p, RegCloseKey, TRUE); return LIBUSB_SUCCESS; @@ -240,7 +259,7 @@ static int init_dlls(void) * incremented index starting at zero) until all interfaces have been returned. */ static bool get_devinfo_data(struct libusb_context *ctx, - HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, char* usb_class, unsigned _index) + HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const char* usb_class, unsigned _index) { if (_index <= 0) { *dev_info = pSetupDiGetClassDevsA(NULL, usb_class, NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES); @@ -343,6 +362,88 @@ err_exit: 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) */ @@ -392,7 +493,7 @@ static int htab_create(struct libusb_context *ctx, unsigned long nel) htab_filled = 0; // allocate memory and zero out. - htab_table = (htab_entry*)calloc(htab_size + 1, sizeof(htab_entry)); + 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; @@ -434,8 +535,11 @@ static unsigned long htab_hash(char* str) int c; char* sz = str; + if (str == NULL) + return 0; + // Compute main hash value (algorithm suggested by Nokia) - while ((c = *sz++)) + while ((c = *sz++) != 0) r = ((r << 5) + r) + c; if (r == 0) ++r; @@ -497,7 +601,7 @@ static unsigned long htab_hash(char* str) // 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*) calloc(1, safe_strlen(str)+1); + 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); @@ -532,7 +636,7 @@ static unsigned long get_ancestor_session_id(DWORD devinst, unsigned level) if (CM_Get_Device_IDA(devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) { return 0; } - // TODO (post hotplug): try without sanitizing + // TODO: (post hotplug): try without sanitizing sanitized_path = sanitize_path(path); if (sanitized_path == NULL) { return 0; @@ -567,7 +671,7 @@ static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, int return LIBUSB_SUCCESS; } - priv->usb_interface[iface].endpoint = (uint8_t*) calloc(1, if_desc->bNumEndpoints); + priv->usb_interface[iface].endpoint = (uint8_t*) malloc(if_desc->bNumEndpoints); if (priv->usb_interface[iface].endpoint == NULL) { return LIBUSB_ERROR_NO_MEM; } @@ -579,38 +683,34 @@ static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, int } libusb_free_config_descriptor(conf_desc); - // Extra init is required for WinUSB endpoints - if (priv->apib->id == USB_API_WINUSB) { - return winusb_configure_endpoints(dev_handle, iface); - } - - return LIBUSB_SUCCESS; + // 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 -static bool is_api_driver(char* driver, uint8_t api) -{ - uint8_t i; +// 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 false; - tmp_str = (char*) calloc(1, len+1); - if (tmp_str == NULL) return false; + 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; inum_configurations == 0) return LIBUSB_ERROR_INVALID_PARAM; - priv->config_descriptor = (unsigned char**) calloc(dev->num_configurations, sizeof(PUSB_CONFIGURATION_DESCRIPTOR)); + 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++) @@ -898,15 +1012,16 @@ static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle 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 + // 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. if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, &cd_buf_short, size, &cd_buf_short, size, &ret_size, NULL)) { - usbi_err(ctx, "could not access configuration descriptor (dummy) for '%s': %s", device_id, windows_error_str(0)); + 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_err(ctx, "unexpected configuration descriptor size (dummy) for '%s'.", device_id); + usbi_info(ctx, "unexpected configuration descriptor size (dummy) for '%s'.", device_id); LOOP_BREAK(LIBUSB_ERROR_IO); } @@ -947,7 +1062,7 @@ static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle i, cd_data->bConfigurationValue, cd_data->wTotalLength); // Cache the descriptor - priv->config_descriptor[i] = (unsigned char*) calloc(1, cd_data->wTotalLength); + priv->config_descriptor[i] = (unsigned char*) malloc(cd_data->wTotalLength); if (priv->config_descriptor[i] == NULL) return LIBUSB_ERROR_NO_MEM; memcpy(priv->config_descriptor[i], cd_data, cd_data->wTotalLength); @@ -956,7 +1071,7 @@ static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle } /* - * Populate a libusb device structure + * Populate a libusbx device structure */ static int init_device(struct libusb_device* dev, struct libusb_device* parent_dev, uint8_t port_number, char* device_id, DWORD devinst) @@ -998,8 +1113,10 @@ static int init_device(struct libusb_device* dev, struct libusb_device* parent_d } 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 = libusb_ref_device(parent_dev); // If the device address is already set, we can stop here if (dev->device_address != 0) { @@ -1041,7 +1158,10 @@ static int init_device(struct libusb_device* dev, struct libusb_device* parent_d if (conn_info.DeviceAddress > UINT8_MAX) { usbi_err(ctx, "program assertion failed: device address overflow"); } - dev->device_address = (uint8_t)conn_info.DeviceAddress; + 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; @@ -1052,7 +1172,7 @@ static int init_device(struct libusb_device* dev, struct libusb_device* parent_d break; } } else { - dev->device_address = UINT8_MAX; // Hubs from HCD have a devaddr of 255 + dev->device_address = 1; // root hubs are set to use device number 1 force_hcd_device_descriptor(dev); } @@ -1065,8 +1185,8 @@ static int init_device(struct libusb_device* dev, struct libusb_device* parent_d } // Returns the api type, or 0 if not found/unsupported -static uint8_t get_api_type(struct libusb_context *ctx, - HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data) +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] = { @@ -1076,8 +1196,10 @@ static uint8_t get_api_type(struct libusb_context *ctx, }; DWORD size, reg_type; unsigned k, l; - uint8_t api; + 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, @@ -1095,7 +1217,6 @@ static uint8_t get_api_type(struct libusb_context *ctx, lookup[k].list[l] = LIST_SEPARATOR; } } - upperize(lookup[k].list); usbi_dbg("%s(s): %s", lookup[k].designation, lookup[k].list); } else { if (GetLastError() != ERROR_INVALID_DATA) { @@ -1105,21 +1226,22 @@ static uint8_t get_api_type(struct libusb_context *ctx, } } - for (api=1; api= 0) { + usbi_dbg("matched %s name against %s API", + lookup[k].designation, (i!=USB_API_WINUSBX)?usb_api_backend[i].designation:sub_api_name[j]); + *api = i; + *sub_api = j; + return; } } - if (k >= 3) continue; - return api; } - return 0; } static int set_composite_interface(struct libusb_context* ctx, struct libusb_device* dev, - char* dev_interface_path, char* device_id, uint8_t api) + char* dev_interface_path, char* device_id, int api, int sub_api) { unsigned i; struct windows_device_priv *priv = _device_priv(dev); @@ -1149,36 +1271,78 @@ static int set_composite_interface(struct libusb_context* ctx, struct libusb_dev } if (priv->usb_interface[interface_number].path != NULL) { - usbi_warn(ctx, "interface[%d] already set - ignoring: %s", interface_number, device_id); - return LIBUSB_ERROR_ACCESS; + 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->composite_api_flags |= 1<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 + * get_device_list: libusbx 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 }; - char* usb_class[2] = {"USB", "NUSB3"}; - SP_DEVINFO_DATA dev_info_data; + 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 class_index = 0; + 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]; @@ -1188,12 +1352,10 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered char* dev_id_path = NULL; unsigned long session_id; DWORD size, reg_type, port_nr, install_state; - BOOL b = FALSE; HKEY key; WCHAR guid_string_w[MAX_GUID_STRING_LENGTH]; GUID* if_guid; LONG s; - uint8_t api; // Keep a list of newly allocated devs to unref libusb_device** unref_list; unsigned int unref_size = 64; @@ -1204,14 +1366,17 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered // 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 and set the device interfaces. + // 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; - nb_guids = DEV_PASS+1; + 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) { @@ -1221,23 +1386,9 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered for (pass = 0; ((pass < nb_guids) && (r == LIBUSB_SUCCESS)); pass++) { //#define ENUM_DEBUG #ifdef ENUM_DEBUG - switch(pass) { - case HCD_PASS: - usbi_dbg("PROCESSING HCDs %s", guid_to_string(guid[pass])); - break; - case HUB_PASS: - usbi_dbg("PROCESSING HUBs %s", guid_to_string(guid[pass])); - break; - case DEV_PASS: - usbi_dbg("PROCESSING DEVs %s", guid_to_string(guid[pass])); - break; - case GEN_PASS: - usbi_dbg("PROCESSING GENs"); - break; - default: - usbi_dbg("PROCESSING EXTs %s", guid_to_string(guid[pass])); - break; - } + 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 @@ -1270,13 +1421,15 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered } } 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" - while ( (class_index < 2) && - (!(b = get_devinfo_data(ctx, &dev_info, &dev_info_data, usb_class[class_index], i))) ) { - class_index++; - i = 0; + // 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 (!b) break; + if (class_index >= ARRAYSIZE(usb_class)) + break; } // Read the Device ID path. This is what we'll use as UID @@ -1310,6 +1463,7 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered // 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; @@ -1319,7 +1473,7 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered 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."); + usbi_info(ctx, "libusbx 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); @@ -1341,6 +1495,9 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered } } 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, @@ -1353,7 +1510,7 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered dev_id_path, install_state); continue; } - api = get_api_type(ctx, &dev_info, &dev_info_data); + get_api_type(ctx, &dev_info, &dev_info_data, &api, &sub_api); break; } @@ -1374,7 +1531,7 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered parent_dev = usbi_get_device_by_session_id(ctx, session_id); } if (parent_dev == NULL) { - usbi_dbg("unlisted ancestor for '%s' (newly connected, etc.) - ignoring", dev_id_path); + usbi_dbg("unlisted ancestor for '%s' (non USB HID, newly connected, etc.) - ignoring", dev_id_path); continue; } parent_priv = _device_priv(parent_dev); @@ -1406,7 +1563,7 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered unref_list[unref_cur++] = dev; if (unref_cur >= unref_size) { unref_size += 64; - unref_list = realloc(unref_list, unref_size*sizeof(libusb_device*)); + 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); @@ -1426,6 +1583,7 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered 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; @@ -1437,10 +1595,18 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered // 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); @@ -1472,10 +1638,15 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered r = LIBUSB_SUCCESS; } break; - default: // later passes - if (parent_priv->apib->id == USB_API_COMPOSITE) { + 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)) { + switch (set_composite_interface(ctx, parent_dev, dev_interface_path, dev_id_path, api, sub_api)) { case LIBUSB_SUCCESS: dev_interface_path = NULL; break; @@ -1493,7 +1664,7 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered } // Free any additional GUIDs - for (pass = DEV_PASS+1; pass < nb_guids; pass++) { + for (pass = HID_PASS+1; pass < nb_guids; pass++) { safe_free(guid[pass]); } @@ -1507,7 +1678,7 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered } /* - * exit: libusb backend deinitialization function + * exit: libusbx backend deinitialization function */ static void windows_exit(void) { @@ -1531,7 +1702,7 @@ static void windows_exit(void) // Only works if exits and inits are balanced exactly if (--concurrent_usage < 0) { // Last exit for (i=0; iwTotalLength, len); memcpy(buffer, priv->config_descriptor[config_index], size); + *host_endian = 0; - return LIBUSB_SUCCESS; + return (int)size; } /* @@ -1620,14 +1792,14 @@ static int windows_open(struct libusb_device_handle *dev_handle) return LIBUSB_ERROR_NO_DEVICE; } - return priv->apib->open(dev_handle); + 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(dev_handle); + priv->apib->close(SUB_API_NOTSET, dev_handle); } static int windows_get_configuration(struct libusb_device_handle *dev_handle, int *config) @@ -1678,7 +1850,7 @@ static int windows_claim_interface(struct libusb_device_handle *dev_handle, int safe_free(priv->usb_interface[iface].endpoint); priv->usb_interface[iface].nb_endpoints= 0; - r = priv->apib->claim_interface(dev_handle, iface); + r = priv->apib->claim_interface(SUB_API_NOTSET, dev_handle, iface); if (r == LIBUSB_SUCCESS) { r = windows_assign_endpoints(dev_handle, iface, 0); @@ -1695,7 +1867,7 @@ static int windows_set_interface_altsetting(struct libusb_device_handle *dev_han safe_free(priv->usb_interface[iface].endpoint); priv->usb_interface[iface].nb_endpoints= 0; - r = priv->apib->set_interface_altsetting(dev_handle, iface, altsetting); + 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); @@ -1708,19 +1880,19 @@ static int windows_release_interface(struct libusb_device_handle *dev_handle, in { struct windows_device_priv *priv = _device_priv(dev_handle->dev); - return priv->apib->release_interface(dev_handle, iface); + 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(dev_handle, endpoint); + 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(dev_handle); + return priv->apib->reset_device(SUB_API_NOTSET, dev_handle); } // The 3 functions below are unlikely to ever get supported on Windows @@ -1748,7 +1920,8 @@ 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.fd); + 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); } @@ -1761,7 +1934,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); int r; - r = priv->apib->submit_bulk_transfer(itransfer); + r = priv->apib->submit_bulk_transfer(SUB_API_NOTSET, itransfer); if (r != LIBUSB_SUCCESS) { return r; } @@ -1781,7 +1954,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); int r; - r = priv->apib->submit_iso_transfer(itransfer); + r = priv->apib->submit_iso_transfer(SUB_API_NOTSET, itransfer); if (r != LIBUSB_SUCCESS) { return r; } @@ -1801,7 +1974,7 @@ static int submit_control_transfer(struct usbi_transfer *itransfer) struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); int r; - r = priv->apib->submit_control_transfer(itransfer); + r = priv->apib->submit_control_transfer(SUB_API_NOTSET, itransfer); if (r != LIBUSB_SUCCESS) { return r; } @@ -1839,7 +2012,7 @@ 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(itransfer); + return priv->apib->abort_control(SUB_API_NOTSET, itransfer); } static int windows_abort_transfers(struct usbi_transfer *itransfer) @@ -1847,7 +2020,7 @@ 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(itransfer); + return priv->apib->abort_transfers(SUB_API_NOTSET, itransfer); } static int windows_cancel_transfer(struct usbi_transfer *itransfer) @@ -1871,13 +2044,13 @@ static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); - int status; + int status, istatus; - usbi_dbg("handling I/O completion with errcode %d", io_result); + 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(itransfer, io_size); + status = priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, io_size); break; case ERROR_GEN_FAILURE: usbi_dbg("detected endpoint stall"); @@ -1888,6 +2061,10 @@ static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t 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; @@ -1897,7 +2074,7 @@ static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t } break; default: - usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error: %s", windows_error_str(0)); + usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error %d: %s", io_result, windows_error_str(0)); status = LIBUSB_TRANSFER_ERROR; break; } @@ -1998,7 +2175,12 @@ unsigned __stdcall windows_clock_gettime_threaded(void* param) // 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: %I64u Hz)", 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 @@ -2035,7 +2217,7 @@ unsigned __stdcall windows_clock_gettime_threaded(void* param) 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 %d: %s", windows_error_str(0)); + usbi_dbg("unable to release timer semaphore: %s", windows_error_str(0)); } continue; case 1: // time to quit @@ -2043,8 +2225,6 @@ unsigned __stdcall windows_clock_gettime_threaded(void* param) return 0; } } - usbi_dbg("ERROR: broken timer thread"); - return 1; } static int windows_clock_gettime(int clk_id, struct timespec *tp) @@ -2096,16 +2276,19 @@ static int windows_clock_gettime(int clk_id, struct timespec *tp) // 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, @@ -2142,67 +2325,73 @@ const struct usbi_os_backend windows_backend = { /* * USB API backends */ -static int unsupported_init(struct libusb_context *ctx) { +static int unsupported_init(int sub_api, struct libusb_context *ctx) { return LIBUSB_SUCCESS; } -static int unsupported_exit(void) { +static int unsupported_exit(int sub_api) { return LIBUSB_SUCCESS; } -static int unsupported_open(struct libusb_device_handle *dev_handle) { +static int unsupported_open(int sub_api, struct libusb_device_handle *dev_handle) { PRINT_UNSUPPORTED_API(open); } -static void unsupported_close(struct libusb_device_handle *dev_handle) { +static void unsupported_close(int sub_api, struct libusb_device_handle *dev_handle) { usbi_dbg("unsupported API call for 'close'"); } -static int unsupported_claim_interface(struct libusb_device_handle *dev_handle, int iface) { +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(struct libusb_device_handle *dev_handle, int iface, int altsetting) { +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(struct libusb_device_handle *dev_handle, int iface) { +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(struct libusb_device_handle *dev_handle, unsigned char endpoint) { +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(struct libusb_device_handle *dev_handle) { +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(struct usbi_transfer *itransfer) { +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(struct usbi_transfer *itransfer) { +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(struct usbi_transfer *itransfer) { +static int unsupported_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer) { PRINT_UNSUPPORTED_API(submit_control_transfer); } -static int unsupported_abort_control(struct usbi_transfer *itransfer) { +static int unsupported_abort_control(int sub_api, struct usbi_transfer *itransfer) { PRINT_UNSUPPORTED_API(abort_control); } -static int unsupported_abort_transfers(struct usbi_transfer *itransfer) { +static int unsupported_abort_transfers(int sub_api, struct usbi_transfer *itransfer) { PRINT_UNSUPPORTED_API(abort_transfers); } -static int unsupported_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) { +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", "NUSB3HUB", "FLXHCIH", "TIHUB3", "ETRONHUB3", "VIAHUB3", "ASMTHUB3"}; +const char* hub_driver_names[] = {"USBHUB", "USBHUB3", "NUSB3HUB", "RUSB3HUB", "FLXHCIH", "TIHUB3", "ETRONHUB3", "VIAHUB3", "ASMTHUB3", "IUSB3HUB"}; const char* composite_driver_names[] = {"USBCCGP"}; -const char* winusb_driver_names[] = {"WINUSB"}; +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", - &CLASS_GUID_UNSUPPORTED, NULL, 0, unsupported_init, unsupported_exit, unsupported_open, unsupported_close, + unsupported_configure_endpoints, unsupported_claim_interface, unsupported_set_interface_altsetting, unsupported_release_interface, @@ -2217,13 +2406,13 @@ const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { }, { USB_API_HUB, "HUB API", - &CLASS_GUID_UNSUPPORTED, hub_driver_names, - sizeof(hub_driver_names)/sizeof(hub_driver_names[0]), + 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, @@ -2238,13 +2427,13 @@ const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { }, { USB_API_COMPOSITE, "Composite API", - &CLASS_GUID_COMPOSITE, composite_driver_names, - sizeof(composite_driver_names)/sizeof(composite_driver_names[0]), + 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, @@ -2257,58 +2446,131 @@ const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { composite_abort_transfers, composite_copy_transfer_data, }, { - USB_API_WINUSB, - "WinUSB API", - &CLASS_GUID_LIBUSB_WINUSB, - winusb_driver_names, - sizeof(winusb_driver_names)/sizeof(winusb_driver_names[0]), - winusb_init, - winusb_exit, - winusb_open, - winusb_close, - winusb_claim_interface, - winusb_set_interface_altsetting, - winusb_release_interface, - winusb_clear_halt, - winusb_reset_device, - winusb_submit_bulk_transfer, + 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, - winusb_submit_control_transfer, - winusb_abort_control, - winusb_abort_transfers, - winusb_copy_transfer_data, + hid_submit_control_transfer, + hid_abort_transfers, + hid_abort_transfers, + hid_copy_transfer_data, }, }; /* - * WinUSB API functions + * WinUSB-like (WinUSB, libusb0/libusbK through libusbk DLL) API functions */ -static int winusb_init(struct libusb_context *ctx) -{ - DLL_LOAD(winusb.dll, WinUsb_Initialize, TRUE); - DLL_LOAD(winusb.dll, WinUsb_Free, TRUE); - DLL_LOAD(winusb.dll, WinUsb_GetAssociatedInterface, TRUE); - DLL_LOAD(winusb.dll, WinUsb_GetDescriptor, TRUE); - DLL_LOAD(winusb.dll, WinUsb_QueryInterfaceSettings, TRUE); - DLL_LOAD(winusb.dll, WinUsb_QueryDeviceInformation, TRUE); - DLL_LOAD(winusb.dll, WinUsb_SetCurrentAlternateSetting, TRUE); - DLL_LOAD(winusb.dll, WinUsb_GetCurrentAlternateSetting, TRUE); - DLL_LOAD(winusb.dll, WinUsb_QueryPipe, TRUE); - DLL_LOAD(winusb.dll, WinUsb_SetPipePolicy, TRUE); - DLL_LOAD(winusb.dll, WinUsb_GetPipePolicy, TRUE); - DLL_LOAD(winusb.dll, WinUsb_ReadPipe, TRUE); - DLL_LOAD(winusb.dll, WinUsb_WritePipe, TRUE); - DLL_LOAD(winusb.dll, WinUsb_ControlTransfer, TRUE); - DLL_LOAD(winusb.dll, WinUsb_ResetPipe, TRUE); - DLL_LOAD(winusb.dll, WinUsb_AbortPipe, TRUE); - DLL_LOAD(winusb.dll, WinUsb_FlushPipe, TRUE); - - api_winusb_available = true; +#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); @@ -2325,12 +2587,12 @@ static int winusb_open(struct libusb_device_handle *dev_handle) HANDLE file_handle; int i; - CHECK_WINUSB_AVAILABLE; + 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_WINUSB) ) { + && (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) { @@ -2351,18 +2613,20 @@ static int winusb_open(struct libusb_device_handle *dev_handle) return LIBUSB_SUCCESS; } -static void winusb_close(struct libusb_device_handle *dev_handle) +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 (!api_winusb_available) + 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_WINUSB) { + 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); @@ -2371,7 +2635,7 @@ static void winusb_close(struct libusb_device_handle *dev_handle) } } -static int winusb_configure_endpoints(struct libusb_device_handle *dev_handle, int iface) +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); @@ -2381,32 +2645,36 @@ static int winusb_configure_endpoints(struct libusb_device_handle *dev_handle, i uint8_t endpoint_address; int i; - CHECK_WINUSB_AVAILABLE; + 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 (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, + 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) continue; // Other policies don't apply to control endpoint + if ((i == -1) || (sub_api == SUB_API_LIBUSB0)) { + continue; // Other policies don't apply to control endpoint or libusb0 + } policy = false; - if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address, + 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 (!WinUsb_SetPipePolicy(winusb_handle, 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); } - if (!WinUsb_SetPipePolicy(winusb_handle, 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 disable ALLOW_PARTIAL_READS for endpoint %02X", endpoint_address); + usbi_dbg("failed to enable ALLOW_PARTIAL_READS for endpoint %02X", endpoint_address); } - policy = true; - if (!WinUsb_SetPipePolicy(winusb_handle, 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); } @@ -2415,18 +2683,26 @@ static int winusb_configure_endpoints(struct libusb_device_handle *dev_handle, i return LIBUSB_SUCCESS; } -static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int iface) +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_WINUSB_AVAILABLE; + 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 interface, we get a handle through WinUsb_Initialize(). + // 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; @@ -2434,35 +2710,64 @@ static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int i return LIBUSB_ERROR_NOT_FOUND; } - if (!WinUsb_Initialize(file_handle, &winusb_handle)) { - usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(0)); + if (!WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) { handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; - - switch(GetLastError()) { - case ERROR_BAD_COMMAND: // The device was disconnected + 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: - usbi_err(ctx, "could not claim interface %d: %s", iface, windows_error_str(0)); - return LIBUSB_ERROR_ACCESS; + // 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)) { + continue; + } + found_filter = true; + break; + } + } + } + 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 WinUsb_GetAssociatedInterface() + // For all other interfaces, use GetAssociatedInterface() winusb_handle = handle_priv->interface_handle[0].api_handle; - // It is a requirement for multiple interface devices using WinUSB that you - // must first claim the first interface before you claim any other + // 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 (WinUsb_Initialize(file_handle, &winusb_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)", iface); + 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 (!WinUsb_GetAssociatedInterface(winusb_handle, (UCHAR)(iface-1), + 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()) { @@ -2484,19 +2789,20 @@ static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int i return LIBUSB_SUCCESS; } -static int winusb_release_interface(struct libusb_device_handle *dev_handle, int iface) +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_WINUSB_AVAILABLE; + 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; } - WinUsb_Free(winusb_handle); + WinUSBX[sub_api].Free(winusb_handle); handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; return LIBUSB_SUCCESS; @@ -2505,16 +2811,23 @@ static int winusb_release_interface(struct libusb_device_handle *dev_handle, int /* * Return the first valid interface (of the same API type), for control transfers */ -static int winusb_get_valid_interface(struct libusb_device_handle *dev_handle) +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) ) { + && (handle_priv->interface_handle[i].api_handle != INVALID_HANDLE_VALUE) + && (priv->usb_interface[i].apib->id == api_id) ) { return i; } } @@ -2544,7 +2857,7 @@ static int interface_by_endpoint(struct windows_device_priv *priv, return -1; } -static int winusb_submit_control_transfer(struct usbi_transfer *itransfer) +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); @@ -2558,7 +2871,7 @@ static int winusb_submit_control_transfer(struct usbi_transfer *itransfer) int current_interface; struct winfd wfd; - CHECK_WINUSB_AVAILABLE; + CHECK_WINUSBX_AVAILABLE(sub_api); transfer_priv->pollable_fd = INVALID_WINFD; size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; @@ -2566,9 +2879,9 @@ static int winusb_submit_control_transfer(struct usbi_transfer *itransfer) if (size > MAX_CTRL_BUFFER_LENGTH) return LIBUSB_ERROR_INVALID_PARAM; - current_interface = winusb_get_valid_interface(transfer->dev_handle); + current_interface = get_valid_interface(transfer->dev_handle, USB_API_WINUSBX); if (current_interface < 0) { - if (auto_claim(transfer, ¤t_interface, USB_API_WINUSB) != LIBUSB_SUCCESS) { + if (auto_claim(transfer, ¤t_interface, USB_API_WINUSBX) != LIBUSB_SUCCESS) { return LIBUSB_ERROR_NOT_FOUND; } } @@ -2576,7 +2889,7 @@ static int winusb_submit_control_transfer(struct usbi_transfer *itransfer) usbi_dbg("will use interface %d", current_interface); winusb_handle = handle_priv->interface_handle[current_interface].api_handle; - wfd = usbi_create_fd(winusb_handle, _O_RDONLY); + 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; @@ -2587,16 +2900,16 @@ static int winusb_submit_control_transfer(struct usbi_transfer *itransfer) && (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.fd); + usbi_free_fd(&wfd); return LIBUSB_ERROR_INVALID_PARAM; } wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; wfd.overlapped->InternalHigh = 0; } else { - if (!WinUsb_ControlTransfer(wfd.handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, wfd.overlapped)) { + if (!WinUSBX[sub_api].ControlTransfer(wfd.handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, wfd.overlapped)) { if(GetLastError() != ERROR_IO_PENDING) { - usbi_err(ctx, "WinUsb_ControlTransfer failed: %s", windows_error_str(0)); - usbi_free_fd(wfd.fd); + usbi_warn(ctx, "ControlTransfer failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); return LIBUSB_ERROR_IO; } } else { @@ -2612,13 +2925,14 @@ static int winusb_submit_control_transfer(struct usbi_transfer *itransfer) return LIBUSB_SUCCESS; } -static int winusb_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) +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_WINUSB_AVAILABLE; + CHECK_WINUSBX_AVAILABLE(sub_api); if (altsetting > 255) { return LIBUSB_ERROR_INVALID_PARAM; @@ -2630,15 +2944,15 @@ static int winusb_set_interface_altsetting(struct libusb_device_handle *dev_hand return LIBUSB_ERROR_NOT_FOUND; } - if (!WinUsb_SetCurrentAlternateSetting(winusb_handle, (UCHAR)altsetting)) { - usbi_err(ctx, "WinUsb_SetCurrentAlternateSetting failed: %s", windows_error_str(0)); + 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 winusb_submit_bulk_transfer(struct usbi_transfer *itransfer) +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); @@ -2650,7 +2964,7 @@ static int winusb_submit_bulk_transfer(struct usbi_transfer *itransfer) int current_interface; struct winfd wfd; - CHECK_WINUSB_AVAILABLE; + CHECK_WINUSBX_AVAILABLE(sub_api); transfer_priv->pollable_fd = INVALID_WINFD; @@ -2664,7 +2978,7 @@ static int winusb_submit_bulk_transfer(struct usbi_transfer *itransfer) winusb_handle = handle_priv->interface_handle[current_interface].api_handle; - wfd = usbi_create_fd(winusb_handle, IS_XFERIN(transfer) ? _O_RDONLY : _O_WRONLY); + 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; @@ -2672,15 +2986,15 @@ static int winusb_submit_bulk_transfer(struct usbi_transfer *itransfer) if (IS_XFERIN(transfer)) { usbi_dbg("reading %d bytes", transfer->length); - ret = WinUsb_ReadPipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped); + 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 = WinUsb_WritePipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped); + 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, "WinUsb_Pipe Transfer failed: %s", windows_error_str(0)); - usbi_free_fd(wfd.fd); + usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); return LIBUSB_ERROR_IO; } } else { @@ -2694,7 +3008,7 @@ static int winusb_submit_bulk_transfer(struct usbi_transfer *itransfer) return LIBUSB_SUCCESS; } -static int winusb_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +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); @@ -2702,7 +3016,7 @@ static int winusb_clear_halt(struct libusb_device_handle *dev_handle, unsigned c HANDLE winusb_handle; int current_interface; - CHECK_WINUSB_AVAILABLE; + CHECK_WINUSBX_AVAILABLE(sub_api); current_interface = interface_by_endpoint(priv, handle_priv, endpoint); if (current_interface < 0) { @@ -2713,8 +3027,8 @@ static int winusb_clear_halt(struct libusb_device_handle *dev_handle, unsigned c usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface); winusb_handle = handle_priv->interface_handle[current_interface].api_handle; - if (!WinUsb_ResetPipe(winusb_handle, endpoint)) { - usbi_err(ctx, "WinUsb_ResetPipe failed: %s", windows_error_str(0)); + if (!WinUSBX[sub_api].ResetPipe(winusb_handle, endpoint)) { + usbi_err(ctx, "ResetPipe failed: %s", windows_error_str(0)); return LIBUSB_ERROR_NO_DEVICE; } @@ -2727,22 +3041,23 @@ static int winusb_clear_halt(struct libusb_device_handle *dev_handle, unsigned c * "You can not call WinUsb_AbortPipe on control pipe. You can possibly cancel * the control transfer using CancelIo" */ -static int winusb_abort_control(struct usbi_transfer *itransfer) +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 winusb_abort_transfers(struct usbi_transfer *itransfer) +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 = usbi_transfer_get_os_priv(itransfer); + 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_WINUSB_AVAILABLE; + CHECK_WINUSBX_AVAILABLE(sub_api); current_interface = transfer_priv->interface_number; if ((current_interface < 0) || (current_interface >= USB_MAXINTERFACES)) { @@ -2753,8 +3068,8 @@ static int winusb_abort_transfers(struct usbi_transfer *itransfer) winusb_handle = handle_priv->interface_handle[current_interface].api_handle; - if (!WinUsb_AbortPipe(winusb_handle, transfer->endpoint)) { - usbi_err(ctx, "WinUsb_AbortPipe failed: %s", windows_error_str(0)); + if (!WinUSBX[sub_api].AbortPipe(winusb_handle, transfer->endpoint)) { + usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0)); return LIBUSB_ERROR_NO_DEVICE; } @@ -2769,8 +3084,8 @@ static int winusb_abort_transfers(struct usbi_transfer *itransfer) * 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 winusb_reset_device(struct libusb_device_handle *dev_handle) +// 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); @@ -2779,7 +3094,7 @@ static int winusb_reset_device(struct libusb_device_handle *dev_handle) HANDLE winusb_handle; int i, j; - CHECK_WINUSB_AVAILABLE; + CHECK_WINUSBX_AVAILABLE(sub_api); // Reset any available pipe (except control) for (i=0; iusb_interface[i].nb_endpoints; j++) { usbi_dbg("resetting ep %02X", priv->usb_interface[i].endpoint[j]); - if (!WinUsb_AbortPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) { - usbi_err(ctx, "WinUsb_AbortPipe (pipe address %02X) failed: %s", + 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]) - && (!WinUsb_FlushPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) ) { - usbi_err(ctx, "WinUsb_FlushPipe (pipe address %02X) failed: %s", + && (!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 (!WinUsb_ResetPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) { - usbi_err(ctx, "WinUsb_ResetPipe (pipe address %02X) failed: %s", + 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 winusb_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) +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; } - /* - * Composite API functions + * 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 composite_init(struct libusb_context *ctx) +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) { - return LIBUSB_SUCCESS; + int i = 0; + while (str[i] && (str[i] != 0x409)) { + i++; + } + return i; } -static int composite_exit(void) +static int _hid_get_device_descriptor(struct hid_device_priv* dev, void *data, size_t *size) { - return LIBUSB_SUCCESS; + 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 composite_open(struct libusb_device_handle *dev_handle) +static int _hid_get_config_descriptor(struct hid_device_priv* dev, void *data, size_t *size) { - struct windows_device_priv *priv = _device_priv(dev_handle->dev); - unsigned api; - int r; - uint8_t flag = 1<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); + } - for (api=USB_API_WINUSB; apicomposite_api_flags & flag) { - r = usb_api_backend[api].open(dev_handle); - if (r != LIBUSB_SUCCESS) { - return r; - } - } - flag <<= 1; + 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; } - return LIBUSB_SUCCESS; + + if (*size > config_total_len) + *size = config_total_len; + memcpy(data, tmp, *size); + return LIBUSB_COMPLETED; } -static void composite_close(struct libusb_device_handle *dev_handle) +static int _hid_get_string_descriptor(struct hid_device_priv* dev, int _index, + void *data, size_t *size) { - struct windows_device_priv *priv = _device_priv(dev_handle->dev); - unsigned api; - uint8_t flag = 1< 255)) { + return LIBUSB_ERROR_OVERFLOW; + } - for (api=USB_API_WINUSB; apicomposite_api_flags & flag) { - usb_api_backend[api].close(dev_handle); + 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; } - flag <<= 1; } + + 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 composite_claim_interface(struct libusb_device_handle *dev_handle, int iface) +static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size) { - struct windows_device_priv *priv = _device_priv(dev_handle->dev); - return priv->usb_interface[iface].apib->claim_interface(dev_handle, iface); + 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 composite_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) +static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size) { - struct windows_device_priv *priv = _device_priv(dev_handle->dev); - return priv->usb_interface[iface].apib->set_interface_altsetting(dev_handle, iface, altsetting); + 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 composite_release_interface(struct libusb_device_handle *dev_handle, int iface) +static int _hid_get_descriptor(struct hid_device_priv* dev, HANDLE hid_handle, int recipient, + int type, int _index, void *data, size_t *size) { - struct windows_device_priv *priv = _device_priv(dev_handle->dev); - return priv->usb_interface[iface].apib->release_interface(dev_handle, iface); + 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 composite_submit_control_transfer(struct usbi_transfer *itransfer) +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) { - 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; + uint8_t *buf; + DWORD ioctl_code, read_size, expected_size = (DWORD)*size; + int r = LIBUSB_SUCCESS; - for (i=0; iusb_interface[i].path != NULL) { - usbi_dbg("using interface %d", i); - return priv->usb_interface[i].apib->submit_control_transfer(itransfer); - } + if (tp->hid_buffer != NULL) { + usbi_dbg("program assertion failed: hid_buffer is not NULL"); } - usbi_err(ctx, "no libusb supported interfaces to complete request"); - return LIBUSB_ERROR_NOT_FOUND; -} + if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) { + usbi_dbg("invalid size (%d)", *size); + return LIBUSB_ERROR_INVALID_PARAM; + } -static int composite_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_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; + 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; + } - 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; + // 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; } - return priv->usb_interface[current_interface].apib->submit_bulk_transfer(itransfer); + // 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 composite_submit_iso_transfer(struct usbi_transfer *itransfer) { +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); - int current_interface; + 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) { @@ -2934,67 +4011,394 @@ static int composite_submit_iso_transfer(struct usbi_transfer *itransfer) { return LIBUSB_ERROR_NOT_FOUND; } - return priv->usb_interface[current_interface].apib->submit_iso_transfer(itransfer); + 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 composite_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +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; } - return priv->usb_interface[current_interface].apib->clear_halt(dev_handle, endpoint); + 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; } -static int composite_abort_control(struct usbi_transfer *itransfer) -{ +// 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 + // 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); + } + } + } - return priv->usb_interface[transfer_priv->interface_number].apib->abort_control(itransfer); + usbi_err(ctx, "no libusbx supported interfaces to complete request"); + return LIBUSB_ERROR_NOT_FOUND; } -static int composite_abort_transfers(struct usbi_transfer *itransfer) +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_transfers(itransfer); -} + return priv->usb_interface[transfer_priv->interface_number].apib-> + abort_control(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer);} -static int composite_reset_device(struct libusb_device_handle *dev_handle) +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); - unsigned api; int r; - uint8_t flag = 1<composite_api_flags & flag) { - r = usb_api_backend[api].reset_device(dev_handle); + 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(itransfer, io_size); + 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/compat/libusb-1.0/libusb/os/windows_usb.h b/compat/libusb-1.0/libusb/os/windows_usb.h index ddbd6807..5d67a562 100644 --- a/compat/libusb-1.0/libusb/os/windows_usb.h +++ b/compat/libusb-1.0/libusb/os/windows_usb.h @@ -1,6 +1,6 @@ /* - * Windows backend for libusb 1.0 - * Copyright (C) 2009-2010 Pete Batard + * Windows backend for libusbx 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 @@ -22,6 +22,8 @@ #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 @@ -30,17 +32,6 @@ #pragma warning(disable:4201) // nameless struct/union #endif -// Windows API default is uppercase - ugh! -#if !defined(bool) -#define bool BOOL -#endif -#if !defined(true) -#define true TRUE -#endif -#if !defined(false) -#define false FALSE -#endif - // Missing from MSVC6 setupapi.h #if !defined(SPDRP_ADDRESS) #define SPDRP_ADDRESS 28 @@ -50,45 +41,30 @@ #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 safe_free(p) do {if (p != NULL) {free((void*)p); p = NULL;}} while(0) -#define safe_closehandle(h) do {if (h != INVALID_HANDLE_VALUE) {CloseHandle(h); h = INVALID_HANDLE_VALUE;}} while(0) -#define safe_min(a, b) min((size_t)(a), (size_t)(b)) -#define safe_strcp(dst, dst_max, src, count) do {memcpy(dst, src, safe_min(count, dst_max)); \ - ((char*)dst)[safe_min(count, dst_max)-1] = 0;} while(0) -#define safe_strcpy(dst, dst_max, src) safe_strcp(dst, dst_max, src, safe_strlen(src)+1) -#define safe_strncat(dst, dst_max, src, count) strncat(dst, src, safe_min(count, dst_max - safe_strlen(dst) - 1)) -#define safe_strcat(dst, dst_max, src) safe_strncat(dst, dst_max, src, safe_strlen(src)+1) -#define safe_strcmp(str1, str2) strcmp(((str1==NULL)?"":str1), ((str2==NULL)?"":str2)) -#define safe_strncmp(str1, str2, count) strncmp(((str1==NULL)?"":str1), ((str2==NULL)?"":str2), count) -#define safe_strlen(str) ((str==NULL)?0:strlen(str)) -#define safe_sprintf _snprintf -#define safe_unref_device(dev) do {if (dev != NULL) {libusb_unref_device(dev); dev = NULL;}} while(0) -#define wchar_to_utf8_ms(wstr, str, strlen) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, strlen, NULL, NULL) -static inline void upperize(char* str) { - size_t i; - if (str == NULL) return; - for (i=0; iparent_dev = NULL; p->path = NULL; p->apib = &usb_api_backend[USB_API_UNSUPPORTED]; - p->composite_api_flags = 0; + 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; } } @@ -203,6 +262,7 @@ static inline void windows_device_priv_release(libusb_device* dev) { 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); @@ -230,6 +290,9 @@ static inline struct windows_device_handle_priv *_device_handle_priv( 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 @@ -239,37 +302,6 @@ struct driver_lookup { const char* designation; // internal designation (for debug output) }; -/* - * API macros - from libusb-win32 1.x - */ -#define DLL_DECLARE_PREFIXNAME(api, ret, prefixname, name, args) \ - typedef ret (api * __dll_##name##_t)args; \ - static __dll_##name##_t prefixname = NULL - -#define DLL_LOAD_PREFIXNAME(dll, prefixname, name, ret_on_failure) \ - do { \ - HMODULE h = GetModuleHandleA(#dll); \ - if (!h) \ - h = LoadLibraryA(#dll); \ - if (!h) { \ - if (ret_on_failure) { return LIBUSB_ERROR_NOT_FOUND; }\ - else { break; } \ - } \ - prefixname = (__dll_##name##_t)GetProcAddress(h, #name); \ - if (prefixname) break; \ - prefixname = (__dll_##name##_t)GetProcAddress(h, #name "A"); \ - if (prefixname) break; \ - prefixname = (__dll_##name##_t)GetProcAddress(h, #name "W"); \ - if (prefixname) break; \ - if(ret_on_failure) \ - return LIBUSB_ERROR_NOT_FOUND; \ - } while(0) - -#define DLL_DECLARE(api, ret, name, args) DLL_DECLARE_PREFIXNAME(api, ret, name, name, args) -#define DLL_LOAD(dll, name, ret_on_failure) DLL_LOAD_PREFIXNAME(dll, name, name, ret_on_failure) -#define DLL_DECLARE_PREFIXED(api, ret, prefix, name, args) DLL_DECLARE_PREFIXNAME(api, ret, prefix##name, name, args) -#define DLL_LOAD_PREFIXED(dll, prefix, name, ret_on_failure) DLL_LOAD_PREFIXNAME(dll, prefix##name, name, ret_on_failure) - /* OLE32 dependency */ DLL_DECLARE_PREFIXED(WINAPI, HRESULT, p, CLSIDFromString, (LPCOLESTR, LPCLSID)); @@ -284,6 +316,7 @@ 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)); @@ -589,20 +622,297 @@ typedef struct { typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; -DLL_DECLARE(WINAPI, BOOL, WinUsb_Initialize, (HANDLE, PWINUSB_INTERFACE_HANDLE)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_Free, (WINUSB_INTERFACE_HANDLE)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_GetAssociatedInterface, (WINUSB_INTERFACE_HANDLE, UCHAR, PWINUSB_INTERFACE_HANDLE)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_GetDescriptor, (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, USHORT, PUCHAR, ULONG, PULONG)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryInterfaceSettings, (WINUSB_INTERFACE_HANDLE, UCHAR, PUSB_INTERFACE_DESCRIPTOR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryDeviceInformation, (WINUSB_INTERFACE_HANDLE, ULONG, PULONG, PVOID)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_SetCurrentAlternateSetting, (WINUSB_INTERFACE_HANDLE, UCHAR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_GetCurrentAlternateSetting, (WINUSB_INTERFACE_HANDLE, PUCHAR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryPipe, (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, PWINUSB_PIPE_INFORMATION)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_SetPipePolicy, (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, ULONG, PVOID)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_GetPipePolicy, (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, PULONG, PVOID)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_ReadPipe, (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_WritePipe, (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_ControlTransfer, (WINUSB_INTERFACE_HANDLE, WINUSB_SETUP_PACKET, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_ResetPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_AbortPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_FlushPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); +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/compat/libusb-1.0/libusb/strerror.c b/compat/libusb-1.0/libusb/strerror.c new file mode 100644 index 00000000..0212df87 --- /dev/null +++ b/compat/libusb-1.0/libusb/strerror.c @@ -0,0 +1,184 @@ +/* + * 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/libusbx/libusbx/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 libusbx-devel\@lists.sourceforge.net
  12. + *
+ */ + +static const char* usbi_locale_supported[] = { "en", "nl", "fr" }; +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", + }, { /* Dutch (nl) */ + "Gelukt", + "Invoer-/uitvoerfout", + "Ongeldig argument", + "Toegang geweigerd (onvoldoende toegangsrechten)", + "Apparaat bestaat niet (verbinding met apparaat verbroken?)", + "Niet gevonden", + "Apparaat of hulpbron is bezig", + "Bewerking verlopen", + "Waarde is te groot", + "Gebroken pijp", + "Onderbroken systeemaanroep", + "Onvoldoende geheugen beschikbaar", + "Bewerking wordt niet ondersteund", + "Andere fout", + }, { /* French (fr) */ + "Succès", + "Erreur d'entrée/sortie", + "Paramètre invalide", + "Accès refusé (permissions insuffisantes)", + "Périphérique introuvable (peut-être déconnecté)", + "Elément introuvable", + "Resource déjà occupée", + "Operation expirée", + "Débordement", + "Erreur de pipe", + "Appel système abandonné (peut-être à cause d’un signal)", + "Mémoire insuffisante", + "Opération non supportée or non implémentée sur cette plateforme", + "Autre erreur" + } +}; + +/** \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/compat/libusb-1.0/libusb/sync.c b/compat/libusb-1.0/libusb/sync.c index ac3ab7e0..42e486db 100644 --- a/compat/libusb-1.0/libusb/sync.c +++ b/compat/libusb-1.0/libusb/sync.c @@ -1,6 +1,6 @@ /* - * Synchronous I/O functions for libusb - * Copyright (C) 2007-2008 Daniel Drake + * Synchronous I/O functions for libusbx + * 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 @@ -17,7 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include "config.h" #include #include #include @@ -28,12 +28,12 @@ /** * @defgroup syncio Synchronous device I/O * - * This page documents libusb's synchronous (blocking) API for USB device I/O. + * This page documents libusbx'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 ctrl_transfer_cb(struct libusb_transfer *transfer) +static void LIBUSB_CALL sync_transfer_cb(struct libusb_transfer *transfer) { int *completed = transfer->user_data; *completed = 1; @@ -41,6 +41,24 @@ static void LIBUSB_CALL ctrl_transfer_cb(struct libusb_transfer *transfer) /* 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. * @@ -81,7 +99,7 @@ int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle, if (!transfer) return LIBUSB_ERROR_NO_MEM; - buffer = malloc(LIBUSB_CONTROL_SETUP_SIZE + wLength); + buffer = (unsigned char*) malloc(LIBUSB_CONTROL_SETUP_SIZE + wLength); if (!buffer) { libusb_free_transfer(transfer); return LIBUSB_ERROR_NO_MEM; @@ -93,7 +111,7 @@ int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle, memcpy(buffer + LIBUSB_CONTROL_SETUP_SIZE, data, wLength); libusb_fill_control_transfer(transfer, dev_handle, buffer, - ctrl_transfer_cb, &completed, timeout); + sync_transfer_cb, &completed, timeout); transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER; r = libusb_submit_transfer(transfer); if (r < 0) { @@ -101,19 +119,7 @@ int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle, return r; } - while (!completed) { - r = libusb_handle_events_completed(HANDLE_CTX(dev_handle), &completed); - if (r < 0) { - if (r == LIBUSB_ERROR_INTERRUPTED) - continue; - libusb_cancel_transfer(transfer); - while (!completed) - if (libusb_handle_events_completed(HANDLE_CTX(dev_handle), &completed) < 0) - break; - 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), @@ -149,14 +155,6 @@ int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle, return r; } -static void LIBUSB_CALL bulk_transfer_cb(struct libusb_transfer *transfer) -{ - int *completed = transfer->user_data; - *completed = 1; - usbi_dbg("actual_length=%d", transfer->actual_length); - /* caller interprets results and frees transfer */ -} - 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) @@ -169,7 +167,7 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, return LIBUSB_ERROR_NO_MEM; libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length, - bulk_transfer_cb, &completed, timeout); + sync_transfer_cb, &completed, timeout); transfer->type = type; r = libusb_submit_transfer(transfer); @@ -178,19 +176,7 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, return r; } - while (!completed) { - r = libusb_handle_events_completed(HANDLE_CTX(dev_handle), &completed); - if (r < 0) { - if (r == LIBUSB_ERROR_INTERRUPTED) - continue; - libusb_cancel_transfer(transfer); - while (!completed) - if (libusb_handle_events_completed(HANDLE_CTX(dev_handle), &completed) < 0) - break; - libusb_free_transfer(transfer); - return r; - } - } + sync_transfer_wait_for_completion(transfer); *transferred = transfer->actual_length; switch (transfer->status) { @@ -236,9 +222,9 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, * 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 + * libusbx 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 + * the first few chunks have completed. libusbx 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. * @@ -284,9 +270,9 @@ int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle, * 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 + * libusbx 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 + * the first few chunks have completed. libusbx 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. * @@ -319,4 +305,3 @@ int API_EXPORTED libusb_interrupt_transfer( return do_sync_bulk_transfer(dev_handle, endpoint, data, length, transferred, timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT); } - diff --git a/compat/libusb-1.0/libusb/version.h b/compat/libusb-1.0/libusb/version.h index 01ee5efe..09d9a9f5 100644 --- a/compat/libusb-1.0/libusb/version.h +++ b/compat/libusb-1.0/libusb/version.h @@ -1,4 +1,5 @@ /* 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 @@ -6,13 +7,12 @@ #define LIBUSB_MINOR 0 #endif #ifndef LIBUSB_MICRO -#define LIBUSB_MICRO 16 +#define LIBUSB_MICRO 17 #endif -/* LIBUSB_NANO may be used for Windows internal versioning. 0 means unused. */ #ifndef LIBUSB_NANO #define LIBUSB_NANO 0 #endif /* LIBUSB_RC is the release candidate suffix. Should normally be empty. */ #ifndef LIBUSB_RC -#define LIBUSB_RC "-rc10" +#define LIBUSB_RC "" #endif diff --git a/compat/libusb-1.0/libusb/version_nano.h b/compat/libusb-1.0/libusb/version_nano.h new file mode 100644 index 00000000..dfa56643 --- /dev/null +++ b/compat/libusb-1.0/libusb/version_nano.h @@ -0,0 +1 @@ +#define LIBUSB_NANO 10830