diff options
author | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-20 18:16:43 +0000 |
---|---|---|
committer | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-20 18:16:43 +0000 |
commit | afed56f762adab7c30f9f91cc6e51308e75c8d5f (patch) | |
tree | 611fc8b0640055507f8cf74de8aa45eeaead2381 | |
parent | 792431fdd053b8f1b3793bfe1eef3001aecaad27 (diff) | |
download | chromium_src-afed56f762adab7c30f9f91cc6e51308e75c8d5f.zip chromium_src-afed56f762adab7c30f9f91cc6e51308e75c8d5f.tar.gz chromium_src-afed56f762adab7c30f9f91cc6e51308e75c8d5f.tar.bz2 |
Update libusb 1.0.9 to libusbx 1.0.16.
libusb 1.0.9 has serious deadlock bugs on Windows. libusbx is the rapid-development version of libusb and will be eventually merged into libusb.
The original commit broke the build. This commit is to fix it.
BUG=223817
TBR=pfeldman@chromium.org
Review URL: https://codereview.chromium.org/19490008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@212797 0039d316-1c4b-4281-b951-d872f2087c98
69 files changed, 9004 insertions, 6796 deletions
diff --git a/third_party/libusb/README.chromium b/third_party/libusb/README.chromium index 61f6e40..b0e8ada6 100644 --- a/third_party/libusb/README.chromium +++ b/third_party/libusb/README.chromium @@ -1,16 +1,17 @@ -Name: libusb +Name: libusbx URL: http://libusb.org -Version: 1.0.9 +Version: 1.0.16 License: LGPL 2.1 License File: src/COPYING Security Critical: yes Description: -libusb provides a platform-agnostic API for accessing the USB subsystem on Mac, -Windows, and Linux systems. +libusbx provides a platform-agnostic API for accessing the USB subsystem on Mac, +Windows, and Linux systems. All the unused files are removed. Local Modifications: -- config.h (empty) has been created to satisfy includes within the tree. -- Exposing an API (libusb_interrupt_handle_event) to explicitly interrupt +- License headers fixed. +- config.h has been created to satisfy includes within the tree. +- Exposing an API (libusb_interrupt_handle_event) to explicitly interrupt libusb_handle_event. - windows-build.patch has been applied. diff --git a/third_party/libusb/libusb.gyp b/third_party/libusb/libusb.gyp index 46e9e6f..0aba182 100644 --- a/third_party/libusb/libusb.gyp +++ b/third_party/libusb/libusb.gyp @@ -9,15 +9,18 @@ 'type': 'static_library', 'sources': [ 'src/config.h', - 'src/libusb/libusb.h', - 'src/libusb/libusbi.h', 'src/libusb/core.c', 'src/libusb/descriptor.c', + 'src/libusb/hotplug.c', + 'src/libusb/hotplug.h', + 'src/libusb/interrupt.c', + 'src/libusb/interrupt.h', 'src/libusb/io.c', + 'src/libusb/libusb.h', + 'src/libusb/libusbi.h', 'src/libusb/sync.c', 'src/libusb/version.h', - 'src/libusb/interrupt.c', - 'src/libusb/interrupt.h', + 'src/libusb/version_nano.h', ], 'include_dirs': [ 'src', @@ -30,10 +33,9 @@ ], }, 'conditions': [ - [ 'OS == "linux" or OS == "android"', { + [ 'OS == "linux" or OS == "android" or OS == "mac"', { 'sources': [ - 'src/libusb/os/linux_usbfs.c', - 'src/libusb/os/linux_usbfs.h', + 'src/libusb/os/poll_posix.c', 'src/libusb/os/poll_posix.h', 'src/libusb/os/threads_posix.c', 'src/libusb/os/threads_posix.h', @@ -43,19 +45,40 @@ 'HAVE_GETTIMEOFDAY=1', 'HAVE_POLL_H=1', 'HAVE_SYS_TIME_H=1', - 'LIBUSB_DESCRIBE="1.0.9"', - 'OS_LINUX=1', + 'LIBUSB_DESCRIBE="1.0.16"', 'POLL_NFDS_TYPE=nfds_t', 'THREADS_POSIX=1', + ], + }], + [ 'OS == "linux" or OS == "android"', { + 'sources': [ + 'src/libusb/os/linux_udev.c', + 'src/libusb/os/linux_usbfs.c', + 'src/libusb/os/linux_usbfs.h', + ], + 'defines': [ + 'HAVE_LIBUDEV=1', + 'OS_LINUX=1', + 'USE_UDEV=1', '_GNU_SOURCE=1', ], }], - ['OS == "win"', { + [ 'OS == "mac"', { + 'sources': [ + 'src/libusb/os/darwin_usb.c', + 'src/libusb/os/darwin_usb.h', + ], + 'defines': [ + 'OS_DARWIN=1', + ], + }], + [ 'OS == "win"', { 'sources': [ 'src/libusb/os/poll_windows.c', 'src/libusb/os/poll_windows.h', 'src/libusb/os/threads_windows.c', 'src/libusb/os/threads_windows.h', + 'src/libusb/os/windows_common.h', 'src/libusb/os/windows_usb.c', 'src/libusb/os/windows_usb.h', 'src/msvc/config.h', @@ -68,29 +91,7 @@ 'include_dirs': [ 'src/msvc', ], - 'defines': [ - 'DDKBUILD=1', - ] - }], - ['OS == "mac"', { - 'sources': [ - 'src/libusb/os/darwin_usb.c', - 'src/libusb/os/darwin_usb.h', - 'src/libusb/os/poll_posix.h', - 'src/libusb/os/threads_posix.c', - 'src/libusb/os/threads_posix.h', - ], - 'defines': [ - 'DEFAULT_VISIBILITY=', - 'HAVE_GETTIMEOFDAY=1', - 'HAVE_POLL_H=1', - 'HAVE_SYS_TIME_H=1', - 'LIBUSB_DESCRIBE="1.0.9"', - 'OS_DARWIN=1', - 'POLL_NFDS_TYPE=nfds_t', - 'THREADS_POSIX=1', - '_GNU_SOURCE=1', - ], + 'msvs_disabled_warnings': [ 4267 ], }], ], }, diff --git a/third_party/libusb/src/AUTHORS b/third_party/libusb/src/AUTHORS index b43d995..6c29e21 100644 --- a/third_party/libusb/src/AUTHORS +++ b/third_party/libusb/src/AUTHORS @@ -1,45 +1,60 @@ -Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org> -Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> -Copyright (C) 2008-2012 Nathan Hjelm <hjelmn@users.sourceforge.net> -Copyright (C) 2009-2012 Pete Batard <pete@akeo.ie> -Copyright (C) 2010 Michael Plante <michael.plante@gmail.com> -Copyright (C) 2010-2012 Peter Stuge <peter@stuge.se> -Copyright (C) 2011-2012 Hans de Goede <hdegoede@redhat.com> -Copyright (C) 2012 Martin Pieuchot <mpi@openbsd.org> +Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com> +Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org> +Copyright © 2010-2012 Peter Stuge <peter@stuge.se> +Copyright © 2008-2011 Nathan Hjelm <hjelmn@users.sourceforge.net> +Copyright © 2009-2012 Pete Batard <pete@akeo.ie> +Copyright © 2009-2012 Ludovic Rousseau <ludovic.rousseau@gmail.com> +Copyright © 2010-2012 Michael Plante <michael.plante@gmail.com> +Copyright © 2011-2012 Hans de Goede <hdegoede@redhat.com> +Copyright © 2012 Martin Pieuchot <mpi@openbsd.org> +Copyright © 2012-2013 Toby Gray <toby.gray@realvnc.com> 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 +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 +Matthias Bolte Mike Frysinger Mikhail Gusarov +Nicholas Corgan Orin Eman 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/third_party/libusb/src/INSTALL b/third_party/libusb/src/INSTALL deleted file mode 100644 index 5458714..0000000 --- a/third_party/libusb/src/INSTALL +++ /dev/null @@ -1,234 +0,0 @@ -Installation Instructions -************************* - -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006 Free Software Foundation, Inc. - -This file is free documentation; the Free Software Foundation gives -unlimited permission to copy, distribute and modify it. - -Basic Installation -================== - -Briefly, the shell commands `./configure; make; make install' should -configure, build, and install this package. The following -more-detailed instructions are generic; see the `README' file for -instructions specific to this package. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, and a -file `config.log' containing compiler output (useful mainly for -debugging `configure'). - - It can also use an optional file (typically called `config.cache' -and enabled with `--cache-file=config.cache' or simply `-C') that saves -the results of its tests to speed up reconfiguring. Caching is -disabled by default to prevent problems with accidental use of stale -cache files. - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If you are using the cache, and at -some point `config.cache' contains results you don't want to keep, you -may remove or edit it. - - The file `configure.ac' (or `configure.in') is used to create -`configure' by a program called `autoconf'. You need `configure.ac' if -you want to change it or regenerate `configure' using a newer version -of `autoconf'. - -The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. - - Running `configure' might take a while. While running, it prints - some messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package. - - 4. Type `make install' to install the programs and any data files and - documentation. - - 5. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - -Compilers and Options -===================== - -Some systems require unusual options for compilation or linking that the -`configure' script does not know about. Run `./configure --help' for -details on some of the pertinent environment variables. - - You can give `configure' initial values for configuration parameters -by setting variables in the command line or in the environment. Here -is an example: - - ./configure CC=c99 CFLAGS=-g LIBS=-lposix - - *Note Defining Variables::, for more details. - -Compiling For Multiple Architectures -==================================== - -You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you can use GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. - - With a non-GNU `make', it is safer to compile the package for one -architecture at a time in the source code directory. After you have -installed the package for one architecture, use `make distclean' before -reconfiguring for another architecture. - -Installation Names -================== - -By default, `make install' installs the package's commands under -`/usr/local/bin', include files under `/usr/local/include', etc. You -can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX'. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -pass the option `--exec-prefix=PREFIX' to `configure', the package uses -PREFIX as the prefix for installing programs and libraries. -Documentation and other data files still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=DIR' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - -Optional Features -================= - -Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - -Specifying the System Type -========================== - -There may be some features `configure' cannot figure out automatically, -but needs to determine by the type of machine the package will run on. -Usually, assuming the package is built to be run on the _same_ -architectures, `configure' can figure that out, but if it prints a -message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - -Sharing Defaults -================ - -If you want to set default values for `configure' scripts to share, you -can create a site shell script called `config.site' that gives default -values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Defining Variables -================== - -Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). - -Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: - - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash - -`configure' Invocation -====================== - -`configure' recognizes the following options to control how it operates. - -`--help' -`-h' - Print a summary of the options to `configure', and exit. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. - diff --git a/third_party/libusb/src/Makefile.am b/third_party/libusb/src/Makefile.am deleted file mode 100644 index ce7b7e2..0000000 --- a/third_party/libusb/src/Makefile.am +++ /dev/null @@ -1,28 +0,0 @@ -AUTOMAKE_OPTIONS = dist-bzip2 no-dist-gzip -ACLOCAL_AMFLAGS = -I m4 -DISTCLEANFILES = libusb-1.0.pc -MAINTAINERCLEANFILES = ChangeLog -EXTRA_DIST = TODO PORTING msvc -SUBDIRS = libusb doc - -if BUILD_EXAMPLES -SUBDIRS += examples -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 - -reldir = .release/$(distdir) -dist-up: dist - rm -rf $(reldir) - mkdir -p $(reldir) - 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/third_party/libusb/src/NEWS b/third_party/libusb/src/NEWS deleted file mode 100644 index f948700..0000000 --- a/third_party/libusb/src/NEWS +++ /dev/null @@ -1,65 +0,0 @@ -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 - diff --git a/third_party/libusb/src/PORTING b/third_party/libusb/src/PORTING deleted file mode 100644 index 7070784..0000000 --- a/third_party/libusb/src/PORTING +++ /dev/null @@ -1,95 +0,0 @@ -PORTING LIBUSB 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 -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 -your target platform. - -In terms of USB I/O, your backend provides functionality to submit -asynchronous transfers (synchronous transfers are implemented in the higher -layers, based on the async interface). Your backend must also provide -functionality to cancel those transfers. - -Your backend must also provide an event handling function to "reap" ongoing -transfers and process their results. - -The backend must also provide standard functions for other USB operations, -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 -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 -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. - - -Interface semantics and documentation -===================================== - -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 -can be found in the .c files where they are implemented. - -You probably want to skim over *all* the documentation before starting your -implementation. For example, you probably need to allocate and store private -OS-specific data for device handles, but the documentation for the mechanism -for doing so is probably not the first thing you will see. - -The Linux backend acts as a good example - view it as a reference -implementation which you should try to match the behaviour of. - - -Getting started -=============== - -1. Modify configure.ac to detect your platform appropriately (see the OS_LINUX -stuff for an example). - -2. Implement your backend in the libusb/os/ directory, modifying -libusb/os/Makefile.am appropriately. - -3. Add preprocessor logic to the top of libusb/core.c to statically assign the -right usbi_backend for your platform. - -4. Produce and test your implementation. - -5. Send your implementation to libusb-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 -platforms. - -The libusb-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. - -You are encouraged to get involved in the process; if the library needs -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/third_party/libusb/src/README b/third_party/libusb/src/README deleted file mode 100644 index 08ae169..0000000 --- a/third_party/libusb/src/README +++ /dev/null @@ -1,22 +0,0 @@ -libusb -====== - -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). - -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. :) - -libusb homepage: -http://libusb.org/ - -Developers will wish to consult the API documentation: -http://libusb.sourceforge.net/api-1.0/ - -Use the mailing list for questions, comments, etc: -http://libusb.org/wiki/MailingList - -- Peter Stuge <peter@stuge.se> -(use the mailing list rather than mailing developers directly) diff --git a/third_party/libusb/src/RELEASE-NOTES b/third_party/libusb/src/RELEASE-NOTES deleted file mode 100644 index b8fe795..0000000 --- a/third_party/libusb/src/RELEASE-NOTES +++ /dev/null @@ -1,35 +0,0 @@ -libusb 1.0.9 - -This release has taken much too long to finish, but this also means that it -has a long list of bugs fixes and many other improvements. - -Perhaps the most exciting improvement is the addition of the backend for -Microsoft Windows, which closes ticket #1. - -A very special thanks goes to Pete Batard, Michael Plante, Tim Roberts, -Orin Eman, Graeme Gill, and everyone else in the community who help work -on the Windows support! - -Because Windows is a peculiar beast you may encounter some bugs when using -this first release with the Windows backend. In that case, please get in -touch with the libusb community so that we can help resolve them. File a -ticket at http://libusb.org/newticket or let us know via email or on IRC. -Visit http://libusb.org/ for all contact details. - -Another exciting improvement is the new OpenBSD backend, which also works -on NetBSD systems. - -All known downstream bugs and all known severe bugs reported directly to -libusb.org have been fixed in this release, so please report new ones! - - -This release would not have been what it is without help, code, reports, -and advice from friends like these: - - Alan Ott, Alan Stern, Brian Shirley, Dave Camarillo, Graeme Gill, - Hans de Goede, Hector Martin, James Hanko, Konrad Rzepecki, - Ludovic Rousseau, Martin Pieuchot, Mike Frysinger, Orin Eman, - Pekka Nikander, Pete Batard, Sean McBride, Sebastian Pipping, - Stephan Meyer, Thomas Röfer, Trygve Laugstøl, Vitali Lovich, Xiaofan Chen - - Thanks! diff --git a/third_party/libusb/src/THANKS b/third_party/libusb/src/THANKS deleted file mode 100644 index d926126..0000000 --- a/third_party/libusb/src/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/third_party/libusb/src/TODO b/third_party/libusb/src/TODO deleted file mode 100644 index 6c162a3..0000000 --- a/third_party/libusb/src/TODO +++ /dev/null @@ -1,9 +0,0 @@ -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 diff --git a/third_party/libusb/src/autogen.sh b/third_party/libusb/src/autogen.sh deleted file mode 100755 index c7bb679..0000000 --- a/third_party/libusb/src/autogen.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -# use libtoolize if available, otherwise look for glibtoolize (darwin) -if (libtoolize --version) < /dev/null > /dev/null 2>&1; then - LIBTOOLIZE=libtoolize -elif (glibtoolize --version) < /dev/null > /dev/null 2>&1; then - LIBTOOLIZE=glibtoolize -else - echo "libtoolize or glibtoolize was not found! Please install libtool." - exit -fi - -$LIBTOOLIZE --copy --force || exit 1 -aclocal || exit 1 -autoheader || exit 1 -autoconf || exit 1 -automake -a -c || exit 1 -./configure --enable-maintainer-mode --enable-debug-log \ - --enable-examples-build $* diff --git a/third_party/libusb/src/config.h b/third_party/libusb/src/config.h index 8b13789..e13e1d2 100644 --- a/third_party/libusb/src/config.h +++ b/third_party/libusb/src/config.h @@ -1 +1,15 @@ - +/* + * 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 + */ diff --git a/third_party/libusb/src/configure.ac b/third_party/libusb/src/configure.ac deleted file mode 100644 index f9e648e..0000000 --- a/third_party/libusb/src/configure.ac +++ /dev/null @@ -1,222 +0,0 @@ -dnl These m4 macros are whitespace sensitive and break if moved around much. -m4_define([LU_VERSION_H], m4_include([libusb/version.h])) -m4_define([LU_DEFINE_VERSION_ATOM], - [m4_define([$1], m4_bregexp(LU_VERSION_H, - [^#define\s*$1\s*\([0-9]*\).*], [\1]))]) -m4_define([LU_DEFINE_VERSION_RC_ATOM], - [m4_define([$1], m4_bregexp(LU_VERSION_H, - [^#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(). - -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/]) - -# 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="1" -lt_revision="0" -lt_age="1" -LTLDFLAGS="-version-info ${lt_current}:${lt_revision}:${lt_age}" - -AM_INIT_AUTOMAKE -AM_MAINTAINER_MODE - -AC_CONFIG_SRCDIR([libusb/core.c]) -AC_CONFIG_MACRO_DIR([m4]) -AM_CONFIG_HEADER([config.h]) -m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) - -AC_PREREQ([2.50]) -AC_PROG_CC -AC_PROG_LIBTOOL -LT_LANG([Windows Resource]) -AC_C_INLINE -AM_PROG_CC_C_O -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" - ;; -*-darwin*) - AC_MSG_RESULT([Darwin/Mac OS X]) - backend="darwin" - ;; -*-openbsd*) - AC_MSG_RESULT([OpenBSD]) - backend="openbsd" - ;; -*-netbsd*) - AC_MSG_RESULT([NetBSD (using OpenBSD backend)]) - backend="openbsd" - ;; -*-mingw*) - AC_MSG_RESULT([Windows]) - backend="windows" - ;; -*-cygwin*) - AC_MSG_RESULT([Cygwin (using Windows backend)]) - backend="windows" - threads="posix" - ;; -*) - 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, PC_LIBS_PRIVATE="-lrt") - threads="posix" - THREAD_CFLAGS="-pthread" - PC_LIBS_PRIVATE="${PC_LIBS_PRIVATE} -pthread" - AC_CHECK_HEADERS([poll.h]) - AC_DEFINE([POLL_NFDS_TYPE],[nfds_t],[type of second poll() argument]) - ;; -darwin) - AC_DEFINE(OS_DARWIN, 1, [Darwin backend]) - AC_SUBST(OS_DARWIN) - threads="posix" - PC_LIBS_PRIVATE="-lobjc -Wl,-framework,IOKit -Wl,-framework,CoreFoundation" - LTLDFLAGS="${LTLDFLAGS} -Wl,-prebind" - AC_CHECK_HEADERS([poll.h]) - AC_CHECK_TYPE([nfds_t], - [AC_DEFINE([POLL_NFDS_TYPE],[nfds_t],[type of second poll() argument])], - [AC_DEFINE([POLL_NFDS_TYPE],[unsigned int],[type of second poll() argument])], - [#include <poll.h>]) - ;; -openbsd) - AC_DEFINE(OS_OPENBSD, 1, [OpenBSD backend]) - AC_SUBST(OS_OPENBSD) - threads="posix" - THREAD_CFLAGS="-pthread" - PC_LIBS_PRIVATE="-pthread" - AC_CHECK_HEADERS([poll.h]) - AC_DEFINE([POLL_NFDS_TYPE],[nfds_t],[type of second poll() argument]) - ;; -windows) - AC_DEFINE(OS_WINDOWS, 1, [Windows backend]) - AC_SUBST(OS_WINDOWS) - PC_LIBS_PRIVATE="" - LTLDFLAGS="${LTLDFLAGS} -avoid-version -Wl,--add-stdcall-alias" - AC_DEFINE([POLL_NFDS_TYPE],[unsigned int],[type of second poll() argument]) - ;; -esac -AC_SUBST(THREAD_CFLAGS) -AC_SUBST(PC_LIBS_PRIVATE) -LIBS="${LIBS} ${PC_LIBS_PRIVATE}" - -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_WINDOWS, test "x$backend" = xwindows) -AM_CONDITIONAL(THREADS_POSIX, test "x$threads" = xposix) -if test "$threads" = posix; then - AC_DEFINE(THREADS_POSIX, 1, [Use POSIX Threads]) -fi - -# timerfd -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=$enableval], [use_timerfd='auto']) - -if test "x$use_timerfd" = "xyes" -a "x$timerfd_h" = "x0"; then - AC_MSG_ERROR([timerfd header not available; glibc 2.9+ required]) -fi - -AC_CHECK_DECL([TFD_NONBLOCK], [tfd_hdr_ok=yes], [tfd_hdr_ok=no], [#include <sys/timerfd.h>]) -if test "x$use_timerfd" = "xyes" -a "x$tfd_hdr_ok" = "xno"; then - AC_MSG_ERROR([timerfd header not usable; glibc 2.9+ required]) -fi - -AC_MSG_CHECKING([whether to use timerfd for timing]) -if test "x$use_timerfd" = "xno"; then - AC_MSG_RESULT([no (disabled by user)]) -else - if test "x$timerfd_h" = "x1" -a "x$tfd_hdr_ok" = "xyes"; then - AC_MSG_RESULT([yes]) - AC_DEFINE(USBI_TIMERFD_AVAILABLE, 1, [timerfd headers available]) - else - AC_MSG_RESULT([no (header not available)]) - fi -fi - -AC_CHECK_TYPES(struct timespec) - -# Message logging -AC_ARG_ENABLE([log], [AS_HELP_STRING([--disable-log], [disable all logging])], - [log_enabled=$enableval], - [log_enabled='yes']) -if test "x$log_enabled" != "xno"; then - AC_DEFINE([ENABLE_LOGGING], 1, [Message logging]) -fi - -AC_ARG_ENABLE([debug-log], [AS_HELP_STRING([--enable-debug-log], - [enable debug logging (default n)])], - [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]) -fi - -# Examples build -AC_ARG_ENABLE([examples-build], [AS_HELP_STRING([--enable-examples-build], - [build example applications (default n)])], - [build_examples=$enableval], - [build_examples='no']) -AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$build_examples" != "xno"]) - -# check for -fvisibility=hidden compiler support (GCC >= 3.4) -saved_cflags="$CFLAGS" -# -Werror required for cygwin -CFLAGS="$CFLAGS -Werror -fvisibility=hidden" -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], - [VISIBILITY_CFLAGS="-fvisibility=hidden" - AC_DEFINE([DEFAULT_VISIBILITY], [__attribute__((visibility("default")))], [Default visibility]) ], - [ VISIBILITY_CFLAGS="" - AC_DEFINE([DEFAULT_VISIBILITY], [], [Default visibility]) ], - ]) -CFLAGS="$saved_cflags" - -# check for -Wno-pointer-sign compiler support (GCC >= 4) -saved_cflags="$CFLAGS" -CFLAGS="$CFLAGS -Wno-pointer-sign" -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], - nopointersign_cflags="-Wno-pointer-sign", nopointersign_cflags="") -CFLAGS="$saved_cflags" - -# sigaction not available on MinGW -AC_CHECK_FUNC([sigaction], [have_sigaction=yes], [have_sigaction=no]) -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) - -AM_CFLAGS="-std=gnu99 -Wall -Wundef -Wunused -Wstrict-prototypes -Werror-implicit-function-declaration $nopointersign_cflags -Wshadow" - -AC_SUBST(VISIBILITY_CFLAGS) -AC_SUBST(AM_CFLAGS) -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([doc/Makefile]) -AC_CONFIG_FILES([doc/doxygen.cfg]) -AC_OUTPUT diff --git a/third_party/libusb/src/doc/Makefile.am b/third_party/libusb/src/doc/Makefile.am deleted file mode 100644 index 931a7c0..0000000 --- a/third_party/libusb/src/doc/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ -EXTRA_DIST = doxygen.cfg.in - -docs: doxygen.cfg - doxygen $^ - -docs-upload: docs - ln -s html api-1.0 - rsync -av api-1.0/ web.sourceforge.net:htdocs/api-1.0/ - rm -f api-1.0 - diff --git a/third_party/libusb/src/doc/doxygen.cfg.in b/third_party/libusb/src/doc/doxygen.cfg.in deleted file mode 100644 index 128e1de..0000000 --- a/third_party/libusb/src/doc/doxygen.cfg.in +++ /dev/null @@ -1,1294 +0,0 @@ -# Doxyfile 1.5.3 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file that -# follow. The default is UTF-8 which is also the encoding used for all text before -# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into -# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of -# possible encodings. - -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 - -# 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 = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, -# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, -# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, -# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = NO - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = YES - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -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. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for Java. -# For instance, namespaces will be presented as packages, qualified scopes -# will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to -# include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = YES - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be extracted -# and appear in the documentation as a namespace called 'anonymous_namespace{file}', -# where file will be replaced with the base name of the file that contains the anonymous -# namespace. By default anonymous namespace are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = NO - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -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 -# popen()) the command <command> <input-file>, where <command> is the value of -# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# 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 - -# 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 -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = @top_srcdir@/libusb - -# This tag can be used to specify the character encoding of the source files that -# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default -# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. -# See http://www.gnu.org/software/libiconv for the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py - -FILE_PATTERNS = - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = NO - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = @top_srcdir@/libusb/libusbi.h - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the output. -# The symbol name can be a fully qualified name, a word, or if the wildcard * is used, -# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command <filter> <input-file>, where <filter> -# is the value of the INPUT_FILTER tag, and <input-file> is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH -# then you must also enable this option. If you don't then doxygen will produce -# a warning and turn it on anyway - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES (the default) -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES (the default) -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. Otherwise they will link to the documentstion. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = YES - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -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) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = YES - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be -# generated containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, -# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are -# probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = NO - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = NO - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = YES - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = YES - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = API_EXPORTED= LIBUSB_CALL= DEFAULT_VISIBILITY= - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to -# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to -# specify the directory where the mscgen tool resides. If left empty the tool is assumed to -# be found in the default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will -# generate a call dependency graph for every global function or class method. -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will -# generate a caller dependency graph for every global function or class method. -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the number -# of direct children of the root node in a graph is already larger than -# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, which results in a white background. -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to the search engine -#--------------------------------------------------------------------------- - -# The SEARCHENGINE tag specifies whether or not a search engine should be -# used. If set to NO the values of all tags below this one will be ignored. - -SEARCHENGINE = NO diff --git a/third_party/libusb/src/examples/Makefile.am b/third_party/libusb/src/examples/Makefile.am deleted file mode 100644 index c7630d2..0000000 --- a/third_party/libusb/src/examples/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -INCLUDES = -I$(top_srcdir)/libusb -noinst_PROGRAMS = listdevs - -listdevs_SOURCES = listdevs.c -listdevs_LDADD = ../libusb/libusb-1.0.la - -if HAVE_SIGACTION -dpfp_SOURCES = dpfp.c -dpfp_LDADD = ../libusb/libusb-1.0.la -noinst_PROGRAMS += dpfp -endif - -if THREADS_POSIX -if HAVE_SIGACTION -dpfp_threaded_SOURCES = dpfp_threaded.c -dpfp_threaded_CFLAGS = $(THREAD_CFLAGS) $(AM_CFLAGS) -dpfp_threaded_LDADD = ../libusb/libusb-1.0.la -noinst_PROGRAMS += dpfp_threaded -endif -endif - diff --git a/third_party/libusb/src/examples/dpfp.c b/third_party/libusb/src/examples/dpfp.c deleted file mode 100644 index ecd5a92..0000000 --- a/third_party/libusb/src/examples/dpfp.c +++ /dev/null @@ -1,507 +0,0 @@ -/* - * libusb example program to manipulate U.are.U 4000B fingerprint scanner. - * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org> - * - * Basic image capture program only, does not consider the powerup quirks or - * the fact that image encryption may be enabled. Not expected to work - * flawlessly all of the time. - * - * 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 <errno.h> -#include <signal.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -#include <libusb.h> - -#define EP_INTR (1 | LIBUSB_ENDPOINT_IN) -#define EP_DATA (2 | LIBUSB_ENDPOINT_IN) -#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN) -#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT) -#define USB_RQ 0x04 -#define INTR_LENGTH 64 - -enum { - MODE_INIT = 0x00, - MODE_AWAIT_FINGER_ON = 0x10, - MODE_AWAIT_FINGER_OFF = 0x12, - MODE_CAPTURE = 0x20, - MODE_SHUT_UP = 0x30, - MODE_READY = 0x80, -}; - -static int next_state(void); - -enum { - STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON = 1, - STATE_AWAIT_IRQ_FINGER_DETECTED, - STATE_AWAIT_MODE_CHANGE_CAPTURE, - STATE_AWAIT_IMAGE, - STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF, - STATE_AWAIT_IRQ_FINGER_REMOVED, -}; - -static int state = 0; -static struct libusb_device_handle *devh = NULL; -static unsigned char imgbuf[0x1b340]; -static unsigned char irqbuf[INTR_LENGTH]; -static struct libusb_transfer *img_transfer = NULL; -static struct libusb_transfer *irq_transfer = NULL; -static int img_idx = 0; -static int do_exit = 0; - -static int find_dpfp_device(void) -{ - devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a); - return devh ? 0 : -EIO; -} - -static int print_f0_data(void) -{ - unsigned char data[0x10]; - int r; - unsigned int i; - - r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data, - sizeof(data), 0); - if (r < 0) { - fprintf(stderr, "F0 error %d\n", r); - return r; - } - if ((unsigned int) r < sizeof(data)) { - fprintf(stderr, "short read (%d)\n", r); - return -1; - } - - printf("F0 data:"); - for (i = 0; i < sizeof(data); i++) - printf("%02x ", data[i]); - printf("\n"); - return 0; -} - -static int get_hwstat(unsigned char *status) -{ - int r; - - r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0x07, 0, status, 1, 0); - if (r < 0) { - fprintf(stderr, "read hwstat error %d\n", r); - return r; - } - if ((unsigned int) r < 1) { - fprintf(stderr, "short read (%d)\n", r); - return -1; - } - - printf("hwstat reads %02x\n", *status); - return 0; -} - -static int set_hwstat(unsigned char data) -{ - int r; - - printf("set hwstat to %02x\n", data); - r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x07, 0, &data, 1, 0); - if (r < 0) { - fprintf(stderr, "set hwstat error %d\n", r); - return r; - } - if ((unsigned int) r < 1) { - fprintf(stderr, "short write (%d)", r); - return -1; - } - - return 0; -} - -static int set_mode(unsigned char data) -{ - int r; - printf("set mode %02x\n", data); - - r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x4e, 0, &data, 1, 0); - if (r < 0) { - fprintf(stderr, "set mode error %d\n", r); - return r; - } - if ((unsigned int) r < 1) { - fprintf(stderr, "short write (%d)", r); - return -1; - } - - return 0; -} - -static void LIBUSB_CALL cb_mode_changed(struct libusb_transfer *transfer) -{ - if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "mode change transfer not completed!\n"); - do_exit = 2; - } - - printf("async cb_mode_changed length=%d actual_length=%d\n", - transfer->length, transfer->actual_length); - if (next_state() < 0) - do_exit = 2; -} - -static int set_mode_async(unsigned char data) -{ - unsigned char *buf = malloc(LIBUSB_CONTROL_SETUP_SIZE + 1); - struct libusb_transfer *transfer; - - if (!buf) - return -ENOMEM; - - transfer = libusb_alloc_transfer(0); - if (!transfer) { - free(buf); - return -ENOMEM; - } - - printf("async set mode %02x\n", data); - libusb_fill_control_setup(buf, CTRL_OUT, USB_RQ, 0x4e, 0, 1); - buf[LIBUSB_CONTROL_SETUP_SIZE] = data; - libusb_fill_control_transfer(transfer, devh, buf, cb_mode_changed, NULL, - 1000); - - transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK - | LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; - return libusb_submit_transfer(transfer); -} - -static int do_sync_intr(unsigned char *data) -{ - int r; - int transferred; - - r = libusb_interrupt_transfer(devh, EP_INTR, data, INTR_LENGTH, - &transferred, 1000); - if (r < 0) { - fprintf(stderr, "intr error %d\n", r); - return r; - } - if (transferred < INTR_LENGTH) { - fprintf(stderr, "short read (%d)\n", r); - return -1; - } - - printf("recv interrupt %04x\n", *((uint16_t *) data)); - return 0; -} - -static int sync_intr(unsigned char type) -{ - int r; - unsigned char data[INTR_LENGTH]; - - while (1) { - r = do_sync_intr(data); - if (r < 0) - return r; - if (data[0] == type) - return 0; - } -} - -static int save_to_file(unsigned char *data) -{ - FILE *fd; - char filename[64]; - - snprintf(filename, sizeof(filename), "finger%d.pgm", img_idx++); - fd = fopen(filename, "w"); - if (!fd) - return -1; - - fputs("P5 384 289 255 ", fd); - (void) fwrite(data + 64, 1, 384*289, fd); - fclose(fd); - printf("saved image to %s\n", filename); - return 0; -} - -static int next_state(void) -{ - int r = 0; - printf("old state: %d\n", state); - switch (state) { - case STATE_AWAIT_IRQ_FINGER_REMOVED: - state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON; - r = set_mode_async(MODE_AWAIT_FINGER_ON); - break; - case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON: - state = STATE_AWAIT_IRQ_FINGER_DETECTED; - break; - case STATE_AWAIT_IRQ_FINGER_DETECTED: - state = STATE_AWAIT_MODE_CHANGE_CAPTURE; - r = set_mode_async(MODE_CAPTURE); - break; - case STATE_AWAIT_MODE_CHANGE_CAPTURE: - state = STATE_AWAIT_IMAGE; - break; - case STATE_AWAIT_IMAGE: - state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF; - r = set_mode_async(MODE_AWAIT_FINGER_OFF); - break; - case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF: - state = STATE_AWAIT_IRQ_FINGER_REMOVED; - break; - default: - printf("unrecognised state %d\n", state); - } - if (r < 0) { - fprintf(stderr, "error detected changing state\n"); - return r; - } - - printf("new state: %d\n", state); - return 0; -} - -static void LIBUSB_CALL cb_irq(struct libusb_transfer *transfer) -{ - unsigned char irqtype = transfer->buffer[0]; - - if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "irq transfer status %d?\n", transfer->status); - do_exit = 2; - libusb_free_transfer(transfer); - irq_transfer = NULL; - return; - } - - printf("IRQ callback %02x\n", irqtype); - switch (state) { - case STATE_AWAIT_IRQ_FINGER_DETECTED: - if (irqtype == 0x01) { - if (next_state() < 0) { - do_exit = 2; - return; - } - } else { - printf("finger-on-sensor detected in wrong state!\n"); - } - break; - case STATE_AWAIT_IRQ_FINGER_REMOVED: - if (irqtype == 0x02) { - if (next_state() < 0) { - do_exit = 2; - return; - } - } else { - printf("finger-on-sensor detected in wrong state!\n"); - } - break; - } - if (libusb_submit_transfer(irq_transfer) < 0) - do_exit = 2; -} - -static void LIBUSB_CALL cb_img(struct libusb_transfer *transfer) -{ - if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "img transfer status %d?\n", transfer->status); - do_exit = 2; - libusb_free_transfer(transfer); - img_transfer = NULL; - return; - } - - printf("Image callback\n"); - save_to_file(imgbuf); - if (next_state() < 0) { - do_exit = 2; - return; - } - if (libusb_submit_transfer(img_transfer) < 0) - do_exit = 2; -} - -static int init_capture(void) -{ - int r; - - r = libusb_submit_transfer(irq_transfer); - if (r < 0) - return r; - - r = libusb_submit_transfer(img_transfer); - if (r < 0) { - libusb_cancel_transfer(irq_transfer); - while (irq_transfer) - if (libusb_handle_events(NULL) < 0) - break; - return r; - } - - /* start state machine */ - state = STATE_AWAIT_IRQ_FINGER_REMOVED; - return next_state(); -} - -static int do_init(void) -{ - unsigned char status; - int r; - - r = get_hwstat(&status); - if (r < 0) - return r; - - if (!(status & 0x80)) { - r = set_hwstat(status | 0x80); - if (r < 0) - return r; - r = get_hwstat(&status); - if (r < 0) - return r; - } - - status &= ~0x80; - r = set_hwstat(status); - if (r < 0) - return r; - - r = get_hwstat(&status); - if (r < 0) - return r; - - r = sync_intr(0x56); - if (r < 0) - return r; - - return 0; -} - -static int alloc_transfers(void) -{ - img_transfer = libusb_alloc_transfer(0); - if (!img_transfer) - return -ENOMEM; - - irq_transfer = libusb_alloc_transfer(0); - if (!irq_transfer) - return -ENOMEM; - - libusb_fill_bulk_transfer(img_transfer, devh, EP_DATA, imgbuf, - sizeof(imgbuf), cb_img, NULL, 0); - libusb_fill_interrupt_transfer(irq_transfer, devh, EP_INTR, irqbuf, - sizeof(irqbuf), cb_irq, NULL, 0); - - return 0; -} - -static void sighandler(int signum) -{ - do_exit = 1; -} - -int main(void) -{ - struct sigaction sigact; - int r = 1; - - r = libusb_init(NULL); - if (r < 0) { - fprintf(stderr, "failed to initialise libusb\n"); - exit(1); - } - - r = find_dpfp_device(); - if (r < 0) { - fprintf(stderr, "Could not find/open device\n"); - goto out; - } - - r = libusb_claim_interface(devh, 0); - if (r < 0) { - fprintf(stderr, "usb_claim_interface error %d\n", r); - goto out; - } - printf("claimed interface\n"); - - r = print_f0_data(); - if (r < 0) - goto out_release; - - r = do_init(); - if (r < 0) - goto out_deinit; - - /* async from here onwards */ - - r = alloc_transfers(); - if (r < 0) - goto out_deinit; - - r = init_capture(); - if (r < 0) - goto out_deinit; - - sigact.sa_handler = sighandler; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigaction(SIGINT, &sigact, NULL); - sigaction(SIGTERM, &sigact, NULL); - sigaction(SIGQUIT, &sigact, NULL); - - while (!do_exit) { - r = libusb_handle_events(NULL); - if (r < 0) - goto out_deinit; - } - - printf("shutting down...\n"); - - if (irq_transfer) { - r = libusb_cancel_transfer(irq_transfer); - if (r < 0) - goto out_deinit; - } - - if (img_transfer) { - r = libusb_cancel_transfer(img_transfer); - if (r < 0) - goto out_deinit; - } - - while (irq_transfer || img_transfer) - if (libusb_handle_events(NULL) < 0) - break; - - if (do_exit == 1) - r = 0; - else - r = 1; - -out_deinit: - libusb_free_transfer(img_transfer); - libusb_free_transfer(irq_transfer); - set_mode(0); - set_hwstat(0x80); -out_release: - libusb_release_interface(devh, 0); -out: - libusb_close(devh); - libusb_exit(NULL); - return r >= 0 ? r : -r; -} - diff --git a/third_party/libusb/src/examples/dpfp_threaded.c b/third_party/libusb/src/examples/dpfp_threaded.c deleted file mode 100644 index 93de9d7..0000000 --- a/third_party/libusb/src/examples/dpfp_threaded.c +++ /dev/null @@ -1,545 +0,0 @@ -/* - * libusb example program to manipulate U.are.U 4000B fingerprint scanner. - * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org> - * - * Basic image capture program only, does not consider the powerup quirks or - * the fact that image encryption may be enabled. Not expected to work - * flawlessly all of the time. - * - * 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 <errno.h> -#include <pthread.h> -#include <signal.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -#include <libusb.h> - -#define EP_INTR (1 | LIBUSB_ENDPOINT_IN) -#define EP_DATA (2 | LIBUSB_ENDPOINT_IN) -#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN) -#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT) -#define USB_RQ 0x04 -#define INTR_LENGTH 64 - -enum { - MODE_INIT = 0x00, - MODE_AWAIT_FINGER_ON = 0x10, - MODE_AWAIT_FINGER_OFF = 0x12, - MODE_CAPTURE = 0x20, - MODE_SHUT_UP = 0x30, - MODE_READY = 0x80, -}; - -static int next_state(void); - -enum { - STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON = 1, - STATE_AWAIT_IRQ_FINGER_DETECTED, - STATE_AWAIT_MODE_CHANGE_CAPTURE, - STATE_AWAIT_IMAGE, - STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF, - STATE_AWAIT_IRQ_FINGER_REMOVED, -}; - -static int state = 0; -static struct libusb_device_handle *devh = NULL; -static unsigned char imgbuf[0x1b340]; -static unsigned char irqbuf[INTR_LENGTH]; -static struct libusb_transfer *img_transfer = NULL; -static struct libusb_transfer *irq_transfer = NULL; -static int img_idx = 0; -static int do_exit = 0; - -static pthread_t poll_thread; -static pthread_cond_t exit_cond = PTHREAD_COND_INITIALIZER; -static pthread_mutex_t exit_cond_lock = PTHREAD_MUTEX_INITIALIZER; - -static void request_exit(int code) -{ - do_exit = code; - pthread_cond_signal(&exit_cond); -} - -static void *poll_thread_main(void *arg) -{ - int r = 0; - printf("poll thread running\n"); - - while (!do_exit) { - struct timeval tv = { 1, 0 }; - r = libusb_handle_events_timeout(NULL, &tv); - if (r < 0) { - request_exit(2); - break; - } - } - - printf("poll thread shutting down\n"); - return NULL; -} - -static int find_dpfp_device(void) -{ - devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a); - return devh ? 0 : -EIO; -} - -static int print_f0_data(void) -{ - unsigned char data[0x10]; - int r; - unsigned int i; - - r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data, - sizeof(data), 0); - if (r < 0) { - fprintf(stderr, "F0 error %d\n", r); - return r; - } - if ((unsigned int) r < sizeof(data)) { - fprintf(stderr, "short read (%d)\n", r); - return -1; - } - - printf("F0 data:"); - for (i = 0; i < sizeof(data); i++) - printf("%02x ", data[i]); - printf("\n"); - return 0; -} - -static int get_hwstat(unsigned char *status) -{ - int r; - - r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0x07, 0, status, 1, 0); - if (r < 0) { - fprintf(stderr, "read hwstat error %d\n", r); - return r; - } - if ((unsigned int) r < 1) { - fprintf(stderr, "short read (%d)\n", r); - return -1; - } - - printf("hwstat reads %02x\n", *status); - return 0; -} - -static int set_hwstat(unsigned char data) -{ - int r; - - printf("set hwstat to %02x\n", data); - r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x07, 0, &data, 1, 0); - if (r < 0) { - fprintf(stderr, "set hwstat error %d\n", r); - return r; - } - if ((unsigned int) r < 1) { - fprintf(stderr, "short write (%d)", r); - return -1; - } - - return 0; -} - -static int set_mode(unsigned char data) -{ - int r; - printf("set mode %02x\n", data); - - r = libusb_control_transfer(devh, CTRL_OUT, USB_RQ, 0x4e, 0, &data, 1, 0); - if (r < 0) { - fprintf(stderr, "set mode error %d\n", r); - return r; - } - if ((unsigned int) r < 1) { - fprintf(stderr, "short write (%d)", r); - return -1; - } - - return 0; -} - -static void LIBUSB_CALL cb_mode_changed(struct libusb_transfer *transfer) -{ - if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "mode change transfer not completed!\n"); - request_exit(2); - } - - printf("async cb_mode_changed length=%d actual_length=%d\n", - transfer->length, transfer->actual_length); - if (next_state() < 0) - request_exit(2); -} - -static int set_mode_async(unsigned char data) -{ - unsigned char *buf = malloc(LIBUSB_CONTROL_SETUP_SIZE + 1); - struct libusb_transfer *transfer; - - if (!buf) - return -ENOMEM; - - transfer = libusb_alloc_transfer(0); - if (!transfer) { - free(buf); - return -ENOMEM; - } - - printf("async set mode %02x\n", data); - libusb_fill_control_setup(buf, CTRL_OUT, USB_RQ, 0x4e, 0, 1); - buf[LIBUSB_CONTROL_SETUP_SIZE] = data; - libusb_fill_control_transfer(transfer, devh, buf, cb_mode_changed, NULL, - 1000); - - transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK - | LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; - return libusb_submit_transfer(transfer); -} - -static int do_sync_intr(unsigned char *data) -{ - int r; - int transferred; - - r = libusb_interrupt_transfer(devh, EP_INTR, data, INTR_LENGTH, - &transferred, 1000); - if (r < 0) { - fprintf(stderr, "intr error %d\n", r); - return r; - } - if (transferred < INTR_LENGTH) { - fprintf(stderr, "short read (%d)\n", r); - return -1; - } - - printf("recv interrupt %04x\n", *((uint16_t *) data)); - return 0; -} - -static int sync_intr(unsigned char type) -{ - int r; - unsigned char data[INTR_LENGTH]; - - while (1) { - r = do_sync_intr(data); - if (r < 0) - return r; - if (data[0] == type) - return 0; - } -} - -static int save_to_file(unsigned char *data) -{ - FILE *fd; - char filename[64]; - - snprintf(filename, sizeof(filename), "finger%d.pgm", img_idx++); - fd = fopen(filename, "w"); - if (!fd) - return -1; - - fputs("P5 384 289 255 ", fd); - (void) fwrite(data + 64, 1, 384*289, fd); - fclose(fd); - printf("saved image to %s\n", filename); - return 0; -} - -static int next_state(void) -{ - int r = 0; - printf("old state: %d\n", state); - switch (state) { - case STATE_AWAIT_IRQ_FINGER_REMOVED: - state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON; - r = set_mode_async(MODE_AWAIT_FINGER_ON); - break; - case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_ON: - state = STATE_AWAIT_IRQ_FINGER_DETECTED; - break; - case STATE_AWAIT_IRQ_FINGER_DETECTED: - state = STATE_AWAIT_MODE_CHANGE_CAPTURE; - r = set_mode_async(MODE_CAPTURE); - break; - case STATE_AWAIT_MODE_CHANGE_CAPTURE: - state = STATE_AWAIT_IMAGE; - break; - case STATE_AWAIT_IMAGE: - state = STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF; - r = set_mode_async(MODE_AWAIT_FINGER_OFF); - break; - case STATE_AWAIT_MODE_CHANGE_AWAIT_FINGER_OFF: - state = STATE_AWAIT_IRQ_FINGER_REMOVED; - break; - default: - printf("unrecognised state %d\n", state); - } - if (r < 0) { - fprintf(stderr, "error detected changing state\n"); - return r; - } - - printf("new state: %d\n", state); - return 0; -} - -static void LIBUSB_CALL cb_irq(struct libusb_transfer *transfer) -{ - unsigned char irqtype = transfer->buffer[0]; - - if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "irq transfer status %d?\n", transfer->status); - irq_transfer = NULL; - request_exit(2); - return; - } - - printf("IRQ callback %02x\n", irqtype); - switch (state) { - case STATE_AWAIT_IRQ_FINGER_DETECTED: - if (irqtype == 0x01) { - if (next_state() < 0) { - request_exit(2); - return; - } - } else { - printf("finger-on-sensor detected in wrong state!\n"); - } - break; - case STATE_AWAIT_IRQ_FINGER_REMOVED: - if (irqtype == 0x02) { - if (next_state() < 0) { - request_exit(2); - return; - } - } else { - printf("finger-on-sensor detected in wrong state!\n"); - } - break; - } - if (libusb_submit_transfer(irq_transfer) < 0) - request_exit(2); -} - -static void LIBUSB_CALL cb_img(struct libusb_transfer *transfer) -{ - if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { - fprintf(stderr, "img transfer status %d?\n", transfer->status); - img_transfer = NULL; - request_exit(2); - return; - } - - printf("Image callback\n"); - save_to_file(imgbuf); - if (next_state() < 0) { - request_exit(2); - return; - } - if (libusb_submit_transfer(img_transfer) < 0) - request_exit(2); -} - -static int init_capture(void) -{ - int r; - - r = libusb_submit_transfer(irq_transfer); - if (r < 0) - return r; - - r = libusb_submit_transfer(img_transfer); - if (r < 0) { - libusb_cancel_transfer(irq_transfer); - while (irq_transfer) - if (libusb_handle_events(NULL) < 0) - break; - return r; - } - - /* start state machine */ - state = STATE_AWAIT_IRQ_FINGER_REMOVED; - return next_state(); -} - -static int do_init(void) -{ - unsigned char status; - int r; - - r = get_hwstat(&status); - if (r < 0) - return r; - - if (!(status & 0x80)) { - r = set_hwstat(status | 0x80); - if (r < 0) - return r; - r = get_hwstat(&status); - if (r < 0) - return r; - } - - status &= ~0x80; - r = set_hwstat(status); - if (r < 0) - return r; - - r = get_hwstat(&status); - if (r < 0) - return r; - - r = sync_intr(0x56); - if (r < 0) - return r; - - return 0; -} - -static int alloc_transfers(void) -{ - img_transfer = libusb_alloc_transfer(0); - if (!img_transfer) - return -ENOMEM; - - irq_transfer = libusb_alloc_transfer(0); - if (!irq_transfer) - return -ENOMEM; - - libusb_fill_bulk_transfer(img_transfer, devh, EP_DATA, imgbuf, - sizeof(imgbuf), cb_img, NULL, 0); - libusb_fill_interrupt_transfer(irq_transfer, devh, EP_INTR, irqbuf, - sizeof(irqbuf), cb_irq, NULL, 0); - - return 0; -} - -static void sighandler(int signum) -{ - request_exit(1); -} - -int main(void) -{ - struct sigaction sigact; - int r = 1; - - r = libusb_init(NULL); - if (r < 0) { - fprintf(stderr, "failed to initialise libusb\n"); - exit(1); - } - - r = find_dpfp_device(); - if (r < 0) { - fprintf(stderr, "Could not find/open device\n"); - goto out; - } - - r = libusb_claim_interface(devh, 0); - if (r < 0) { - fprintf(stderr, "usb_claim_interface error %d %s\n", r, strerror(-r)); - goto out; - } - printf("claimed interface\n"); - - r = print_f0_data(); - if (r < 0) - goto out_release; - - r = do_init(); - if (r < 0) - goto out_deinit; - - /* async from here onwards */ - - sigact.sa_handler = sighandler; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigaction(SIGINT, &sigact, NULL); - sigaction(SIGTERM, &sigact, NULL); - sigaction(SIGQUIT, &sigact, NULL); - - r = pthread_create(&poll_thread, NULL, poll_thread_main, NULL); - if (r) - goto out_deinit; - - r = alloc_transfers(); - if (r < 0) { - request_exit(1); - pthread_join(poll_thread, NULL); - goto out_deinit; - } - - r = init_capture(); - if (r < 0) { - request_exit(1); - pthread_join(poll_thread, NULL); - goto out_deinit; - } - - while (!do_exit) { - pthread_mutex_lock(&exit_cond_lock); - pthread_cond_wait(&exit_cond, &exit_cond_lock); - pthread_mutex_unlock(&exit_cond_lock); - } - - printf("shutting down...\n"); - pthread_join(poll_thread, NULL); - - r = libusb_cancel_transfer(irq_transfer); - if (r < 0) { - request_exit(1); - goto out_deinit; - } - - r = libusb_cancel_transfer(img_transfer); - if (r < 0) { - request_exit(1); - goto out_deinit; - } - - while (img_transfer || irq_transfer) - if (libusb_handle_events(NULL) < 0) - break; - - if (do_exit == 1) - r = 0; - else - r = 1; - -out_deinit: - libusb_free_transfer(img_transfer); - libusb_free_transfer(irq_transfer); - set_mode(0); - set_hwstat(0x80); -out_release: - libusb_release_interface(devh, 0); -out: - libusb_close(devh); - libusb_exit(NULL); - return r >= 0 ? r : -r; -} - diff --git a/third_party/libusb/src/examples/listdevs.c b/third_party/libusb/src/examples/listdevs.c deleted file mode 100644 index 6ab8917..0000000 --- a/third_party/libusb/src/examples/listdevs.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * libusb example program to list devices on the bus - * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org> - * - * 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 <stdio.h> -#include <sys/types.h> - -#include <libusb.h> - -static void print_devs(libusb_device **devs) -{ - libusb_device *dev; - int i = 0; - - while ((dev = devs[i++]) != NULL) { - struct libusb_device_descriptor desc; - int r = libusb_get_device_descriptor(dev, &desc); - if (r < 0) { - fprintf(stderr, "failed to get device descriptor"); - return; - } - - printf("%04x:%04x (bus %d, device %d)\n", - desc.idVendor, desc.idProduct, - libusb_get_bus_number(dev), libusb_get_device_address(dev)); - } -} - -int main(void) -{ - libusb_device **devs; - int r; - ssize_t cnt; - - r = libusb_init(NULL); - if (r < 0) - return r; - - cnt = libusb_get_device_list(NULL, &devs); - if (cnt < 0) - return (int) cnt; - - print_devs(devs); - libusb_free_device_list(devs, 1); - - libusb_exit(NULL); - return 0; -} - diff --git a/third_party/libusb/src/libusb-1.0.pc.in b/third_party/libusb/src/libusb-1.0.pc.in deleted file mode 100644 index f26babc..0000000 --- a/third_party/libusb/src/libusb-1.0.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@prefix@ -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 -Version: @VERSION@ -Libs: -L${libdir} -lusb-1.0 -Libs.private: @PC_LIBS_PRIVATE@ -Cflags: -I${includedir}/libusb-1.0 - diff --git a/third_party/libusb/src/libusb/Makefile.am b/third_party/libusb/src/libusb/Makefile.am deleted file mode 100644 index 3316ebc..0000000 --- a/third_party/libusb/src/libusb/Makefile.am +++ /dev/null @@ -1,49 +0,0 @@ -lib_LTLIBRARIES = libusb-1.0.la - -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 - -EXTRA_DIST = $(LINUX_USBFS_SRC) $(DARWIN_USB_SRC) $(OPENBSD_USB_SRC) \ - $(WINDOWS_USB_SRC) os/threads_posix.c os/threads_windows.c - -if OS_LINUX -OS_SRC = $(LINUX_USBFS_SRC) -endif - -if OS_DARWIN -OS_SRC = $(DARWIN_USB_SRC) -AM_CFLAGS_EXT = -no-cpp-precomp -endif - -if OS_OPENBSD -OS_SRC = $(OPENBSD_USB_SRC) -endif - -if OS_WINDOWS -OS_SRC = $(WINDOWS_USB_SRC) - -.rc.lo: - $(AM_V_GEN)$(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --tag=RC --mode=compile $(RC) $(RCFLAGS) -i $< -o $@ - -libusb-1.0.rc: version.h -endif - -if THREADS_POSIX -THREADS_SRC = os/threads_posix.h os/threads_posix.c -else -THREADS_SRC = os/threads_windows.h os/threads_windows.c -endif - -libusb_1_0_la_CFLAGS = $(VISIBILITY_CFLAGS) $(AM_CFLAGS) $(THREAD_CFLAGS) \ - -DLIBUSB_DESCRIBE=\"`git --git-dir "$(top_srcdir)/.git" describe --tags 2>/dev/null`\" -libusb_1_0_la_LDFLAGS = $(LTLDFLAGS) -libusb_1_0_la_SOURCES = libusbi.h core.c descriptor.c io.c sync.c $(OS_SRC) \ - os/linux_usbfs.h os/darwin_usb.h os/windows_usb.h \ - $(THREADS_SRC) \ - os/poll_posix.h os/poll_windows.h - -hdrdir = $(includedir)/libusb-1.0 -hdr_HEADERS = libusb.h diff --git a/third_party/libusb/src/libusb/core.c b/third_party/libusb/src/libusb/core.c index 767dcbf..e29e8df 100644 --- a/third_party/libusb/src/libusb/core.c +++ b/third_party/libusb/src/libusb/core.c @@ -1,7 +1,9 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ /* - * Core functions for libusb - * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org> - * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * Core functions for libusbx + * Copyright © 2012-2013 Nathan Hjelm <hjelmn@cs.unm.edu> + * Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org> + * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,20 +20,26 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <config.h> +#include "config.h" #include <errno.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#ifdef HAVE_SYS_TYPES_H #include <sys/types.h> - +#endif #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #endif +#ifdef __ANDROID__ +#include <android/log.h> +#endif + #include "libusbi.h" +#include "hotplug.h" #if defined(OS_LINUX) const struct usbi_os_backend * const usbi_backend = &linux_usbfs_backend; @@ -41,38 +49,41 @@ const struct usbi_os_backend * const usbi_backend = &darwin_backend; const struct usbi_os_backend * const usbi_backend = &openbsd_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; +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 - * <a href="http://libusb.sourceforge.net">libusb homepage</a>. + * <a href="http://libusbx.org">libusbx homepage</a>. * * 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 - * <a href="http://sourceforge.net/mail/?group_id=1674">libusb-devel mailing - * list</a>. + * <a href="http://mailing-list.libusbx.org">libusbx-devel mailing list</a>. * * 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 <a href="http://www.usb.org/developers/docs/">USB 2.0 + * can be found in the <a href="http://www.usb.org/developers/docs/">USB 3.0 * Specification</a> which is available for free download. You can probably * find less verbose introductions by searching the web. * @@ -86,67 +97,71 @@ static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; * usually won't need to thread) * - Lightweight with lean API * - Compatible with libusb-0.1 through the libusb-compat-0.1 translation layer + * - Hotplug support (on some platforms). See \ref hotplug. * * \section gettingstarted Getting Started * * To begin reading the API documentation, start with the Modules page which - * links to the different categories of libusb's functionality. + * 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. */ @@ -161,7 +176,7 @@ static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; * 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. * @@ -184,22 +199,9 @@ static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; * - Clearing of halt/stall condition (libusb_clear_halt()) * - Device resets (libusb_reset_device()) * - * \section nohotplug No hotplugging - * - * libusb-1.0 lacks functionality for providing notifications of when devices - * are added or removed. This functionality is planned to be implemented - * for libusb-1.1. - * - * That said, there is basic disconnection handling for open device handles: - * - If there are ongoing transfers, libusb's handle_events loop will detect - * disconnections and complete ongoing transfers with the - * LIBUSB_TRANSFER_NO_DEVICE status code. - * - Many functions such as libusb_set_configuration() return the special - * LIBUSB_ERROR_NO_DEVICE error code when the device has been disconnected. - * * \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. @@ -210,13 +212,13 @@ static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; * -# 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 @@ -256,50 +258,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. */ @@ -307,24 +282,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 <em>context</em> concept. When you + * This is made possible through libusbx's <em>context</em> 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(). @@ -337,17 +312,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. */ /** @@ -404,7 +379,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. @@ -420,8 +395,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 <em>device handle</em> + * libusbx to open the device using the libusb_open() function. Assuming + * success, libusbx then returns you a <em>device handle</em> * (a \ref libusb_device_handle pointer). All "real" I/O operations then * operate on the handle rather than the original device pointer. * @@ -429,10 +404,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 * @@ -500,7 +475,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; @@ -543,12 +518,66 @@ 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); + } + + return dev; +} + +void usbi_connect_device(struct libusb_device *dev) +{ + libusb_hotplug_message message; + ssize_t ret; + + memset(&message, 0, sizeof(message)); + message.event = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED; + message.device = dev; + dev->attached = 1; + + usbi_mutex_lock(&dev->ctx->usb_devs_lock); + list_add(&dev->list, &dev->ctx->usb_devs); + usbi_mutex_unlock(&dev->ctx->usb_devs_lock); + + /* Signal that an event has occurred for this device if we support hotplug AND + * the hotplug pipe is ready. This prevents an event from getting raised during + * initial enumeration. */ + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_pipe[1] > 0) { + ret = usbi_write(dev->ctx->hotplug_pipe[1], &message, sizeof(message)); + if (sizeof (message) != ret) { + usbi_err(DEVICE_CTX(dev), "error writing hotplug message"); + } + } +} + +void usbi_disconnect_device(struct libusb_device *dev) +{ + libusb_hotplug_message message; + struct libusb_context *ctx = dev->ctx; + ssize_t ret; + + memset(&message, 0, sizeof(message)); + message.event = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT; + message.device = dev; + usbi_mutex_lock(&dev->lock); + dev->attached = 0; + usbi_mutex_unlock(&dev->lock); + + /* 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_add(&dev->list, &ctx->usb_devs); + list_del(&dev->list); usbi_mutex_unlock(&ctx->usb_devs_lock); - return dev; } /* Perform some final sanity checks on a newly discovered device. If this @@ -557,15 +586,13 @@ struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, int usbi_sanitize_device(struct libusb_device *dev) { int r; - unsigned char raw_desc[DEVICE_DESC_LENGTH]; uint8_t num_configurations; - int host_endian; - r = usbi_backend->get_device_descriptor(dev, raw_desc, &host_endian); + r = usbi_device_cache_descriptor(dev); if (r < 0) return r; - num_configurations = raw_desc[DEVICE_DESC_LENGTH - 1]; + num_configurations = dev->device_descriptor.bNumConfigurations; if (num_configurations > USB_MAXCONFIG) { usbi_err(DEVICE_CTX(dev), "too many configurations"); return LIBUSB_ERROR_IO; @@ -576,7 +603,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, @@ -613,7 +640,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, @@ -629,7 +656,28 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx, if (!discdevs) return LIBUSB_ERROR_NO_MEM; - r = usbi_backend->get_device_list(ctx, &discdevs); + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + /* backend provides hotplug support */ + struct libusb_device *dev; + + if (usbi_backend->hotplug_poll) + usbi_backend->hotplug_poll(); + + usbi_mutex_lock(&ctx->usb_devs_lock); + list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) { + discdevs = discovered_devs_append(discdevs, dev); + + if (!discdevs) { + r = LIBUSB_ERROR_NO_MEM; + break; + } + } + usbi_mutex_unlock(&ctx->usb_devs_lock); + } else { + /* backend does not provide hotplug support */ + r = usbi_backend->get_device_list(ctx, &discdevs); + } + if (r < 0) { len = r; goto out; @@ -637,7 +685,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; @@ -689,6 +737,87 @@ uint8_t API_EXPORTED libusb_get_bus_number(libusb_device *dev) } /** \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 * \returns the device address @@ -777,7 +906,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. * @@ -820,7 +949,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; @@ -863,12 +992,15 @@ 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); - usbi_mutex_lock(&dev->ctx->usb_devs_lock); - list_del(&dev->list); - usbi_mutex_unlock(&dev->ctx->usb_devs_lock); + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + /* backend does not support hotplug */ + usbi_disconnect_device(dev); + } usbi_mutex_destroy(&dev->lock); free(dev); @@ -947,6 +1079,10 @@ int API_EXPORTED libusb_open(libusb_device *dev, int r; usbi_dbg("open %d.%d", dev->bus_number, dev->device_address); + if (!dev->attached) { + return LIBUSB_ERROR_NO_DEVICE; + } + _handle = malloc(sizeof(*_handle) + priv_size); if (!_handle) return LIBUSB_ERROR_NO_MEM; @@ -958,6 +1094,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); @@ -978,7 +1115,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); @@ -989,7 +1126,7 @@ int API_EXPORTED libusb_open(libusb_device *dev, /** \ingroup dev * Convenience function for finding a device with a particular * <tt>idVendor</tt>/<tt>idProduct</tt> 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. * @@ -1052,7 +1189,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; @@ -1240,7 +1377,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 + * <tt>dev</tt> 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. * @@ -1262,6 +1406,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) @@ -1275,7 +1420,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 <tt>dev</tt>, 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 @@ -1293,6 +1441,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) @@ -1303,6 +1452,9 @@ int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev, if (interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; + if (!dev->dev->attached) + return LIBUSB_ERROR_NO_DEVICE; + usbi_mutex_lock(&dev->lock); if (dev->claimed_interfaces & (1 << interface_number)) goto out; @@ -1323,6 +1475,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 <tt>dev</tt>, the kernel + * driver will be re-attached after releasing the interface. + * * \param dev a device handle * \param interface_number the <tt>bInterfaceNumber</tt> of the * previously-claimed interface @@ -1330,6 +1485,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) @@ -1385,6 +1541,11 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev, return LIBUSB_ERROR_INVALID_PARAM; usbi_mutex_lock(&dev->lock); + if (!dev->dev->attached) { + usbi_mutex_unlock(&dev->lock); + return LIBUSB_ERROR_NO_DEVICE; + } + if (!(dev->claimed_interfaces & (1 << interface_number))) { usbi_mutex_unlock(&dev->lock); return LIBUSB_ERROR_NOT_FOUND; @@ -1415,6 +1576,9 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev, unsigned char endpoint) { usbi_dbg("endpoint %x", endpoint); + if (!dev->dev->attached) + return LIBUSB_ERROR_NO_DEVICE; + return usbi_backend->clear_halt(dev, endpoint); } @@ -1440,12 +1604,15 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev, int API_EXPORTED libusb_reset_device(libusb_device_handle *dev) { usbi_dbg(""); + if (!dev->dev->attached) + return LIBUSB_ERROR_NO_DEVICE; + return usbi_backend->reset_device(dev); } /** \ingroup 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. @@ -1464,6 +1631,10 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev, int interface_number) { usbi_dbg("interface %d", interface_number); + + if (!dev->dev->attached) + return LIBUSB_ERROR_NO_DEVICE; + if (usbi_backend->kernel_driver_active) return usbi_backend->kernel_driver_active(dev, interface_number); else @@ -1476,6 +1647,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 @@ -1491,6 +1666,10 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev, int interface_number) { usbi_dbg("interface %d", interface_number); + + if (!dev->dev->attached) + return LIBUSB_ERROR_NO_DEVICE; + if (usbi_backend->detach_kernel_driver) return usbi_backend->detach_kernel_driver(dev, interface_number); else @@ -1521,36 +1700,68 @@ int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev, int interface_number) { usbi_dbg("interface %d", interface_number); + + if (!dev->dev->attached) + return LIBUSB_ERROR_NO_DEVICE; + if (usbi_backend->attach_kernel_driver) return usbi_backend->attach_kernel_driver(dev, interface_number); else return LIBUSB_ERROR_NOT_SUPPORTED; } +/** \ingroup dev + * Enable/disable 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 @@ -1565,7 +1776,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 @@ -1578,11 +1789,18 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level) */ int API_EXPORTED libusb_init(libusb_context **context) { + struct libusb_device *dev, *next; char *dbg = getenv("LIBUSB_DEBUG"); struct libusb_context *ctx; + static int first_init = 1; int r = 0; usbi_mutex_static_lock(&default_context_lock); + + if (!timestamp_origin.tv_sec) { + usbi_gettimeofday(×tamp_origin, NULL); + } + if (!context && usbi_default_context) { usbi_dbg("reusing default context"); default_context_refcnt++; @@ -1590,12 +1808,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); @@ -1603,47 +1824,70 @@ 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); - - if (usbi_backend->init) { - r = usbi_backend->init(ctx); - if (r) - goto err_free_ctx; + /* 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); + usbi_mutex_init(&ctx->hotplug_cbs_lock, NULL); list_init(&ctx->usb_devs); list_init(&ctx->open_devs); + list_init(&ctx->hotplug_cbs); - r = usbi_io_init(ctx); - if (r < 0) { - if (usbi_backend->exit) - usbi_backend->exit(); - goto err_destroy_mutex; + 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; - } else if (!usbi_default_context) { - usbi_dbg("created default context"); - usbi_default_context = ctx; - default_context_refcnt++; + if (usbi_backend->init) { + r = usbi_backend->init(ctx); + if (r) + goto err_free_ctx; } + + r = usbi_io_init(ctx); + if (r < 0) + goto err_backend_exit; + usbi_mutex_static_unlock(&default_context_lock); + if (context) + *context = ctx; + return 0; -err_destroy_mutex: +err_backend_exit: + if (usbi_backend->exit) + usbi_backend->exit(); +err_free_ctx: + if (ctx == usbi_default_context) + usbi_default_context = NULL; + usbi_mutex_destroy(&ctx->open_devs_lock); usbi_mutex_destroy(&ctx->usb_devs_lock); -err_free_ctx: + usbi_mutex_destroy(&ctx->hotplug_cbs_lock); + + 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); + free(ctx); err_unlock: usbi_mutex_static_unlock(&default_context_lock); @@ -1657,13 +1901,15 @@ err_unlock: */ void API_EXPORTED libusb_exit(struct libusb_context *ctx) { + struct libusb_device *dev, *next; + usbi_dbg(""); USBI_GET_CONTEXT(ctx); /* if working with default context, only actually do the deinitialization * if we're the last user */ + usbi_mutex_static_lock(&default_context_lock); if (ctx == usbi_default_context) { - 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); @@ -1671,11 +1917,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); - /* a little sanity check. doesn't bother with open_devs locking because - * unless there is an application bug, nobody will be accessing this. */ + usbi_mutex_static_lock(&active_contexts_lock); + list_del (&ctx->list); + usbi_mutex_static_unlock(&active_contexts_lock); + + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + usbi_hotplug_deregister_all(ctx); + usbi_mutex_lock(&ctx->usb_devs_lock); + list_for_each_entry_safe(dev, next, &ctx->usb_devs, list, struct libusb_device) { + list_del(&dev->list); + libusb_unref_device(dev); + } + usbi_mutex_unlock(&ctx->usb_devs_lock); + } + + /* a few sanity checks. don't bother with locking because unless + * there is an application bug, nobody will be accessing these. */ + if (!list_empty(&ctx->usb_devs)) + usbi_warn(ctx, "some libusb_devices were leaked"); if (!list_empty(&ctx->open_devs)) usbi_warn(ctx, "application left some devices open"); @@ -1685,21 +1947,29 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx) usbi_mutex_destroy(&ctx->open_devs_lock); usbi_mutex_destroy(&ctx->usb_devs_lock); + usbi_mutex_destroy(&ctx->hotplug_cbs_lock); free(ctx); } /** \ingroup misc * Check at runtime if the loaded library has a given capability. + * This call should be performed after \ref libusb_init(), to ensure the + * backend has updated its capability set. * * \param capability the \ref libusb_capability to check for - * \returns 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; } @@ -1732,85 +2002,152 @@ 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, const char * str) +{ + UNUSED(ctx); + fputs(str, stderr); +} + +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 == 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 == LIBUSB_LOG_LEVEL_INFO && ctx->debug < LIBUSB_LOG_LEVEL_INFO) return; - if (level == LOG_LEVEL_INFO && ctx->debug < 3) + if (level == LIBUSB_LOG_LEVEL_DEBUG && ctx->debug < LIBUSB_LOG_LEVEL_DEBUG) return; #endif +#ifdef __ANDROID__ + int prio; + switch (level) { + case LOG_LEVEL_INFO: + prio = ANDROID_LOG_INFO; + break; + case LOG_LEVEL_WARNING: + prio = ANDROID_LOG_WARN; + break; + case LOG_LEVEL_ERROR: + prio = ANDROID_LOG_ERROR; + break; + case LOG_LEVEL_DEBUG: + prio = ANDROID_LOG_DEBUG; + break; + default: + prio = ANDROID_LOG_UNKNOWN; + break; + } + + __android_log_vprint(prio, "LibUsb", format, args); +#else 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, "[timestamp] [threadID] facility level [function call] <message>\n"); + usbi_log_str(ctx, "--------------------------------------------------------------------------------\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: + break; 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, buf); +#endif } -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; @@ -1821,19 +2158,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: @@ -1860,13 +2196,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"; + + 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**"; } /** \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/third_party/libusb/src/libusb/descriptor.c b/third_party/libusb/src/libusb/descriptor.c index e358e9e..ba6d1467 100644 --- a/third_party/libusb/src/libusb/descriptor.c +++ b/third_party/libusb/src/libusb/descriptor.c @@ -1,7 +1,8 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ /* - * USB descriptor handling functions for libusb - * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org> - * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * USB descriptor handling functions for libusbx + * Copyright © 2007 Daniel Drake <dsd@gentoo.org> + * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -39,12 +40,14 @@ /* set host_endian if the w values are already in host endian format, * as opposed to bus endian. */ -int usbi_parse_descriptor(unsigned char *source, const char *descriptor, +int usbi_parse_descriptor(const unsigned char *source, const char *descriptor, void *dest, int host_endian) { - unsigned char *sp = source, *dp = dest; + const unsigned char *sp = source; + unsigned char *dp = dest; uint16_t w; const char *cp; + uint32_t d; for (cp = descriptor; *cp; cp++) { switch (*cp) { @@ -63,6 +66,24 @@ int usbi_parse_descriptor(unsigned char *source, const char *descriptor, sp += 2; dp += 2; break; + case 'd': /* 32-bit word, convert from little endian to CPU */ + dp += ((uintptr_t)dp & 1); /* Align to word boundary */ + + if (host_endian) { + memcpy(dp, sp, 4); + } else { + d = (sp[3] << 24) | (sp[2] << 16) | + (sp[1] << 8) | sp[0]; + *((uint32_t *)dp) = d; + } + sp += 4; + dp += 4; + break; + case 'u': /* 16 byte UUID */ + memcpy(dp, sp, 16); + sp += 16; + dp += 16; + break; } } @@ -85,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; @@ -114,10 +141,14 @@ 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 */ @@ -188,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; @@ -198,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) { @@ -208,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; @@ -224,11 +281,17 @@ 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 */ @@ -256,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); @@ -283,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; @@ -306,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; } @@ -331,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; @@ -371,12 +432,18 @@ 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 */ @@ -411,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; @@ -423,11 +494,56 @@ 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 = 0; + + r = usbi_backend->get_device_descriptor(dev, (unsigned char *) &dev->device_descriptor, + &host_endian); + if (r < 0) + return r; + + if (!host_endian) { + dev->device_descriptor.bcdUSB = libusb_le16_to_cpu(dev->device_descriptor.bcdUSB); + dev->device_descriptor.idVendor = libusb_le16_to_cpu(dev->device_descriptor.idVendor); + dev->device_descriptor.idProduct = libusb_le16_to_cpu(dev->device_descriptor.idProduct); + dev->device_descriptor.bcdDevice = libusb_le16_to_cpu(dev->device_descriptor.bcdDevice); + } + + return LIBUSB_SUCCESS; +} + /** \ingroup desc * Get the USB device descriptor for a given device. * * This is a non-blocking function; the device descriptor is cached in memory. * + * Note since 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 @@ -435,22 +551,9 @@ err: int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc) { - unsigned char raw_desc[DEVICE_DESC_LENGTH]; - int host_endian = 0; - int r; - usbi_dbg(""); - r = usbi_backend->get_device_descriptor(dev, raw_desc, &host_endian); - if (r < 0) - return r; - - memcpy((unsigned char *) desc, raw_desc, sizeof(raw_desc)); - if (!host_endian) { - desc->bcdUSB = libusb_le16_to_cpu(desc->bcdUSB); - desc->idVendor = libusb_le16_to_cpu(desc->idVendor); - desc->idProduct = libusb_le16_to_cpu(desc->idProduct); - desc->bcdDevice = libusb_le16_to_cpu(desc->bcdDevice); - } + memcpy((unsigned char *) desc, (unsigned char *) &dev->device_descriptor, + sizeof (dev->device_descriptor)); return 0; } @@ -471,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; } @@ -536,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; @@ -546,44 +633,27 @@ 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; } @@ -635,8 +705,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) @@ -664,6 +744,394 @@ void API_EXPORTED libusb_free_config_descriptor( } /** \ingroup desc + * Get an endpoints superspeed endpoint companion descriptor (if any) + * + * \param ctx the context to operate on, or NULL for the default context + * \param endpoint endpoint descriptor from which to get the superspeed + * endpoint companion descriptor + * \param ep_comp output location for the superspeed endpoint companion + * descriptor. Only valid if 0 was returned. Must be freed with + * libusb_free_ss_endpoint_companion_descriptor() after use. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns another LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_ss_endpoint_companion_descriptor( + struct libusb_context *ctx, + const struct libusb_endpoint_descriptor *endpoint, + struct libusb_ss_endpoint_companion_descriptor **ep_comp) +{ + struct usb_descriptor_header header; + int size = endpoint->extra_length; + const unsigned char *buffer = endpoint->extra; + + *ep_comp = NULL; + + while (size >= DESC_HEADER_LENGTH) { + usbi_parse_descriptor(buffer, "bb", &header, 0); + if (header.bLength < 2 || header.bLength > size) { + usbi_err(ctx, "invalid descriptor length %d", + header.bLength); + return LIBUSB_ERROR_IO; + } + if (header.bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) { + buffer += header.bLength; + size -= header.bLength; + continue; + } + if (header.bLength < LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) { + usbi_err(ctx, "invalid ss-ep-comp-desc length %d", + header.bLength); + return LIBUSB_ERROR_IO; + } + *ep_comp = malloc(sizeof(**ep_comp)); + if (*ep_comp == NULL) + return LIBUSB_ERROR_NO_MEM; + usbi_parse_descriptor(buffer, "bbbbw", *ep_comp, 0); + return LIBUSB_SUCCESS; + } + return LIBUSB_ERROR_NOT_FOUND; +} + +/** \ingroup desc + * Free a superspeed endpoint companion descriptor obtained from + * libusb_get_ss_endpoint_companion_descriptor(). + * It is safe to call this function with a NULL ep_comp parameter, in which + * case the function simply returns. + * + * \param ep_comp the superspeed endpoint companion descriptor to free + */ +void API_EXPORTED libusb_free_ss_endpoint_companion_descriptor( + struct libusb_ss_endpoint_companion_descriptor *ep_comp) +{ + free(ep_comp); +} + +static int parse_bos(struct libusb_context *ctx, + struct libusb_bos_descriptor **bos, + unsigned char *buffer, int size, int host_endian) +{ + struct libusb_bos_descriptor bos_header, *_bos; + struct libusb_bos_dev_capability_descriptor dev_cap; + int i; + + if (size < LIBUSB_DT_BOS_SIZE) { + usbi_err(ctx, "short bos descriptor read %d/%d", + size, LIBUSB_DT_BOS_SIZE); + return LIBUSB_ERROR_IO; + } + + usbi_parse_descriptor(buffer, "bbwb", &bos_header, host_endian); + if (bos_header.bDescriptorType != LIBUSB_DT_BOS) { + usbi_err(ctx, "unexpected descriptor %x (expected %x)", + bos_header.bDescriptorType, LIBUSB_DT_BOS); + return LIBUSB_ERROR_IO; + } + if (bos_header.bLength < LIBUSB_DT_BOS_SIZE) { + usbi_err(ctx, "invalid bos bLength (%d)", bos_header.bLength); + return LIBUSB_ERROR_IO; + } + if (bos_header.bLength > size) { + usbi_err(ctx, "short bos descriptor read %d/%d", + size, bos_header.bLength); + return LIBUSB_ERROR_IO; + } + + _bos = calloc (1, + sizeof(*_bos) + bos_header.bNumDeviceCaps * sizeof(void *)); + if (!_bos) + return LIBUSB_ERROR_NO_MEM; + + usbi_parse_descriptor(buffer, "bbwb", _bos, host_endian); + buffer += bos_header.bLength; + size -= bos_header.bLength; + + /* Get the device capability descriptors */ + for (i = 0; i < bos_header.bNumDeviceCaps; i++) { + if (size < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) { + usbi_warn(ctx, "short dev-cap descriptor read %d/%d", + size, LIBUSB_DT_DEVICE_CAPABILITY_SIZE); + break; + } + usbi_parse_descriptor(buffer, "bbb", &dev_cap, host_endian); + if (dev_cap.bDescriptorType != LIBUSB_DT_DEVICE_CAPABILITY) { + usbi_warn(ctx, "unexpected descriptor %x (expected %x)", + dev_cap.bDescriptorType, LIBUSB_DT_DEVICE_CAPABILITY); + break; + } + if (dev_cap.bLength < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) { + usbi_err(ctx, "invalid dev-cap bLength (%d)", + dev_cap.bLength); + libusb_free_bos_descriptor(_bos); + return LIBUSB_ERROR_IO; + } + if (dev_cap.bLength > size) { + usbi_warn(ctx, "short dev-cap descriptor read %d/%d", + size, dev_cap.bLength); + break; + } + + _bos->dev_capability[i] = malloc(dev_cap.bLength); + if (!_bos->dev_capability[i]) { + libusb_free_bos_descriptor(_bos); + return LIBUSB_ERROR_NO_MEM; + } + memcpy(_bos->dev_capability[i], buffer, dev_cap.bLength); + buffer += dev_cap.bLength; + size -= dev_cap.bLength; + } + _bos->bNumDeviceCaps = (uint8_t)i; + *bos = _bos; + + return LIBUSB_SUCCESS; +} + +/** \ingroup desc + * Get a Binary Object Store (BOS) descriptor + * This is a BLOCKING function, which will send requests to the device. + * + * \param handle the handle of an open libusb device + * \param bos output location for the BOS descriptor. Only valid if 0 was returned. + * Must be freed with \ref libusb_free_bos_descriptor() after use. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the device doesn't have a BOS descriptor + * \returns another LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *handle, + struct libusb_bos_descriptor **bos) +{ + struct libusb_bos_descriptor _bos; + uint8_t bos_header[LIBUSB_DT_BOS_SIZE] = {0}; + unsigned char *bos_data = NULL; + const int host_endian = 0; + int r; + + /* Read the BOS. This generates 2 requests on the bus, + * one for the header, and one for the full BOS */ + r = libusb_get_descriptor(handle, LIBUSB_DT_BOS, 0, bos_header, + LIBUSB_DT_BOS_SIZE); + if (r < 0) { + if (r != LIBUSB_ERROR_PIPE) + usbi_err(handle->dev->ctx, "failed to read BOS (%d)", r); + return r; + } + if (r < LIBUSB_DT_BOS_SIZE) { + usbi_err(handle->dev->ctx, "short BOS read %d/%d", + r, LIBUSB_DT_BOS_SIZE); + return LIBUSB_ERROR_IO; + } + + usbi_parse_descriptor(bos_header, "bbwb", &_bos, host_endian); + usbi_dbg("found BOS descriptor: size %d bytes, %d capabilities", + _bos.wTotalLength, _bos.bNumDeviceCaps); + bos_data = calloc(_bos.wTotalLength, 1); + if (bos_data == NULL) + return LIBUSB_ERROR_NO_MEM; + + r = libusb_get_descriptor(handle, LIBUSB_DT_BOS, 0, bos_data, + _bos.wTotalLength); + if (r >= 0) + r = parse_bos(handle->dev->ctx, bos, bos_data, r, host_endian); + else + usbi_err(handle->dev->ctx, "failed to read BOS (%d)", r); + + free(bos_data); + return r; +} + +/** \ingroup desc + * Free a BOS descriptor obtained from libusb_get_bos_descriptor(). + * It is safe to call this function with a NULL bos parameter, in which + * case the function simply returns. + * + * \param bos the BOS descriptor to free + */ +void API_EXPORTED libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos) +{ + int i; + + if (!bos) + return; + + for (i = 0; i < bos->bNumDeviceCaps; i++) + free(bos->dev_capability[i]); + free(bos); +} + +/** \ingroup desc + * Get an USB 2.0 Extension descriptor + * + * \param ctx the context to operate on, or NULL for the default context + * \param dev_cap Device Capability descriptor with a bDevCapabilityType of + * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION + * LIBUSB_BT_USB_2_0_EXTENSION + * \param usb_2_0_extension output location for the USB 2.0 Extension + * descriptor. Only valid if 0 was returned. Must be freed with + * libusb_free_usb_2_0_extension_descriptor() after use. + * \returns 0 on success + * \returns a LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_usb_2_0_extension_descriptor( + struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension) +{ + struct libusb_usb_2_0_extension_descriptor *_usb_2_0_extension; + const int host_endian = 0; + + if (dev_cap->bDevCapabilityType != LIBUSB_BT_USB_2_0_EXTENSION) { + usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)", + dev_cap->bDevCapabilityType, + LIBUSB_BT_USB_2_0_EXTENSION); + return LIBUSB_ERROR_INVALID_PARAM; + } + if (dev_cap->bLength < LIBUSB_BT_USB_2_0_EXTENSION_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %d/%d", + dev_cap->bLength, LIBUSB_BT_USB_2_0_EXTENSION_SIZE); + return LIBUSB_ERROR_IO; + } + + _usb_2_0_extension = malloc(sizeof(*_usb_2_0_extension)); + if (!_usb_2_0_extension) + return LIBUSB_ERROR_NO_MEM; + + usbi_parse_descriptor((unsigned char *)dev_cap, "bbbd", + _usb_2_0_extension, host_endian); + + *usb_2_0_extension = _usb_2_0_extension; + return LIBUSB_SUCCESS; +} + +/** \ingroup desc + * Free a USB 2.0 Extension descriptor obtained from + * libusb_get_usb_2_0_extension_descriptor(). + * It is safe to call this function with a NULL usb_2_0_extension parameter, + * in which case the function simply returns. + * + * \param usb_2_0_extension the USB 2.0 Extension descriptor to free + */ +void API_EXPORTED libusb_free_usb_2_0_extension_descriptor( + struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension) +{ + free(usb_2_0_extension); +} + +/** \ingroup desc + * Get a SuperSpeed USB Device Capability descriptor + * + * \param ctx the context to operate on, or NULL for the default context + * \param dev_cap Device Capability descriptor with a bDevCapabilityType of + * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY + * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY + * \param ss_usb_device_cap output location for the SuperSpeed USB Device + * Capability descriptor. Only valid if 0 was returned. Must be freed with + * libusb_free_ss_usb_device_capability_descriptor() after use. + * \returns 0 on success + * \returns a LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_ss_usb_device_capability_descriptor( + struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap) +{ + struct libusb_ss_usb_device_capability_descriptor *_ss_usb_device_cap; + const int host_endian = 0; + + if (dev_cap->bDevCapabilityType != LIBUSB_BT_SS_USB_DEVICE_CAPABILITY) { + usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)", + dev_cap->bDevCapabilityType, + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY); + return LIBUSB_ERROR_INVALID_PARAM; + } + if (dev_cap->bLength < LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %d/%d", + dev_cap->bLength, LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE); + return LIBUSB_ERROR_IO; + } + + _ss_usb_device_cap = malloc(sizeof(*_ss_usb_device_cap)); + if (!_ss_usb_device_cap) + return LIBUSB_ERROR_NO_MEM; + + usbi_parse_descriptor((unsigned char *)dev_cap, "bbbbwbbw", + _ss_usb_device_cap, host_endian); + + *ss_usb_device_cap = _ss_usb_device_cap; + return LIBUSB_SUCCESS; +} + +/** \ingroup desc + * Free a SuperSpeed USB Device Capability descriptor obtained from + * libusb_get_ss_usb_device_capability_descriptor(). + * It is safe to call this function with a NULL ss_usb_device_cap + * parameter, in which case the function simply returns. + * + * \param ss_usb_device_cap the USB 2.0 Extension descriptor to free + */ +void API_EXPORTED libusb_free_ss_usb_device_capability_descriptor( + struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap) +{ + free(ss_usb_device_cap); +} + +/** \ingroup desc + * Get a Container ID descriptor + * + * \param ctx the context to operate on, or NULL for the default context + * \param dev_cap Device Capability descriptor with a bDevCapabilityType of + * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID + * LIBUSB_BT_CONTAINER_ID + * \param container_id output location for the Container ID descriptor. + * Only valid if 0 was returned. Must be freed with + * libusb_free_container_id_descriptor() after use. + * \returns 0 on success + * \returns a LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_container_id_descriptor(struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_container_id_descriptor **container_id) +{ + struct libusb_container_id_descriptor *_container_id; + const int host_endian = 0; + + if (dev_cap->bDevCapabilityType != LIBUSB_BT_CONTAINER_ID) { + usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)", + dev_cap->bDevCapabilityType, + LIBUSB_BT_CONTAINER_ID); + return LIBUSB_ERROR_INVALID_PARAM; + } + if (dev_cap->bLength < LIBUSB_BT_CONTAINER_ID_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %d/%d", + dev_cap->bLength, LIBUSB_BT_CONTAINER_ID_SIZE); + return LIBUSB_ERROR_IO; + } + + _container_id = malloc(sizeof(*_container_id)); + if (!_container_id) + return LIBUSB_ERROR_NO_MEM; + + usbi_parse_descriptor((unsigned char *)dev_cap, "bbbbu", + _container_id, host_endian); + + *container_id = _container_id; + return LIBUSB_SUCCESS; +} + +/** \ingroup desc + * Free a Container ID descriptor obtained from + * libusb_get_container_id_descriptor(). + * It is safe to call this function with a NULL container_id parameter, + * in which case the function simply returns. + * + * \param container_id the USB 2.0 Extension descriptor to free + */ +void API_EXPORTED libusb_free_container_id_descriptor( + struct libusb_container_id_descriptor *container_id) +{ + free(container_id); +} + +/** \ingroup desc * Retrieve a string descriptor in C style ASCII. * * Wrapper around libusb_get_string_descriptor(). Uses the first language @@ -718,7 +1186,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]; @@ -727,4 +1195,3 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev, data[di] = 0; return di; } - diff --git a/third_party/libusb/src/libusb/hotplug.c b/third_party/libusb/src/libusb/hotplug.c new file mode 100644 index 0000000..6b04342 --- /dev/null +++ b/third_party/libusb/src/libusb/hotplug.c @@ -0,0 +1,320 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ +/* + * Hotplug functions for libusbx + * Copyright © 2012-2013 Nathan Hjelm <hjelmn@mac.com> + * Copyright © 2012-2013 Peter Stuge <peter@stuge.se> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#include <assert.h> + +#include "libusbi.h" +#include "hotplug.h" + +/** + * @defgroup hotplug Device hotplug event notification + * This page details how to use the libusb hotplug interface, where available. + * + * Be mindful that not all platforms currently implement hotplug notification and + * that you should first call on \ref libusb_has_capability() with parameter + * \ref LIBUSB_CAP_HAS_HOTPLUG to confirm that hotplug support is available. + * + * \page hotplug Device hotplug event notification + * + * \section intro Introduction + * + * Version 1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102, has added support + * for hotplug events on <b>some</b> platforms (you should test if your platform + * supports hotplug notification by calling \ref libusb_has_capability() with + * parameter \ref LIBUSB_CAP_HAS_HOTPLUG). + * + * This interface allows you to request notification for the arrival and departure + * of matching USB devices. + * + * To receive hotplug notification you register a callback by calling + * \ref libusb_hotplug_register_callback(). This function will optionally return + * a handle that can be passed to \ref libusb_hotplug_deregister_callback(). + * + * A callback function must return an int (0 or 1) indicating whether the callback is + * expecting additional events. Returning 0 will rearm the callback and 1 will cause + * the callback to be deregistered. + * + * Callbacks for a particular context are automatically deregistered by libusb_exit(). + * + * As of 1.0.16 there are two supported hotplug events: + * - LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: A device has arrived and is ready to use + * - LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: A device has left and is no longer available + * + * A hotplug event can listen for either or both of these events. + * + * Note: If you receive notification that a device has left and you have any + * a libusb_device_handles for the device it is up to you to call libusb_close() + * on each handle to free up any remaining resources associated with the device. + * Once a device has left any libusb_device_handle associated with the device + * are invalid and will remain so even if the device comes back. + * + * When handling a LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED event it is considered + * safe to call any 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(). + * + * The following code provides an example of the usage of the hotplug interface: +\code +static int count = 0; + +int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev, + libusb_hotplug_event event, void *user_data) { + static libusb_device_handle *handle = NULL; + struct libusb_device_descriptor desc; + int rc; + + (void)libusb_get_device_descriptor(dev, &desc); + + if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) { + rc = libusb_open(dev, &handle); + if (LIBUSB_SUCCESS != rc) { + printf("Could not open USB device\n"); + } + } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) { + if (handle) { + libusb_close(handle); + handle = NULL; + } + } else { + printf("Unhandled event %d\n", event); + } + count++; + + return 0; +} + +int main (void) { + libusb_hotplug_callback_handle handle; + int rc; + + libusb_init(NULL); + + rc = libusb_hotplug_register_callback(NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, 0x045a, 0x5005, + LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback, NULL, + &handle); + if (LIBUSB_SUCCESS != rc) { + printf("Error creating a hotplug callback\n"); + libusb_exit(NULL); + return EXIT_FAILURE; + } + + while (count < 2) { + usleep(10000); + } + + libusb_hotplug_deregister_callback(handle); + libusb_exit(NULL); + + return 0; +} +\endcode + */ + +static int usbi_hotplug_match_cb (struct libusb_context *ctx, + struct libusb_device *dev, libusb_hotplug_event event, + struct libusb_hotplug_callback *hotplug_cb) +{ + /* Handle lazy deregistration of callback */ + if (hotplug_cb->needs_free) { + /* Free callback */ + return 1; + } + + if (!(hotplug_cb->events & event)) { + return 0; + } + + if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->vendor_id && + hotplug_cb->vendor_id != dev->device_descriptor.idVendor) { + return 0; + } + + if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->product_id && + hotplug_cb->product_id != dev->device_descriptor.idProduct) { + return 0; + } + + if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->dev_class && + hotplug_cb->dev_class != dev->device_descriptor.bDeviceClass) { + return 0; + } + + return hotplug_cb->cb (ctx == usbi_default_context ? NULL : ctx, + dev, event, hotplug_cb->user_data); +} + +void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, + libusb_hotplug_event event) +{ + struct libusb_hotplug_callback *hotplug_cb, *next; + int ret; + + usbi_mutex_lock(&ctx->hotplug_cbs_lock); + + list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) { + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + ret = usbi_hotplug_match_cb (ctx, dev, event, hotplug_cb); + usbi_mutex_lock(&ctx->hotplug_cbs_lock); + + if (ret) { + list_del(&hotplug_cb->list); + free(hotplug_cb); + } + } + + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + + /* 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); + } +} + +int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx, + libusb_hotplug_event events, libusb_hotplug_flag flags, + int vendor_id, int product_id, int dev_class, + libusb_hotplug_callback_fn cb_fn, void *user_data, + libusb_hotplug_callback_handle *handle) +{ + libusb_hotplug_callback *new_callback; + static int handle_id = 1; + + /* check for hotplug support */ + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + /* check for sane values */ + if ((LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) || + (LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) || + (LIBUSB_HOTPLUG_MATCH_ANY != dev_class && (~0xff & dev_class)) || + !cb_fn) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + USBI_GET_CONTEXT(ctx); + + new_callback = (libusb_hotplug_callback *)calloc(1, sizeof (*new_callback)); + if (!new_callback) { + return LIBUSB_ERROR_NO_MEM; + } + + new_callback->ctx = ctx; + new_callback->vendor_id = vendor_id; + new_callback->product_id = product_id; + new_callback->dev_class = dev_class; + new_callback->flags = flags; + new_callback->events = events; + new_callback->cb = cb_fn; + new_callback->user_data = user_data; + new_callback->needs_free = 0; + + usbi_mutex_lock(&ctx->hotplug_cbs_lock); + + /* protect the handle by the context hotplug lock. it doesn't matter if the same handle + * is used for different contexts only that the handle is unique for this context */ + new_callback->handle = handle_id++; + + list_add(&new_callback->list, &ctx->hotplug_cbs); + + if (flags & LIBUSB_HOTPLUG_ENUMERATE) { + struct libusb_device *dev; + + usbi_mutex_lock(&ctx->usb_devs_lock); + + list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) { + (void) usbi_hotplug_match_cb (ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, new_callback); + } + + usbi_mutex_unlock(&ctx->usb_devs_lock); + } + + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + + if (handle) { + *handle = new_callback->handle; + } + + return LIBUSB_SUCCESS; +} + +void API_EXPORTED libusb_hotplug_deregister_callback (struct libusb_context *ctx, + libusb_hotplug_callback_handle handle) +{ + struct libusb_hotplug_callback *hotplug_cb; + libusb_hotplug_message message; + ssize_t ret; + + /* check for hotplug support */ + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + return; + } + + USBI_GET_CONTEXT(ctx); + + usbi_mutex_lock(&ctx->hotplug_cbs_lock); + list_for_each_entry(hotplug_cb, &ctx->hotplug_cbs, list, + struct libusb_hotplug_callback) { + if (handle == hotplug_cb->handle) { + /* Mark this callback for deregistration */ + hotplug_cb->needs_free = 1; + } + } + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + + /* wakeup handle_events to do the actual free */ + memset(&message, 0, sizeof(message)); + ret = usbi_write(ctx->hotplug_pipe[1], &message, sizeof(message)); + if (sizeof(message) != ret) { + usbi_err(ctx, "error writing hotplug message"); + } +} + +void usbi_hotplug_deregister_all(struct libusb_context *ctx) { + struct libusb_hotplug_callback *hotplug_cb, *next; + + usbi_mutex_lock(&ctx->hotplug_cbs_lock); + list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, + struct libusb_hotplug_callback) { + list_del(&hotplug_cb->list); + free(hotplug_cb); + } + + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); +} diff --git a/third_party/libusb/src/libusb/hotplug.h b/third_party/libusb/src/libusb/hotplug.h new file mode 100644 index 0000000..614ddbc --- /dev/null +++ b/third_party/libusb/src/libusb/hotplug.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ +/* + * Hotplug support for libusbx + * Copyright © 2012-2013 Nathan Hjelm <hjelmn@mac.com> + * Copyright © 2012-2013 Peter Stuge <peter@stuge.se> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if !defined(USBI_HOTPLUG_H) +#define USBI_HOTPLUG_H + +#ifndef LIBUSBI_H +#include "libusbi.h" +#endif + +/** \ingroup hotplug + * The hotplug callback structure. The user populates this structure with + * libusb_hotplug_prepare_callback() and then calls libusb_hotplug_register_callback() + * to receive notification of hotplug events. + */ +struct libusb_hotplug_callback { + /** Context this callback is associated with */ + struct libusb_context *ctx; + + /** Vendor ID to match or LIBUSB_HOTPLUG_MATCH_ANY */ + int vendor_id; + + /** Product ID to match or LIBUSB_HOTPLUG_MATCH_ANY */ + int product_id; + + /** Device class to match or LIBUSB_HOTPLUG_MATCH_ANY */ + int dev_class; + + /** Hotplug callback flags */ + libusb_hotplug_flag flags; + + /** Event(s) that will trigger this callback */ + libusb_hotplug_event events; + + /** Callback function to invoke for matching event/device */ + libusb_hotplug_callback_fn cb; + + /** Handle for this callback (used to match on deregister) */ + libusb_hotplug_callback_handle handle; + + /** User data that will be passed to the callback function */ + void *user_data; + + /** Callback is marked for deletion */ + int needs_free; + + /** List this callback is registered in (ctx->hotplug_cbs) */ + struct list_head list; +}; + +typedef struct libusb_hotplug_callback libusb_hotplug_callback; + +struct libusb_hotplug_message { + libusb_hotplug_event event; + struct libusb_device *device; +}; + +typedef struct libusb_hotplug_message libusb_hotplug_message; + +void usbi_hotplug_deregister_all(struct libusb_context *ctx); +void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, + libusb_hotplug_event event); + +#endif diff --git a/third_party/libusb/src/libusb/interrupt.c b/third_party/libusb/src/libusb/interrupt.c index addec78..59221f5 100644 --- a/third_party/libusb/src/libusb/interrupt.c +++ b/third_party/libusb/src/libusb/interrupt.c @@ -1,7 +1,9 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <stdlib.h> + #include "libusbi.h" int API_EXPORTED libusb_interrupt_handle_event(struct libusb_context* ctx) { diff --git a/third_party/libusb/src/libusb/interrupt.h b/third_party/libusb/src/libusb/interrupt.h index 73fd275..ac8d52d 100644 --- a/third_party/libusb/src/libusb/interrupt.h +++ b/third_party/libusb/src/libusb/interrupt.h @@ -1,4 +1,4 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/third_party/libusb/src/libusb/io.c b/third_party/libusb/src/libusb/io.c index e9bd312..4368b99 100644 --- a/third_party/libusb/src/libusb/io.c +++ b/third_party/libusb/src/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 <dsd@gentoo.org> - * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * I/O functions for libusbx + * Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org> + * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,33 +19,34 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <config.h> +#include "config.h" #include <errno.h> -#include <signal.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <time.h> - +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #endif - #ifdef USBI_TIMERFD_AVAILABLE #include <sys/timerfd.h> #endif #include "libusbi.h" +#include "hotplug.h" /** * \page io Synchronous and asynchronous device I/O * * \section intro Introduction * - * If you're using libusb in your application, you're probably wanting to + * 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. @@ -74,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. @@ -129,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 @@ -142,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 @@ -157,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 @@ -174,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. @@ -210,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 @@ -225,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. @@ -238,7 +240,7 @@ if (r == 0 && actual_length == sizeof(data)) { * -# <b>Allocation</b>: allocate a libusb_transfer * -# <b>Filling</b>: populate the libusb_transfer instance with information * about the transfer you wish to perform - * -# <b>Submission</b>: ask libusb to submit the transfer + * -# <b>Submission</b>: ask libusbx to submit the transfer * -# <b>Completion handling</b>: examine transfer results in the * libusb_transfer structure * -# <b>Deallocation</b>: clean up resources @@ -285,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. * @@ -324,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 @@ -466,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. * @@ -486,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 + * + * 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 * - * For the details on retrieving the set of file descriptors and determining - * the next timeout, see the \ref poll "polling and timing" API documentation. + * 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 @@ -551,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. * @@ -563,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 @@ -583,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 @@ -604,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 } @@ -632,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 @@ -644,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 } @@ -665,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 <em>any future platforms supported by libusb which may have time-based + * and <em>any future platforms supported by libusbx which may have time-based * event requirements</em>). 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(). @@ -699,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 @@ -710,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). * @@ -739,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); } @@ -753,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 @@ -765,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 * @@ -817,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(). * @@ -838,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); } @@ -854,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). @@ -880,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); } @@ -926,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. @@ -937,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). @@ -951,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 @@ -966,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 @@ -988,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 @@ -1005,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 @@ -1021,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 @@ -1070,6 +1128,17 @@ int usbi_io_init(struct libusb_context *ctx) if (r < 0) goto err_close_pipe; + /* create hotplug pipe */ + r = usbi_pipe(ctx->hotplug_pipe); + if (r < 0) { + r = LIBUSB_ERROR_OTHER; + goto err; + } + + r = usbi_add_pollfd(ctx, ctx->hotplug_pipe[0], POLLIN); + if (r < 0) + goto err_close_hp_pipe; + #ifdef USBI_TIMERFD_AVAILABLE ctx->timerfd = timerfd_create(usbi_backend->get_timerfd_clockid(), TFD_NONBLOCK); @@ -1079,7 +1148,7 @@ int usbi_io_init(struct libusb_context *ctx) if (r < 0) { usbi_remove_pollfd(ctx, ctx->ctrl_pipe[0]); close(ctx->timerfd); - goto err_close_pipe; + goto err_close_hp_pipe; } } else { usbi_dbg("timerfd not available (code %d error %d)", ctx->timerfd, errno); @@ -1089,6 +1158,9 @@ 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]); err_close_pipe: usbi_close(ctx->ctrl_pipe[0]); usbi_close(ctx->ctrl_pipe[1]); @@ -1107,6 +1179,9 @@ void usbi_io_exit(struct libusb_context *ctx) usbi_remove_pollfd(ctx, ctx->ctrl_pipe[0]); usbi_close(ctx->ctrl_pipe[0]); usbi_close(ctx->ctrl_pipe[1]); + usbi_remove_pollfd(ctx, ctx->hotplug_pipe[0]); + usbi_close(ctx->hotplug_pipe[0]); + usbi_close(ctx->hotplug_pipe[1]); #ifdef USBI_TIMERFD_AVAILABLE if (usbi_using_timerfd(ctx)) { usbi_remove_pollfd(ctx, ctx->timerfd); @@ -1141,7 +1216,7 @@ static int calculate_timeout(struct usbi_transfer *transfer) current_time.tv_sec += timeout / 1000; current_time.tv_nsec += (timeout % 1000) * 1000000; - if (current_time.tv_nsec > 1000000000) { + while (current_time.tv_nsec >= 1000000000) { current_time.tv_nsec -= 1000000000; current_time.tv_sec++; } @@ -1151,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; @@ -1161,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; } @@ -1186,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(). @@ -1232,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); @@ -1273,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. @@ -1291,7 +1437,7 @@ 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); itransfer->transferred = 0; @@ -1302,30 +1448,22 @@ 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); out: + updated_fds = (itransfer->flags & USBI_TRANSFER_UPDATED_FDS); usbi_mutex_unlock(&itransfer->lock); + if (updated_fds) + usbi_fd_notification(ctx); return r; } @@ -1353,7 +1491,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 @@ -1369,66 +1508,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 @@ -1456,14 +1535,8 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, if (usbi_using_timerfd(ctx)) r = arm_timerfd_for_next_timeout(ctx); usbi_mutex_unlock(&ctx->flying_transfers_lock); - - if (usbi_using_timerfd(ctx)) { - if (r < 0) - return r; - r = disarm_timerfd(ctx); - if (r < 0) - return r; - } + if (usbi_using_timerfd(ctx) && (r < 0)) + return r; if (status == LIBUSB_TRANSFER_COMPLETED && transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) { @@ -1512,11 +1585,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. * @@ -1532,14 +1605,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; } @@ -1555,11 +1629,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. * @@ -1602,7 +1676,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. * @@ -1622,7 +1696,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 @@ -1650,7 +1724,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 @@ -1676,9 +1750,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, * <b>and</b> 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. * @@ -1746,7 +1820,7 @@ int API_EXPORTED libusb_wait_for_event(libusb_context *ctx, struct timeval *tv) timeout.tv_sec += tv->tv_sec; timeout.tv_nsec += tv->tv_usec * 1000; - if (timeout.tv_nsec > 1000000000) { + while (timeout.tv_nsec >= 1000000000) { timeout.tv_nsec -= 1000000000; timeout.tv_sec++; } @@ -1826,10 +1900,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 */ @@ -1853,7 +1923,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; @@ -1862,7 +1932,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; @@ -1878,7 +1949,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) @@ -1916,9 +1987,36 @@ 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; + 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 < %d", + 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[1] is the timerfd */ - if (usbi_using_timerfd(ctx) && fds[1].revents) { + /* on timerfd configurations, fds[2] is the timerfd */ + if (usbi_using_timerfd(ctx) && fds[2].revents) { /* timerfd indicates that a timeout has expired */ int ret; usbi_dbg("timerfd triggered"); @@ -1935,7 +2033,7 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv) } else { /* more events pending... * prevent OS backend from trying to handle events on timerfd */ - fds[1].revents = 0; + fds[2].revents = 0; r--; } } @@ -1980,7 +2078,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 @@ -2129,9 +2227,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 @@ -2158,19 +2256,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 @@ -2179,10 +2277,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) { @@ -2196,21 +2294,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. * @@ -2219,7 +2317,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 */ @@ -2268,7 +2366,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); @@ -2286,7 +2384,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. * @@ -2364,7 +2462,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. @@ -2404,14 +2502,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) { @@ -2426,12 +2525,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) { @@ -2447,6 +2556,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/third_party/libusb/src/libusb/libusb-1.0.def b/third_party/libusb/src/libusb/libusb-1.0.def deleted file mode 100644 index 1d6a5d2..0000000 --- a/third_party/libusb/src/libusb/libusb-1.0.def +++ /dev/null @@ -1,120 +0,0 @@ -LIBRARY -EXPORTS - libusb_alloc_transfer - libusb_alloc_transfer@4 = libusb_alloc_transfer - libusb_attach_kernel_driver - libusb_attach_kernel_driver@8 = libusb_attach_kernel_driver - libusb_bulk_transfer - libusb_bulk_transfer@24 = libusb_bulk_transfer - libusb_cancel_transfer - libusb_cancel_transfer@4 = libusb_cancel_transfer - libusb_claim_interface - libusb_claim_interface@8 = libusb_claim_interface - libusb_clear_halt - libusb_clear_halt@8 = libusb_clear_halt - libusb_close - libusb_close@4 = libusb_close - libusb_control_transfer - libusb_control_transfer@32 = libusb_control_transfer - libusb_detach_kernel_driver - libusb_detach_kernel_driver@8 = libusb_detach_kernel_driver - libusb_error_name - libusb_error_name@4 = libusb_error_name - libusb_event_handler_active - libusb_event_handler_active@4 = libusb_event_handler_active - libusb_event_handling_ok - libusb_event_handling_ok@4 = libusb_event_handling_ok - libusb_exit - libusb_exit@4 = libusb_exit - libusb_free_config_descriptor - libusb_free_config_descriptor@4 = libusb_free_config_descriptor - libusb_free_device_list - libusb_free_device_list@8 = libusb_free_device_list - libusb_free_transfer - libusb_free_transfer@4 = libusb_free_transfer - libusb_get_active_config_descriptor - libusb_get_active_config_descriptor@8 = libusb_get_active_config_descriptor - libusb_get_bus_number - libusb_get_bus_number@4 = libusb_get_bus_number - libusb_get_config_descriptor - libusb_get_config_descriptor@12 = libusb_get_config_descriptor - libusb_get_config_descriptor_by_value - libusb_get_config_descriptor_by_value@12 = libusb_get_config_descriptor_by_value - libusb_get_configuration - libusb_get_configuration@8 = libusb_get_configuration - libusb_get_device - libusb_get_device@4 = libusb_get_device - libusb_get_device_address - libusb_get_device_address@4 = libusb_get_device_address - libusb_get_device_descriptor - libusb_get_device_descriptor@8 = libusb_get_device_descriptor - libusb_get_device_list - libusb_get_device_list@8 = libusb_get_device_list - libusb_get_device_speed - libusb_get_device_speed@4 = libusb_get_device_speed - libusb_get_max_iso_packet_size - libusb_get_max_iso_packet_size@8 = libusb_get_max_iso_packet_size - libusb_get_max_packet_size - libusb_get_max_packet_size@8 = libusb_get_max_packet_size - libusb_get_next_timeout - libusb_get_next_timeout@8 = libusb_get_next_timeout - libusb_get_pollfds - libusb_get_pollfds@4 = libusb_get_pollfds - libusb_get_string_descriptor_ascii - libusb_get_string_descriptor_ascii@16 = libusb_get_string_descriptor_ascii - libusb_get_version - libusb_get_version@0 = libusb_get_version - libusb_handle_events - libusb_handle_events@4 = libusb_handle_events - libusb_handle_events_completed - libusb_handle_events_completed@8 = libusb_handle_events_completed - libusb_handle_events_locked - libusb_handle_events_locked@8 = libusb_handle_events_locked - libusb_handle_events_timeout - libusb_handle_events_timeout@8 = libusb_handle_events_timeout - libusb_handle_events_timeout_completed - libusb_handle_events_timeout_completed@12 = libusb_handle_events_timeout_completed - libusb_has_capability - libusb_has_capability@4 = libusb_has_capability - libusb_init - libusb_init@4 = libusb_init - libusb_interrupt_transfer - libusb_interrupt_transfer@24 = libusb_interrupt_transfer - libusb_kernel_driver_active - libusb_kernel_driver_active@8 = libusb_kernel_driver_active - libusb_lock_event_waiters - libusb_lock_event_waiters@4 = libusb_lock_event_waiters - libusb_lock_events - libusb_lock_events@4 = libusb_lock_events - libusb_open - libusb_open@8 = libusb_open - libusb_open_device_with_vid_pid - libusb_open_device_with_vid_pid@12 = libusb_open_device_with_vid_pid - libusb_pollfds_handle_timeouts - libusb_pollfds_handle_timeouts@4 = libusb_pollfds_handle_timeouts - libusb_ref_device - libusb_ref_device@4 = libusb_ref_device - libusb_release_interface - libusb_release_interface@8 = libusb_release_interface - libusb_reset_device - libusb_reset_device@4 = libusb_reset_device - libusb_set_configuration - libusb_set_configuration@8 = libusb_set_configuration - libusb_set_debug - libusb_set_debug@8 = libusb_set_debug - libusb_set_interface_alt_setting - libusb_set_interface_alt_setting@12 = libusb_set_interface_alt_setting - libusb_set_pollfd_notifiers - libusb_set_pollfd_notifiers@16 = libusb_set_pollfd_notifiers - libusb_submit_transfer - libusb_submit_transfer@4 = libusb_submit_transfer - libusb_try_lock_events - libusb_try_lock_events@4 = libusb_try_lock_events - libusb_unlock_event_waiters - libusb_unlock_event_waiters@4 = libusb_unlock_event_waiters - libusb_unlock_events - libusb_unlock_events@4 = libusb_unlock_events - libusb_unref_device - libusb_unref_device@4 = libusb_unref_device - libusb_wait_for_event - libusb_wait_for_event@8 = libusb_wait_for_event diff --git a/third_party/libusb/src/libusb/libusb-1.0.rc b/third_party/libusb/src/libusb/libusb-1.0.rc deleted file mode 100644 index a59a430..0000000 --- a/third_party/libusb/src/libusb/libusb-1.0.rc +++ /dev/null @@ -1,56 +0,0 @@ -/* - * For Windows: input this file to the Resoure Compiler to produce a binary - * .res file. This is then embedded in the resultant library (like any other - * compilation object). - * The information can then be queried using standard APIs and can also be - * viewed with utilities such as Windows Explorer. - */ -#include "winresrc.h" - -#include "version.h" -#ifndef LIBUSB_VERSIONSTRING -#define LU_STR(s) #s -#define LU_XSTR(s) LU_STR(s) -#if LIBUSB_NANO > 0 -#define LIBUSB_VERSIONSTRING LU_XSTR(LIBUSB_MAJOR) "." LU_XSTR(LIBUSB_MINOR) "." LU_XSTR(LIBUSB_MICRO) "." LU_XSTR(LIBUSB_NANO) LIBUSB_RC "\0" -#else -#define LIBUSB_VERSIONSTRING LU_XSTR(LIBUSB_MAJOR) "." LU_XSTR(LIBUSB_MINOR) "." LU_XSTR(LIBUSB_MICRO) LIBUSB_RC "\0" -#endif -#endif - -VS_VERSION_INFO VERSIONINFO - FILEVERSION LIBUSB_MAJOR,LIBUSB_MINOR,LIBUSB_MICRO,LIBUSB_NANO - PRODUCTVERSION LIBUSB_MAJOR,LIBUSB_MINOR,LIBUSB_MICRO,LIBUSB_NANO - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "\0" - VALUE "CompanyName", "libusb.org\0" - VALUE "FileDescription", "C library for writing portable USB drivers in userspace\0" - VALUE "FileVersion", LIBUSB_VERSIONSTRING - VALUE "InternalName", "libusb\0" - VALUE "LegalCopyright", "See individual source files, GNU LGPL v2.1 or later.\0" - VALUE "LegalTrademarks", "http://www.gnu.org/licenses/lgpl-2.1.html\0" - VALUE "OriginalFilename", "libusb-1.0.dll\0" - VALUE "PrivateBuild", "\0" - VALUE "ProductName", "libusb-1.0\0" - VALUE "ProductVersion", LIBUSB_VERSIONSTRING - VALUE "SpecialBuild", "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END diff --git a/third_party/libusb/src/libusb/libusb.h b/third_party/libusb/src/libusb/libusb.h index 1d0dd7d..15bd0d5 100644 --- a/third_party/libusb/src/libusb/libusb.h +++ b/third_party/libusb/src/libusb/libusb.h @@ -1,7 +1,10 @@ /* - * Public libusb header file - * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org> - * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * Public libusbx header file + * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com> + * Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org> + * Copyright © 2012 Pete Batard <pete@akeo.ie> + * Copyright © 2012 Nathan Hjelm <hjelmn@cs.unm.edu> + * 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 @@ -22,8 +25,16 @@ #define LIBUSB_H #ifdef _MSC_VER +// Disable warning 4200 for [0]. +#pragma warning(push) +#pragma warning(disable: 4200) +#endif + +#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 @@ -36,7 +47,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; @@ -45,36 +56,49 @@ typedef unsigned __int32 uint32_t; #include <stdint.h> #endif +#if !defined(_WIN32_WCE) #include <sys/types.h> -#include <time.h> -#include <limits.h> +#endif #if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__) #include <sys/time.h> #endif +#include <time.h> +#include <limits.h> + /* '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 <windows.h> #if defined(interface) #undef interface #endif +#if !defined(__CYGWIN__) +#include <winsock.h> +#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 <em>one true calling * convention</em> (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 <tt>stdcall</tt> 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 @@ -82,7 +106,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. * @@ -90,19 +114,44 @@ 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 @@ -121,8 +170,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; } @@ -218,6 +267,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, @@ -229,15 +284,35 @@ enum libusb_descriptor_type { /** Hub descriptor */ LIBUSB_DT_HUB = 0x29, + + /** SuperSpeed Hub descriptor */ + LIBUSB_DT_SUPERSPEED_HUB = 0x2a, + + /** SuperSpeed Endpoint Companion descriptor */ + LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30 }; /* Descriptor sizes per descriptor type */ #define LIBUSB_DT_DEVICE_SIZE 18 #define LIBUSB_DT_CONFIG_SIZE 9 #define LIBUSB_DT_INTERFACE_SIZE 9 -#define LIBUSB_DT_ENDPOINT_SIZE 7 -#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define LIBUSB_DT_ENDPOINT_SIZE 7 +#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ #define LIBUSB_DT_HUB_NONVAR_SIZE 7 +#define LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE 6 +#define LIBUSB_DT_BOS_SIZE 5 +#define LIBUSB_DT_DEVICE_CAPABILITY_SIZE 3 + +/* BOS descriptor sizes */ +#define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7 +#define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10 +#define LIBUSB_BT_CONTAINER_ID_SIZE 20 + +/* We unwrap the BOS => define its max size */ +#define LIBUSB_DT_BOS_MAX_SIZE ((LIBUSB_DT_BOS_SIZE) +\ + (LIBUSB_BT_USB_2_0_EXTENSION_SIZE) +\ + (LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) +\ + (LIBUSB_BT_CONTAINER_ID_SIZE)) #define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ #define LIBUSB_ENDPOINT_DIR_MASK 0x80 @@ -275,7 +350,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, @@ -313,6 +388,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 @@ -392,7 +474,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 { @@ -446,7 +528,7 @@ struct libusb_device_descriptor { /** \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 { @@ -486,7 +568,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; @@ -496,7 +578,7 @@ struct libusb_endpoint_descriptor { /** \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 { @@ -536,7 +618,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; @@ -558,7 +640,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 { @@ -594,7 +676,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; @@ -602,6 +684,187 @@ struct libusb_config_descriptor { int extra_length; }; +/** \ingroup desc + * A structure representing the superspeed endpoint companion + * descriptor. This descriptor is documented in section 9.6.7 of + * the USB 3.0 specification. All multiple-byte fields are represented in + * host-endian format. + */ +struct libusb_ss_endpoint_companion_descriptor { + + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_SS_ENDPOINT_COMPANION in + * this context. */ + uint8_t bDescriptorType; + + + /** The maximum number of packets the endpoint can send or + * recieve as part of a burst. */ + uint8_t bMaxBurst; + + /** In bulk EP: bits 4:0 represents the maximum number of + * streams the EP supports. In isochronous EP: bits 1:0 + * represents the Mult - a zero based value that determines + * the maximum number of packets within a service interval */ + uint8_t bmAttributes; + + /** The total number of bytes this EP will transfer every + * service interval. valid only for periodic EPs. */ + uint16_t wBytesPerInterval; +}; + +/** \ingroup desc + * A generic representation of a BOS Device Capability descriptor. It is + * advised to check bDevCapabilityType and call the matching + * libusb_get_*_descriptor function to get a structure fully matching the type. + */ +struct libusb_bos_dev_capability_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + /** Device Capability type */ + uint8_t bDevCapabilityType; + /** Device Capability data (bLength - 3 bytes) */ + uint8_t dev_capability_data +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; +}; + +/** \ingroup desc + * A structure representing the Binary Device Object Store (BOS) descriptor. + * This descriptor is documented in section 9.6.2 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_bos_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_BOS LIBUSB_DT_BOS + * in this context. */ + uint8_t bDescriptorType; + + /** Length of this descriptor and all of its sub descriptors */ + uint16_t wTotalLength; + + /** The number of separate device capability descriptors in + * the BOS */ + uint8_t bNumDeviceCaps; + + /** bNumDeviceCap Device Capability Descriptors */ + struct libusb_bos_dev_capability_descriptor *dev_capability +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; +}; + +/** \ingroup desc + * A structure representing the USB 2.0 Extension descriptor + * This descriptor is documented in section 9.6.2.1 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_usb_2_0_extension_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION + * LIBUSB_BT_USB_2_0_EXTENSION in this context. */ + uint8_t bDevCapabilityType; + + /** Bitmap encoding of supported device level features. + * A value of one in a bit location indicates a feature is + * supported; a value of zero indicates it is not supported. + * See \ref libusb_usb_2_0_extension_attributes. */ + uint32_t bmAttributes; +}; + +/** \ingroup desc + * A structure representing the SuperSpeed USB Device Capability descriptor + * This descriptor is documented in section 9.6.2.2 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_ss_usb_device_capability_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY + * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY in this context. */ + uint8_t bDevCapabilityType; + + /** Bitmap encoding of supported device level features. + * A value of one in a bit location indicates a feature is + * supported; a value of zero indicates it is not supported. + * See \ref libusb_ss_usb_device_capability_attributes. */ + uint8_t bmAttributes; + + /** Bitmap encoding of the speed supported by this device when + * operating in SuperSpeed mode. See \ref libusb_supported_speed. */ + uint16_t wSpeedSupported; + + /** The lowest speed at which all the functionality supported + * by the device is available to the user. For example if the + * device supports all its functionality when connected at + * full speed and above then it sets this value to 1. */ + uint8_t bFunctionalitySupport; + + /** U1 Device Exit Latency. */ + uint8_t bU1DevExitLat; + + /** U2 Device Exit Latency. */ + uint16_t bU2DevExitLat; +}; + +/** \ingroup desc + * A structure representing the Container ID descriptor. + * This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification. + * All multiple-byte fields, except UUIDs, are represented in host-endian format. + */ +struct libusb_container_id_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID + * LIBUSB_BT_CONTAINER_ID in this context. */ + uint8_t bDevCapabilityType; + + /** Reserved field */ + uint8_t bReserved; + + /** 128 bit UUID */ + uint8_t ContainerID[16]; +}; + /** \ingroup asyncio * Setup packet for control transfers. */ struct libusb_control_setup { @@ -632,14 +895,15 @@ struct libusb_control_setup { #define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) -/* libusb */ +/* libusbx */ struct libusb_context; struct libusb_device; 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. */ @@ -651,27 +915,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. @@ -688,8 +952,8 @@ typedef struct libusb_context libusb_context; * Certain operations can be performed on a device, but in order to do any * I/O you will have to first obtain a device handle using libusb_open(). * - * Devices are reference counted with libusb_device_ref() and - * libusb_device_unref(), and are freed when the reference count reaches 0. + * Devices are reference counted with libusb_ref_device() and + * libusb_unref_device(), and are freed when the reference count reaches 0. * New devices presented by libusb_get_device_list() have a reference count of * 1, and libusb_free_device_list() can optionally decrease the reference count * on all devices in the list. libusb_open() adds another reference which is @@ -712,27 +976,83 @@ 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 full speed (12MBit/s). */ + LIBUSB_SPEED_FULL = 2, + + /** The device is operating at high speed (480MBit/s). */ + LIBUSB_SPEED_HIGH = 3, + + /** The device is operating at super speed (5000MBit/s). */ + LIBUSB_SPEED_SUPER = 4, +}; + +/** \ingroup dev + * Supported speeds (wSpeedSupported) bitfield. Indicates what + * speeds the device supports. + */ +enum libusb_supported_speed { + /** Low speed operation supported (1.5MBit/s). */ + LIBUSB_LOW_SPEED_OPERATION = 1, + + /** Full speed operation supported (12MBit/s). */ + LIBUSB_FULL_SPEED_OPERATION = 2, + + /** High speed operation supported (480MBit/s). */ + LIBUSB_HIGH_SPEED_OPERATION = 4, + + /** Superspeed operation supported (5000MBit/s). */ + LIBUSB_SUPER_SPEED_OPERATION = 8, +}; + +/** \ingroup dev + * Masks for the bits of the + * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field + * of the USB 2.0 Extension descriptor. + */ +enum libusb_usb_2_0_extension_attributes { + /** Supports Link Power Management (LPM) */ + LIBUSB_BM_LPM_SUPPORT = 2, +}; + +/** \ingroup dev + * Masks for the bits of the + * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field + * field of the SuperSpeed USB Device Capability descriptor. + */ +enum libusb_ss_usb_device_capability_attributes { + /** Supports Latency Tolerance Messages (LTM) */ + LIBUSB_BM_LTM_SUPPORT = 2, +}; - /** The device is operating at low speed (1.5MBit/s). */ - LIBUSB_SPEED_LOW = 1, +/** \ingroup dev + * USB capability types + */ +enum libusb_bos_type { + /** Wireless USB device capability */ + LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 1, - /** The device is operating at full speed (12MBit/s). */ - LIBUSB_SPEED_FULL = 2, + /** USB 2.0 extensions */ + LIBUSB_BT_USB_2_0_EXTENSION = 2, - /** The device is operating at high speed (480MBit/s). */ - LIBUSB_SPEED_HIGH = 3, + /** SuperSpeed USB device capability */ + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 3, - /** The device is operating at super speed (5000MBit/s). */ - LIBUSB_SPEED_SUPER = 4, + /** 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. + * 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) */ @@ -774,13 +1094,16 @@ enum libusb_error { /** Operation not supported or unimplemented on this platform */ LIBUSB_ERROR_NOT_SUPPORTED = -12, - /* NB! Remember to update libusb_error_name() - when adding new error codes here. */ + /* 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 { @@ -806,6 +1129,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 @@ -868,7 +1194,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. @@ -881,12 +1207,6 @@ typedef void (LIBUSB_CALL *libusb_transfer_cb_fn)(struct libusb_transfer *transf * completed, the library populates the transfer with the results and passes * it back to the user. */ - -#if defined(OS_WIN) -#pragma warning(push) -#pragma warning(disable:4200) -#endif // defined(OS_WIN) - struct libusb_transfer { /** Handle of the device that this transfer will be submitted to */ libusb_device_handle *dev_handle; @@ -945,18 +1265,42 @@ struct libusb_transfer { ; }; -#if defined(OS_WIN) -#pragma warning(pop) -#endif // defined(OS_WIN) - /** \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, + 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); @@ -965,6 +1309,8 @@ void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); const struct libusb_version * LIBUSB_CALL libusb_get_version(void); int LIBUSB_CALL libusb_has_capability(uint32_t capability); const char * LIBUSB_CALL libusb_error_name(int errcode); +int LIBUSB_CALL libusb_setlocale(const char *locale); +const char * LIBUSB_CALL libusb_strerror(enum libusb_error errcode); ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, libusb_device ***list); @@ -985,7 +1331,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, @@ -1019,6 +1396,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 */ @@ -1136,8 +1515,8 @@ static inline void libusb_fill_control_transfer( 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; } @@ -1272,7 +1651,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; @@ -1312,12 +1691,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 */ @@ -1350,8 +1729,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 @@ -1446,8 +1825,127 @@ void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx, libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, void *user_data); +/** \ingroup hotplug + * Callback handle. + * + * Callbacks handles are generated by libusb_hotplug_register_callback() + * and can be used to deregister callbacks. Callback handles are unique + * per libusb_context and it is safe to call libusb_hotplug_deregister_callback() + * on an already deregisted callback. + * + * Since version 1.0.16, \ref 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. */ + LIBUSB_HOTPLUG_ENUMERATE = 1, +} 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 */ + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = 0x01, + + /** A device has left and is no longer available. + * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device. + * It is safe to call libusb_get_device_descriptor on a device that has left */ + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = 0x02, +} libusb_hotplug_event; + +/** \ingroup hotplug + * Wildcard matching for hotplug events */ +#define LIBUSB_HOTPLUG_MATCH_ANY -1 + +/** \ingroup hotplug + * Hotplug callback function type. When requesting hotplug event notifications, + * you pass a pointer to a callback function of this type. + * + * This callback may be called by an internal event thread and as such it is + * recommended the callback do minimal processing before returning. + * + * 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. + * + * Since version 1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102 + * + * \param libusb_context context of this notification + * \param device libusb_device this event occurred on + * \param event event that occurred + * \param user_data user data provided when this callback was registered + * \returns bool whether this callback is finished processing events. + * returning 1 will cause this callback to be deregistered + */ +typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx, + libusb_device *device, + libusb_hotplug_event event, + void *user_data); + +/** \ingroup hotplug + * Register a hotplug callback function + * + * Register a callback with the libusb_context. The callback will fire + * when a matching event occurs on a matching device. The callback is + * armed until either it is deregistered with libusb_hotplug_deregister_callback() + * or the supplied callback returns 1 to indicate it is finished processing events. + * + * 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 + * \param[in] flags hotplug callback flags. See \ref libusb_hotplug_flag + * \param[in] vendor_id the vendor id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] product_id the product id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] dev_class the device class to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] cb_fn the function to be invoked on a matching event/device + * \param[in] user_data user data to pass to the callback function + * \param[out] handle pointer to store the handle of the allocated callback (can be NULL) + * \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure + */ +int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx, + libusb_hotplug_event events, + libusb_hotplug_flag flags, + int vendor_id, int product_id, + int dev_class, + libusb_hotplug_callback_fn cb_fn, + void *user_data, + libusb_hotplug_callback_handle *handle); + +/** \ingroup hotplug + * Deregisters a hotplug callback. + * + * Deregister a callback from a libusb_context. This function is safe to call from within + * a hotplug callback. + * + * Since version 1.0.16, \ref 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); + #ifdef __cplusplus } #endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + #endif diff --git a/third_party/libusb/src/libusb/libusbi.h b/third_party/libusb/src/libusb/libusbi.h index 976be0d..02efae30 100644 --- a/third_party/libusb/src/libusb/libusbi.h +++ b/third_party/libusb/src/libusb/libusbi.h @@ -1,7 +1,7 @@ /* - * Internal header for libusb - * Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org> - * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * Internal header for libusbx + * Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org> + * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com> * * 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,7 @@ #ifndef LIBUSBI_H #define LIBUSBI_H -#include <config.h> +#include "config.h" #include <stddef.h> #include <stdint.h> @@ -31,14 +31,17 @@ #include <poll.h> #endif -#include <libusb.h> -#include <version.h> +#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,23 +52,39 @@ #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); \ @@ -108,6 +127,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 +147,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 +208,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 <os/threads_posix.h> -#elif defined(OS_WINDOWS) +#include "os/threads_posix.h" +#elif defined(OS_WINDOWS) || defined(OS_WINCE) #include <os/threads_windows.h> #endif -#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD) -#include <unistd.h> -#include <os/poll_posix.h> -#elif defined(OS_WINDOWS) -#include <os/poll_windows.h> -#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 { @@ -265,6 +233,11 @@ struct libusb_context { struct list_head open_devs; usbi_mutex_t open_devs_lock; + /* A list of registered hotplug callbacks */ + struct list_head hotplug_cbs; + usbi_mutex_t hotplug_cbs_lock; + int hotplug_pipe[2]; + /* this is a list of in-flight transfer handles, sorted by timeout * expiration. URBs to timeout the soonest are placed at the beginning of * the list, URBs that will time out later are placed after, and urbs with @@ -302,6 +275,8 @@ struct libusb_context { * this timerfd is maintained to trigger on the next pending timeout */ int timerfd; #endif + + struct list_head list; }; #ifdef USBI_TIMERFD_AVAILABLE @@ -319,13 +294,25 @@ 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; struct list_head list; unsigned long session_data; - unsigned char os_priv[0]; + + struct libusb_device_descriptor device_descriptor; + int attached; + + unsigned char os_priv +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; }; struct libusb_device_handle { @@ -335,7 +322,14 @@ struct libusb_device_handle { struct list_head list; struct libusb_device *dev; - unsigned char os_priv[0]; + 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 { @@ -385,6 +379,9 @@ 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 have been updated */ + USBI_TRANSFER_UPDATED_FDS = 1 << 4, }; #define USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \ @@ -426,12 +423,35 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, enum libusb_transfer_status status); int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer); -int usbi_parse_descriptor(unsigned char *source, const char *descriptor, +int usbi_parse_descriptor(const unsigned char *source, const char *descriptor, void *dest, int host_endian); +int usbi_device_cache_descriptor(libusb_device *dev); int usbi_get_config_index_by_value(struct libusb_device *dev, uint8_t bConfigurationValue, int *idx); -/* polling */ +void usbi_connect_device (struct libusb_device *dev); +void usbi_disconnect_device (struct libusb_device *dev); + +/* Internal abstraction for poll (needs struct usbi_transfer on Windows) */ +#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD) +#include <unistd.h> +#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 */ @@ -454,7 +474,13 @@ void usbi_fd_notification(struct libusb_context *ctx); struct discovered_devs { size_t len; size_t capacity; - struct libusb_device *devices[0]; + struct libusb_device *devices +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; }; struct discovered_devs *discovered_devs_append( @@ -468,11 +494,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. @@ -502,7 +531,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. * @@ -533,18 +562,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. * @@ -568,7 +616,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. @@ -647,13 +695,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: @@ -830,6 +894,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 @@ -850,7 +916,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. @@ -930,6 +996,9 @@ extern const struct usbi_os_backend linux_usbfs_backend; extern const struct usbi_os_backend darwin_backend; extern const struct usbi_os_backend openbsd_backend; extern const struct usbi_os_backend windows_backend; +extern const struct usbi_os_backend wince_backend; -#endif +extern struct list_head active_contexts_list; +extern usbi_mutex_static_t active_contexts_lock; +#endif diff --git a/third_party/libusb/src/libusb/os/darwin_usb.c b/third_party/libusb/src/libusb/os/darwin_usb.c index 8834d0f..a24558c 100644 --- a/third_party/libusb/src/libusb/os/darwin_usb.c +++ b/third_party/libusb/src/libusb/os/darwin_usb.c @@ -1,6 +1,7 @@ +/* -*- Mode: C; indent-tabs-mode:nil -*- */ /* - * darwin backend for libusb 1.0 - * Copyright (C) 2008-2011 Nathan Hjelm <hjelmn@users.sourceforge.net> + * darwin backend for libusbx 1.0 + * Copyright © 2008-2013 Nathan Hjelm <hjelmn@users.sourceforge.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,19 +18,16 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <config.h> +#include "config.h" #include <ctype.h> -#include <dirent.h> #include <errno.h> -#include <fcntl.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/ioctl.h> -#include <sys/stat.h> #include <sys/types.h> #include <unistd.h> +#include <fcntl.h> #include <libkern/OSAtomic.h> #include <mach/clock.h> @@ -42,22 +40,23 @@ #include <objc/objc-auto.h> #endif -#include <IOKit/IOCFBundle.h> -#include <IOKit/usb/IOUSBLib.h> -#include <IOKit/IOCFPlugIn.h> - #include "darwin_usb.h" /* async event thread */ -static pthread_mutex_t libusb_darwin_at_mutex; -static pthread_cond_t libusb_darwin_at_cond; +static pthread_mutex_t libusb_darwin_at_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER; static clock_serv_t clock_realtime; static clock_serv_t clock_monotonic; -static CFRunLoopRef libusb_darwin_acfl = NULL; /* async cf loop */ +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; @@ -67,6 +66,10 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, int static int darwin_reset_device(struct libusb_device_handle *dev_handle); static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0); +static int darwin_scan_devices(struct libusb_context *ctx); +static int process_new_device (struct libusb_context *ctx, io_service_t service); + +#if defined(ENABLE_LOGGING) static const char *darwin_error_str (int result) { switch (result) { case kIOReturnSuccess: @@ -95,10 +98,15 @@ static const char *darwin_error_str (int result) { return "data overrun"; case kIOReturnCannotWire: return "physical memory can not be wired down"; + case kIOReturnNoResources: + return "out of resources"; + case kIOUSBHighSpeedSplitError: + return "high speed split error"; default: return "unknown error"; } } +#endif static int darwin_to_libusb (int result) { switch (result) { @@ -125,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; @@ -134,19 +157,19 @@ 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]; if (dev_handle->claimed_interfaces & (1 << iface)) { for (i = 0 ; i < cInterface->num_endpoints ; i++) { - 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); - return 0; - } + if (cInterface->endpoint_addrs[i] == ep) { + *pipep = i + 1; + *ifcp = iface; + usbi_dbg ("pipe %d on interface %d matches", *pipep, *ifcp); + return 0; + } } } } @@ -157,7 +180,7 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui return -1; } -static int usb_setup_device_iterator (io_iterator_t *deviceIterator, long location) { +static int usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 location) { CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName); if (!matchingDict) @@ -169,7 +192,9 @@ static int usb_setup_device_iterator (io_iterator_t *deviceIterator, long locati &kCFTypeDictionaryValueCallBacks); if (propertyMatchDict) { - CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberLongType, &location); + /* there are no unsigned CFNumber types so treat the value as signed. the os seems to do this + internally (CFNumberType of locationID is 3) */ + CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location); CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF); /* release our reference to the CFNumber (CFDictionarySetValue retains it) */ @@ -185,118 +210,92 @@ static int usb_setup_device_iterator (io_iterator_t *deviceIterator, long locati 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; +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); - - (*plugInInterface)->Stop(plugInInterface); - IODestroyPlugInInterface (plugInInterface); - - /* get the location from the device */ - if (locationp) - (*(device))->GetLocationID(device, locationp); + (LPVOID)&device); + /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */ + (*plugInInterface)->Release (plugInInterface); return device; } -static kern_return_t darwin_get_device (uint32_t dev_location, usb_device_t ***darwin_device) { - kern_return_t kresult; - UInt32 location; - io_iterator_t deviceIterator; +static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) { + struct libusb_context *ctx; + io_service_t service; - kresult = usb_setup_device_iterator (&deviceIterator, dev_location); - if (kresult) - return kresult; + usbi_mutex_lock(&active_contexts_lock); - /* This port of libusb uses locations to keep track of devices. */ - while ((*darwin_device = usb_get_next_device (deviceIterator, &location)) != NULL) { - if (location == dev_location) - break; + while ((service = IOIteratorNext(add_devices))) { + /* add this device to each active context's device list */ + list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { + process_new_device (ctx, service);; + } - (**darwin_device)->Release(*darwin_device); + IOObjectRelease(service); } - IOObjectRelease (deviceIterator); - - if (!(*darwin_device)) - return kIOReturnNoDevice; - - return kIOReturnSuccess; + usbi_mutex_unlock(&active_contexts_lock); } - - static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { - struct libusb_context *ctx = (struct libusb_context *)ptr; - struct libusb_device_handle *handle; - struct darwin_device_priv *dpriv; - struct darwin_device_handle_priv *priv; + struct libusb_device *dev = NULL; + struct libusb_context *ctx; io_service_t device; - long location; - bool locationValid; - CFTypeRef locationCF; - UInt32 message; - - usbi_info (ctx, "a device has been detached"); + 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) + if (!ret) continue; - locationValid = CFGetTypeID(locationCF) == CFNumberGetTypeID() && - CFNumberGetValue(locationCF, kCFNumberLongType, &location); - - CFRelease (locationCF); - - if (!locationValid) - continue; + usbi_mutex_lock(&active_contexts_lock); - usbi_mutex_lock(&ctx->open_devs_lock); - list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) { - dpriv = (struct darwin_device_priv *)handle->dev->os_priv; + list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { + usbi_dbg ("notifying context %p of device disconnect", ctx); - /* the device may have been opened several times. write to each handle's event descriptor */ - if (dpriv->location == location && handle->os_priv) { - priv = (struct darwin_device_handle_priv *)handle->os_priv; - - message = MESSAGE_DEVICE_GONE; - write (priv->fds[1], &message, sizeof (message)); + dev = usbi_get_device_by_session_id(ctx, 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); } } - usbi_mutex_unlock(&ctx->open_devs_lock); + usbi_mutex_unlock(&active_contexts_lock); } } @@ -307,7 +306,7 @@ static void darwin_clear_iterator (io_iterator_t iter) { IOObjectRelease (device); } -static void *event_thread_main (void *arg0) { +static void *darwin_event_thread_main (void *arg0) { IOReturn kresult; struct libusb_context *ctx = (struct libusb_context *)arg0; CFRunLoopRef runloop; @@ -315,23 +314,22 @@ static void *event_thread_main (void *arg0) { /* Set this thread's name, so it can be seen in the debugger and crash reports. */ #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 - pthread_setname_np ("org.libusb.device-detach"); -#endif + pthread_setname_np ("org.libusb.device-hotplug"); /* Tell the Objective-C garbage collector about this thread. This is required because, unlike NSThreads, pthreads are not automatically registered. Although we don't use Objective-C, we use CoreFoundation, which does. */ -#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 objc_registerThreadWithCollector(); #endif - /* hotplug (device removal) source */ + /* hotplug (device arrival/removal) sources */ CFRunLoopSourceRef libusb_notification_cfsource; io_notification_port_t libusb_notification_port; io_iterator_t libusb_rem_device_iterator; + io_iterator_t libusb_add_device_iterator; - usbi_info (ctx, "creating hotplug event source"); + usbi_dbg ("creating hotplug event source"); runloop = CFRunLoopGetCurrent (); CFRetain (runloop); @@ -343,9 +341,21 @@ static void *event_thread_main (void *arg0) { /* create notifications for removed devices */ kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification, - IOServiceMatching(kIOUSBDeviceClassName), - (IOServiceMatchingCallback)darwin_devices_detached, - (void *)ctx, &libusb_rem_device_iterator); + IOServiceMatching(kIOUSBDeviceClassName), + (IOServiceMatchingCallback)darwin_devices_detached, + (void *)ctx, &libusb_rem_device_iterator); + + if (kresult != kIOReturnSuccess) { + usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); + + pthread_exit (NULL); + } + + /* create notifications for attached devices */ + kresult = IOServiceAddMatchingNotification(libusb_notification_port, kIOFirstMatchNotification, + IOServiceMatching(kIOUSBDeviceClassName), + (IOServiceMatchingCallback)darwin_devices_attached, + (void *)ctx, &libusb_add_device_iterator); if (kresult != kIOReturnSuccess) { usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); @@ -355,10 +365,11 @@ static void *event_thread_main (void *arg0) { /* arm notifiers */ darwin_clear_iterator (libusb_rem_device_iterator); + darwin_clear_iterator (libusb_add_device_iterator); - usbi_info (ctx, "thread ready to receive events"); + usbi_dbg ("darwin event thread ready to receive events"); - /* signal the main thread that the async runloop has been created. */ + /* signal the main thread that the hotplug runloop has been created. */ pthread_mutex_lock (&libusb_darwin_at_mutex); libusb_darwin_acfl = runloop; pthread_cond_signal (&libusb_darwin_at_cond); @@ -367,11 +378,17 @@ static void *event_thread_main (void *arg0) { /* run the runloop */ CFRunLoopRun(); - usbi_info (ctx, "thread exiting"); + usbi_dbg ("darwin event thread exiting"); + + /* remove the notification cfsource */ + CFRunLoopRemoveSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode); /* delete notification port */ IONotificationPortDestroy (libusb_notification_port); + + /* delete iterators */ IOObjectRelease (libusb_rem_device_iterator); + IOObjectRelease (libusb_add_device_iterator); CFRelease (runloop); @@ -380,21 +397,40 @@ static void *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); + if (LIBUSB_SUCCESS != rc) { + return rc; + } 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); mach_port_deallocate(mach_task_self(), host_self); - pthread_mutex_init (&libusb_darwin_at_mutex, NULL); - pthread_cond_init (&libusb_darwin_at_cond, NULL); - - pthread_create (&libusb_darwin_at, NULL, event_thread_main, (void *)ctx); + pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, (void *)ctx); pthread_mutex_lock (&libusb_darwin_at_mutex); while (!libusb_darwin_acfl) @@ -402,7 +438,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) { @@ -410,14 +446,14 @@ static void darwin_exit (void) { mach_port_deallocate(mach_task_self(), clock_realtime); mach_port_deallocate(mach_task_self(), clock_monotonic); - /* stop the async runloop and wait for the thread to terminate. */ + /* stop the event runloop and wait for the thread to terminate. */ CFRunLoopStop (libusb_darwin_acfl); pthread_join (libusb_darwin_at, NULL); } } static int darwin_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) { - struct darwin_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); @@ -428,7 +464,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; @@ -446,15 +482,15 @@ 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) - return LIBUSB_ERROR_INVALID_PARAM; + return LIBUSB_ERROR_NOT_FOUND; config_index = get_configuration_index (dev, priv->active_config); if (config_index < 0) @@ -464,27 +500,15 @@ 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; - usb_device_t **device = NULL; + int ret; - if (!priv) + if (!priv || !priv->device) return LIBUSB_ERROR_OTHER; - if (!priv->device) { - kresult = darwin_get_device (priv->location, &device); - if (kresult || !device) { - usbi_err (DEVICE_CTX (dev), "could not find device: %s", darwin_error_str (kresult)); - - return darwin_to_libusb (kresult); - } - - /* don't have to open the device to get a config descriptor */ - } else - device = priv->device; - - kresult = (*device)->GetConfigurationDescriptorPtr (device, config_index, &desc); + kresult = (*priv->device)->GetConfigurationDescriptorPtr (priv->device, config_index, &desc); if (kresult == kIOReturnSuccess) { /* copy descriptor */ if (libusb_le16_to_cpu(desc->wTotalLength) < len) @@ -496,15 +520,16 @@ static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t confi *host_endian = 0; } - if (!priv->device) - (*device)->Release (device); + ret = darwin_to_libusb (kresult); + if (ret != LIBUSB_SUCCESS) + return ret; - return darwin_to_libusb (kresult); + 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; @@ -512,14 +537,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 @@ -545,72 +570,83 @@ 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_cache_device_descriptor (struct libusb_context *ctx, struct libusb_device *dev, usb_device_t **device) { - struct darwin_device_priv *priv; - int retries = 5, delay = 30000; +static int darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) { + IOUSBDevRequestTO req; + + memset (buffer, 0, buffer_size); + + /* Set up request for descriptor/ */ + req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); + req.bRequest = kUSBRqGetDescriptor; + req.wValue = desc << 8; + req.wIndex = desc_index; + req.wLength = buffer_size; + req.pData = buffer; + req.noDataTimeout = 20; + req.completionTimeout = 100; + + return (*device)->DeviceRequestTO (device, &req); +} + +static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct darwin_cached_device *dev) { + usb_device_t **device = dev->device; + int retries = 1, delay = 30000; int unsuspended = 0, try_unsuspend = 1, try_reconfigure = 1; int is_open = 0; int ret = 0, ret2; - IOUSBDevRequest req; 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 { - /* Set up request for device descriptor */ - memset (&(priv->dev_descriptor), 0, sizeof(IOUSBDeviceDescriptor)); - req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); - req.bRequest = kUSBRqGetDescriptor; - req.wValue = kUSBDeviceDesc << 8; - req.wIndex = 0; - req.wLength = sizeof(priv->dev_descriptor); - req.pData = &(priv->dev_descriptor); - - /* according to Apple's documentation the device must be open for DeviceRequest but we may not be able to open some - * devices and Apple's USB Prober doesn't bother to open the device before issuing a descriptor request. Still, - * to follow the spec as closely as possible, try opening the device */ - - ret = (*(device))->DeviceRequest (device, &req); - - if (kIOReturnOverrun == ret && kUSBDeviceDesc == priv->dev_descriptor.bDescriptorType) + /**** retrieve device descriptor ****/ + ret = darwin_request_descriptor (device, kUSBDeviceDesc, 0, &dev->dev_descriptor, sizeof(dev->dev_descriptor)); + + if (kIOReturnOverrun == ret && kUSBDeviceDesc == dev->dev_descriptor.bDescriptorType) /* received an overrun error but we still received a device descriptor */ ret = kIOReturnSuccess; - if (kIOReturnSuccess == ret && (0 == priv->dev_descriptor.bNumConfigurations || - 0 == priv->dev_descriptor.bcdUSB)) { + if (kIOUSBVendorIDAppleComputer == idVendor) { + /* NTH: don't bother retrying or unsuspending Apple devices */ + break; + } + + if (kIOReturnSuccess == ret && (0 == dev->dev_descriptor.bNumConfigurations || + 0 == dev->dev_descriptor.bcdUSB)) { /* work around for incorrectly configured devices */ if (try_reconfigure && is_open) { - usbi_dbg("descriptor appears to be invalid. resetting configuration before trying again..."); + usbi_dbg("descriptor appears to be invalid. resetting configuration before trying again..."); - /* set the first configuration */ - (*device)->SetConfiguration(device, 1); + /* set the first configuration */ + (*device)->SetConfiguration(device, 1); - /* don't try to reconfigure again */ - try_reconfigure = 0; + /* don't try to reconfigure again */ + try_reconfigure = 0; } ret = kIOUSBPipeStalled; @@ -619,25 +655,27 @@ 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 */ - ret2 = (*device)->USBDeviceSuspend (device, 0); - if (kIOReturnSuccess != ret2) { - /* prevent log spew from poorly behaving devices. this indicates the - os actually had trouble communicating with the device */ - usbi_dbg("could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2)); - } else - unsuspended = 1; - - try_unsuspend = 0; + /* try to unsuspend the device */ + ret2 = (*device)->USBDeviceSuspend (device, 0); + if (kIOReturnSuccess != ret2) { + /* prevent log spew from poorly behaving devices. this indicates the + os actually had trouble communicating with the device */ + usbi_dbg("could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2)); + } else + unsuspended = 1; + + try_unsuspend = 0; } } @@ -658,128 +696,223 @@ 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)); - + usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device", + idVendor, idProduct, darwin_error_str (ret), ret); return -1; } - usbi_dbg ("device descriptor:"); - usbi_dbg (" bDescriptorType: 0x%02x", priv->dev_descriptor.bDescriptorType); - usbi_dbg (" bcdUSB: 0x%04x", priv->dev_descriptor.bcdUSB); - usbi_dbg (" bDeviceClass: 0x%02x", priv->dev_descriptor.bDeviceClass); - usbi_dbg (" bDeviceSubClass: 0x%02x", priv->dev_descriptor.bDeviceSubClass); - usbi_dbg (" bDeviceProtocol: 0x%02x", priv->dev_descriptor.bDeviceProtocol); - usbi_dbg (" bMaxPacketSize0: 0x%02x", priv->dev_descriptor.bMaxPacketSize0); - usbi_dbg (" idVendor: 0x%04x", priv->dev_descriptor.idVendor); - usbi_dbg (" idProduct: 0x%04x", priv->dev_descriptor.idProduct); - usbi_dbg (" bcdDevice: 0x%04x", priv->dev_descriptor.bcdDevice); - usbi_dbg (" iManufacturer: 0x%02x", priv->dev_descriptor.iManufacturer); - usbi_dbg (" iProduct: 0x%02x", priv->dev_descriptor.iProduct); - usbi_dbg (" iSerialNumber: 0x%02x", priv->dev_descriptor.iSerialNumber); - usbi_dbg (" bNumConfigurations: 0x%02x", priv->dev_descriptor.bNumConfigurations); - /* catch buggy hubs (which appear to be virtual). Apple's own USB prober has problems with these devices. */ - if (libusb_le16_to_cpu (priv->dev_descriptor.idProduct) != idProduct) { + 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)); + idProduct, libusb_le16_to_cpu (dev->dev_descriptor.idProduct)); return -1; } + 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 0; } -static int process_new_device (struct libusb_context *ctx, usb_device_t **device, UInt32 locationID, struct discovered_devs **_discdevs) { - struct darwin_device_priv *priv; - struct libusb_device *dev; - struct discovered_devs *discdevs; - UInt16 address; - UInt8 devSpeed; - int ret = 0, need_unref = 0; +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, parent_sessionID; + int ret = LIBUSB_SUCCESS; + usb_device_t **device; + io_service_t parent; + kern_return_t result; + UInt8 port = 0; + + *cached_out = NULL; + + /* 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 { - dev = usbi_get_device_by_session_id(ctx, locationID); - if (!dev) { - usbi_info (ctx, "allocating new device for location 0x%08x", locationID); - dev = usbi_alloc_device(ctx, locationID); - need_unref = 1; - } else - usbi_info (ctx, "using existing device for location 0x%08x", locationID); + 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 (!dev) { + if (*cached_out) + break; + + usbi_dbg("caching new device with sessionID 0x%x\n", sessionID); + + new_device = calloc (1, sizeof (*new_device)); + if (!new_device) { ret = LIBUSB_ERROR_NO_MEM; break; } - priv = (struct darwin_device_priv *)dev->os_priv; + device = darwin_device_from_service (service); + if (!device) { + ret = LIBUSB_ERROR_NO_DEVICE; + free (new_device); + new_device = NULL; + break; + } - (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&address); + /* add this device to the cached device list */ + list_add(&new_device->list, &darwin_cached_devices); - ret = darwin_cache_device_descriptor (ctx, dev, device); - if (ret < 0) + (*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; - /* 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) + if (new_device->can_enumerate) { + snprintf(new_device->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", new_device->address, + new_device->dev_descriptor.idVendor, new_device->dev_descriptor.idProduct, + new_device->dev_descriptor.bDeviceClass, new_device->dev_descriptor.bDeviceSubClass); + } + } while (0); + + usbi_mutex_unlock(&darwin_cached_devices_lock); + + /* keep track of devices regardless of if we successfully enumerate them to + prevent them from being enumerated multiple times */ + + *cached_out = new_device; + + return ret; +} + +static int process_new_device (struct libusb_context *ctx, io_service_t service) { + struct darwin_device_priv *priv; + struct libusb_device *dev = NULL; + struct darwin_cached_device *cached_device; + UInt8 devSpeed; + int ret = 0; + + do { + ret = darwin_get_cached_device (ctx, service, &cached_device); + + if (ret < 0 || (cached_device && !cached_device->can_enumerate)) { + return ret; + } + + /* check current active configuration (and cache the first configuration value-- + which may be used by claim_interface) */ + ret = darwin_check_configuration (ctx, cached_device); + if (ret) break; - dev->bus_number = locationID >> 24; - dev->device_address = address; + usbi_dbg ("allocating new device in context %p for with session 0x%08x", + ctx, cached_device->session); - (*device)->GetDeviceSpeed (device, &devSpeed); + dev = usbi_alloc_device(ctx, cached_device->session); + if (!dev) { + return LIBUSB_ERROR_NO_MEM; + } + + priv = (struct darwin_device_priv *)dev->os_priv; + + priv->dev = cached_device; + darwin_ref_cached_device (priv->dev); + + if (cached_device->parent_session > 0) { + dev->parent_dev = usbi_get_device_by_session_id (ctx, 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; + + /* need to add a reference to the parent device */ + if (dev->parent_dev) { + libusb_ref_device(dev->parent_dev); + } + + (*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed); switch (devSpeed) { case kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break; case kUSBDeviceSpeedFull: dev->speed = LIBUSB_SPEED_FULL; break; case kUSBDeviceSpeedHigh: dev->speed = LIBUSB_SPEED_HIGH; break; +#if DeviceVersion >= 500 + case kUSBDeviceSpeedSuper: dev->speed = LIBUSB_SPEED_SUPER; break; +#endif default: usbi_warn (ctx, "Got unknown device speed %d", devSpeed); } - /* 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; - /* append the device to the list of discovered devices */ - discdevs = discovered_devs_append(*_discdevs, dev); - if (!discdevs) { - ret = LIBUSB_ERROR_NO_MEM; - break; - } - - *_discdevs = discdevs; - - 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 (need_unref) - libusb_unref_device(dev); + if (0 == ret) { + usbi_connect_device (dev); + } else { + libusb_unref_device (dev); + } return ret; } -static int darwin_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) { - io_iterator_t deviceIterator; - usb_device_t **device; - kern_return_t kresult; - UInt32 location; +static int darwin_scan_devices(struct libusb_context *ctx) { + io_iterator_t deviceIterator; + io_service_t service; + kern_return_t kresult; kresult = usb_setup_device_iterator (&deviceIterator, 0); if (kresult != kIOReturnSuccess) return darwin_to_libusb (kresult); - while ((device = usb_get_next_device (deviceIterator, &location)) != NULL) { - (void) process_new_device (ctx, device, location, _discdevs); + while ((service = IOIteratorNext (deviceIterator))) { + (void) process_new_device (ctx, service); - (*(device))->Release(device); + IOObjectRelease(service); } IOObjectRelease(deviceIterator); @@ -789,56 +922,43 @@ static int darwin_get_device_list(struct libusb_context *ctx, struct discovered_ 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; - usb_device_t **darwin_device; + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); IOReturn kresult; if (0 == dpriv->open_count) { - kresult = darwin_get_device (dpriv->location, &darwin_device); - if (kresult) { - usbi_err (HANDLE_CTX (dev_handle), "could not find device: %s", darwin_error_str (kresult)); - return darwin_to_libusb (kresult); - } - - dpriv->device = darwin_device; - /* 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)); - - switch (kresult) { - case kIOReturnExclusiveAccess: - /* it is possible to perform some actions on a device that is not open so do not return an error */ - priv->is_open = 0; - - break; - default: - (*(dpriv->device))->Release (dpriv->device); - dpriv->device = NULL; - return darwin_to_libusb (kresult); + usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceOpen: %s", darwin_error_str(kresult)); + + if (kIOReturnExclusiveAccess != kresult) { + return darwin_to_libusb (kresult); } + + /* it is possible to perform some actions on a device that is not open so do not return an error */ + priv->is_open = 0; } else { - /* create async event source */ - kresult = (*(dpriv->device))->CreateDeviceAsyncEventSource (dpriv->device, &priv->cfSource); - if (kresult != kIOReturnSuccess) { - usbi_err (HANDLE_CTX (dev_handle), "CreateDeviceAsyncEventSource: %s", darwin_error_str(kresult)); + priv->is_open = 1; + } - (*(dpriv->device))->USBDeviceClose (dpriv->device); - (*(dpriv->device))->Release (dpriv->device); + /* create async event source */ + kresult = (*(dpriv->device))->CreateDeviceAsyncEventSource (dpriv->device, &priv->cfSource); + if (kresult != kIOReturnSuccess) { + usbi_err (HANDLE_CTX (dev_handle), "CreateDeviceAsyncEventSource: %s", darwin_error_str(kresult)); - dpriv->device = NULL; - return darwin_to_libusb (kresult); + if (priv->is_open) { + (*(dpriv->device))->USBDeviceClose (dpriv->device); } - priv->is_open = 1; - - CFRetain (libusb_darwin_acfl); + priv->is_open = 0; - /* add the cfSource to the aync run loop */ - CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes); + return darwin_to_libusb (kresult); } + + CFRetain (libusb_darwin_acfl); + + /* add the cfSource to the aync run loop */ + CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes); } /* device opened successfully */ @@ -852,14 +972,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; @@ -877,30 +997,23 @@ static void darwin_close (struct libusb_device_handle *dev_handle) { libusb_release_interface (dev_handle, i); if (0 == dpriv->open_count) { - if (priv->is_open) { - /* delete the device's async event source */ - if (priv->cfSource) { - CFRunLoopRemoveSource (libusb_darwin_acfl, priv->cfSource, kCFRunLoopDefaultMode); - CFRelease (priv->cfSource); - } + /* delete the device's async event source */ + if (priv->cfSource) { + CFRunLoopRemoveSource (libusb_darwin_acfl, priv->cfSource, kCFRunLoopDefaultMode); + CFRelease (priv->cfSource); + priv->cfSource = NULL; + CFRelease (libusb_darwin_acfl); + } + if (priv->is_open) { /* close the device */ kresult = (*(dpriv->device))->USBDeviceClose(dpriv->device); if (kresult) { - /* Log the fact that we had a problem closing the file, however failing a - * close isn't really an error, so return success anyway */ - usbi_err (HANDLE_CTX (dev_handle), "USBDeviceClose: %s", darwin_error_str(kresult)); + /* Log the fact that we had a problem closing the file, however failing a + * close isn't really an error, so return success anyway */ + usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceClose: %s", darwin_error_str(kresult)); } } - - kresult = (*(dpriv->device))->Release(dpriv->device); - if (kresult) { - /* Log the fact that we had a problem closing the file, however failing a - * close isn't really an error, so return success anyway */ - usbi_err (HANDLE_CTX (dev_handle), "Release: %s", darwin_error_str(kresult)); - } - - dpriv->device = NULL; } /* file descriptors are maintained per-instance */ @@ -912,7 +1025,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; @@ -920,7 +1033,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; @@ -946,9 +1059,10 @@ static int darwin_set_configuration(struct libusb_device_handle *dev_handle, int static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_service_t *usbInterfacep) { IOUSBFindInterfaceRequest request; - uint8_t current_interface; kern_return_t kresult; io_iterator_t interface_iterator; + UInt8 bInterfaceNumber; + int ret; *usbInterfacep = IO_OBJECT_NULL; @@ -962,10 +1076,16 @@ static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_s if (kresult) return kresult; - for ( current_interface = 0 ; current_interface <= ifc ; current_interface++ ) { - *usbInterfacep = IOIteratorNext(interface_iterator); - if (current_interface != ifc) - (void) IOObjectRelease (*usbInterfacep); + while ((*usbInterfacep = IOIteratorNext(interface_iterator))) { + /* find the interface number */ + ret = get_ioregistry_value_number (*usbInterfacep, CFSTR("bInterfaceNumber"), kCFNumberSInt8Type, + &bInterfaceNumber); + + if (ret && bInterfaceNumber == ifc) { + break; + } + + (void) IOObjectRelease (*usbInterfacep); } /* done with the interface iterator */ @@ -987,7 +1107,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); @@ -999,7 +1119,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { /* iterate through pipe references */ for (i = 1 ; i <= numep ; i++) { kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1, - &dont_care2, &dont_care3); + &dont_care2, &dont_care3); if (kresult != kIOReturnSuccess) { usbi_err (HANDLE_CTX (dev_handle), "error getting pipe information for pipe %d: %s", i, darwin_error_str(kresult)); @@ -1007,7 +1127,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 +1138,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; @@ -1057,7 +1177,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i /* get an interface to the device's interface */ kresult = IOCreatePlugInInterfaceForService (usbInterface, kIOUSBInterfaceUserClientTypeID, - kIOCFPlugInInterfaceID, &plugInInterface, &score); + kIOCFPlugInInterfaceID, &plugInInterface, &score); /* ignore release error */ (void)IOObjectRelease (usbInterface); @@ -1074,10 +1194,11 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i /* Do the actual claim */ kresult = (*plugInInterface)->QueryInterface(plugInInterface, - CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), - (LPVOID)&cInterface->interface); + CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), + (LPVOID)&cInterface->interface); /* We no longer need the intermediate plug-in */ - IODestroyPlugInInterface (plugInInterface); + /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */ + (*plugInInterface)->Release (plugInInterface); if (kresult || !cInterface->interface) { usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); @@ -1115,7 +1236,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; } @@ -1142,11 +1263,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; @@ -1196,31 +1317,68 @@ 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; + bool reenumerate = false; IOReturn kresult; + int i; kresult = (*(dpriv->device))->ResetDevice (dpriv->device); - if (kresult) + if (kresult) { usbi_err (HANDLE_CTX (dev_handle), "ResetDevice: %s", darwin_error_str (kresult)); + return darwin_to_libusb (kresult); + } - return darwin_to_libusb (kresult); + do { + usbi_dbg ("darwin/reset_device: checking if device descriptor changed"); + + /* ignore return code. if we can't get a descriptor it might be worthwhile re-enumerating anway */ + (void) darwin_request_descriptor (dpriv->device, kUSBDeviceDesc, 0, &descriptor, sizeof (descriptor)); + + /* check if the device descriptor has changed */ + if (0 != memcmp (&dpriv->dev_descriptor, &descriptor, sizeof (descriptor))) { + reenumerate = true; + break; + } + + /* check if any configuration descriptor has changed */ + for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) { + usbi_dbg ("darwin/reset_device: checking if configuration descriptor %d changed", i); + + (void) darwin_request_descriptor (dpriv->device, kUSBConfDesc, i, &configuration, sizeof (configuration)); + (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration); + + if (!cached_configuration || 0 != memcmp (cached_configuration, &configuration, sizeof (configuration))) { + reenumerate = true; + break; + } + } + } while (0); + + if (reenumerate) { + usbi_dbg ("darwin/reset_device: device requires reenumeration"); + (void) (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg ("darwin/reset_device: device reset complete"); + + return LIBUSB_SUCCESS; } static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, int interface) { - struct darwin_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; @@ -1259,7 +1417,15 @@ static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, } static void darwin_destroy_device(struct libusb_device *dev) { - (void)dev; + struct darwin_device_priv *dpriv = (struct darwin_device_priv *) dev->os_priv; + + if (dpriv->dev) { + /* need to hold the lock in case this is the last reference to the device */ + usbi_mutex_lock(&darwin_cached_devices_lock); + darwin_deref_cached_device (dpriv->dev); + dpriv->dev = NULL; + usbi_mutex_unlock(&darwin_cached_devices_lock); + } } static int submit_bulk_transfer(struct usbi_transfer *itransfer) { @@ -1268,15 +1434,12 @@ 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; struct darwin_interface *cInterface; - if (IS_XFEROUT(transfer) && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) - return LIBUSB_ERROR_NOT_SUPPORTED; - if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface) != 0) { usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); @@ -1286,33 +1449,38 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) { cInterface = &priv->interfaces[iface]; (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number, - &transferType, &maxPacketSize, &interval); + &transferType, &maxPacketSize, &interval); + + if (0 != (transfer->length % maxPacketSize)) { + /* do not need a zero packet */ + transfer->flags &= ~LIBUSB_TRANSFER_ADD_ZERO_PACKET; + } /* submit the request */ /* timeouts are unavailable on interrupt endpoints */ if (transferType == kUSBInterrupt) { if (IS_XFERIN(transfer)) ret = (*(cInterface->interface))->ReadPipeAsync(cInterface->interface, pipeRef, transfer->buffer, - transfer->length, darwin_async_io_callback, itransfer); + transfer->length, darwin_async_io_callback, itransfer); else ret = (*(cInterface->interface))->WritePipeAsync(cInterface->interface, pipeRef, transfer->buffer, - transfer->length, darwin_async_io_callback, itransfer); + transfer->length, darwin_async_io_callback, itransfer); } else { itransfer->flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; if (IS_XFERIN(transfer)) ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer, - transfer->length, transfer->timeout, transfer->timeout, - darwin_async_io_callback, (void *)itransfer); + transfer->length, transfer->timeout, transfer->timeout, + darwin_async_io_callback, (void *)itransfer); else ret = (*(cInterface->interface))->WritePipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer, - transfer->length, transfer->timeout, transfer->timeout, - darwin_async_io_callback, (void *)itransfer); + transfer->length, transfer->timeout, transfer->timeout, + darwin_async_io_callback, (void *)itransfer); } if (ret) usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out", - darwin_error_str(ret), ret); + darwin_error_str(ret), ret); return darwin_to_libusb (ret); } @@ -1322,11 +1490,12 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv; - IOReturn kresult; - uint8_t pipeRef, iface; - UInt64 frame; - AbsoluteTime atTime; - int i; + IOReturn kresult; + uint8_t direction, number, interval, pipeRef, iface, transferType; + uint16_t maxPacketSize; + UInt64 frame; + AbsoluteTime atTime; + int i; struct darwin_interface *cInterface; @@ -1343,7 +1512,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; @@ -1356,6 +1525,10 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { cInterface = &priv->interfaces[iface]; + /* determine the properties of this endpoint and the speed of the device */ + (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number, + &transferType, &maxPacketSize, &interval); + /* Last but not least we need the bus frame number */ kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime); if (kresult) { @@ -1366,6 +1539,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; @@ -1375,18 +1551,23 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { /* submit the request */ if (IS_XFERIN(transfer)) kresult = (*(cInterface->interface))->ReadIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame, - transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, - itransfer); + transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, + itransfer); else kresult = (*(cInterface->interface))->WriteIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame, - transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, - itransfer); + transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, + itransfer); - cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets / 8; + if (LIBUSB_SPEED_FULL == transfer->dev_handle->dev->speed) + /* Full speed */ + cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1)); + else + /* High/super speed */ + cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1)) / 8; if (kresult != kIOReturnSuccess) { usbi_err (TRANSFER_CTX (transfer), "isochronous transfer failed (dir: %s): %s", IS_XFERIN(transfer) ? "In" : "Out", - darwin_error_str(kresult)); + darwin_error_str(kresult)); free (tpriv->isoc_framelist); tpriv->isoc_framelist = NULL; } @@ -1397,7 +1578,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); @@ -1412,7 +1593,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; @@ -1463,10 +1644,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; @@ -1478,7 +1659,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; @@ -1495,20 +1676,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); } @@ -1543,18 +1719,24 @@ static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) struct usbi_transfer *itransfer = (struct usbi_transfer *)refcon; struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)transfer->dev_handle->os_priv; - UInt32 message, size; + struct darwin_msg_async_io_complete message = {.itransfer = itransfer, .result = result, + .size = (UInt32) (uintptr_t) arg0}; + + usbi_dbg ("an async io operation has completed"); + + /* if requested write a zero packet */ + if (kIOReturnSuccess == result && IS_XFEROUT(transfer) && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) { + struct darwin_interface *cInterface; + uint8_t iface, pipeRef; - usbi_info (ITRANSFER_CTX (itransfer), "an async io operation has completed"); + (void) ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface); + cInterface = &priv->interfaces[iface]; - size = (UInt32) (uintptr_t) arg0; + (*(cInterface->interface))->WritePipe (cInterface->interface, pipeRef, transfer->buffer, 0); + } /* send a completion message to the device's file descriptor */ - message = MESSAGE_ASYNC_IO_COMPLETE; write (priv->fds[1], &message, sizeof (message)); - write (priv->fds[1], &itransfer, sizeof (itransfer)); - write (priv->fds[1], &result, sizeof (IOReturn)); - write (priv->fds[1], &size, sizeof (size)); } static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_t result) { @@ -1568,17 +1750,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; } } @@ -1597,17 +1779,17 @@ static void darwin_handle_callback (struct usbi_transfer *itransfer, kern_return return; } - usbi_info (ITRANSFER_CTX (itransfer), "handling %s completion with kernel status %d", - isControl ? "control" : isBulk ? "bulk" : isIsoc ? "isoc" : "interrupt", result); + usbi_dbg ("handling %s completion with kernel status %d", + isControl ? "control" : isBulk ? "bulk" : isIsoc ? "isoc" : "interrupt", result); if (kIOReturnSuccess == result || kIOReturnUnderrun == result) { if (isIsoc && tpriv->isoc_framelist) { /* copy isochronous results back */ for (i = 0; i < transfer->num_iso_packets ; i++) { - struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i]; - lib_desc->status = darwin_to_libusb (tpriv->isoc_framelist[i].frStatus); - lib_desc->actual_length = tpriv->isoc_framelist[i].frActCount; + struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i]; + lib_desc->status = darwin_to_libusb (tpriv->isoc_framelist[i].frStatus); + lib_desc->actual_length = tpriv->isoc_framelist[i].frActCount; } } else if (!isIsoc) itransfer->transferred += io_size; @@ -1618,64 +1800,35 @@ static void darwin_handle_callback (struct usbi_transfer *itransfer, kern_return } static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready) { - struct usbi_transfer *itransfer; - UInt32 io_size; - IOReturn kresult; + struct darwin_msg_async_io_complete message; POLL_NFDS_TYPE i = 0; ssize_t ret; - UInt32 message; usbi_mutex_lock(&ctx->open_devs_lock); + for (i = 0; i < nfds && num_ready > 0; i++) { struct pollfd *pollfd = &fds[i]; - struct libusb_device_handle *handle; - struct darwin_device_handle_priv *hpriv = NULL; - usbi_info (ctx, "checking fd %i with revents = %x", fds[i], pollfd->revents); + usbi_dbg ("checking fd %i with revents = %x", pollfd->fd, pollfd->revents); if (!pollfd->revents) continue; num_ready--; - list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) { - hpriv = (struct darwin_device_handle_priv *)handle->os_priv; - if (hpriv->fds[0] == pollfd->fd) - break; - } - - if (!(pollfd->revents & POLLERR)) { - ret = read (hpriv->fds[0], &message, sizeof (message)); - if (ret < (ssize_t)sizeof (message)) - continue; - } else - /* could not poll the device-- response is to delete the device (this seems a little heavy-handed) */ - message = MESSAGE_DEVICE_GONE; - - switch (message) { - case MESSAGE_DEVICE_GONE: - /* remove the device's async port from the runloop */ - if (hpriv->cfSource) { - if (libusb_darwin_acfl) - CFRunLoopRemoveSource (libusb_darwin_acfl, hpriv->cfSource, kCFRunLoopDefaultMode); - CFRelease (hpriv->cfSource); - hpriv->cfSource = NULL; - } - usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->fds[0]); - usbi_handle_disconnect(handle); - - /* done with this device */ + if (pollfd->revents & POLLERR) { + /* this probably will never happen so ignore the error an move on. */ continue; - case MESSAGE_ASYNC_IO_COMPLETE: - read (hpriv->fds[0], &itransfer, sizeof (itransfer)); - read (hpriv->fds[0], &kresult, sizeof (IOReturn)); - read (hpriv->fds[0], &io_size, sizeof (UInt32)); + } - darwin_handle_callback (itransfer, kresult, io_size); - break; - default: - usbi_err (ctx, "unknown message received from device pipe"); + /* there is only one type of message */ + ret = read (pollfd->fd, &message, sizeof (message)); + if (ret < (ssize_t) sizeof (message)) { + usbi_dbg ("WARNING: short read on async io completion pipe\n"); + continue; } + + darwin_handle_callback (message.itransfer, message.result, message.size); } usbi_mutex_unlock(&ctx->open_devs_lock); @@ -1709,42 +1862,42 @@ static int darwin_clock_gettime(int clk_id, struct timespec *tp) { } const struct usbi_os_backend darwin_backend = { - .name = "Darwin", - .init = darwin_init, - .exit = darwin_exit, - .get_device_list = darwin_get_device_list, - .get_device_descriptor = darwin_get_device_descriptor, - .get_active_config_descriptor = darwin_get_active_config_descriptor, - .get_config_descriptor = darwin_get_config_descriptor, - - .open = darwin_open, - .close = darwin_close, - .get_configuration = darwin_get_configuration, - .set_configuration = darwin_set_configuration, - .claim_interface = darwin_claim_interface, - .release_interface = darwin_release_interface, - - .set_interface_altsetting = darwin_set_interface_altsetting, - .clear_halt = darwin_clear_halt, - .reset_device = darwin_reset_device, - - .kernel_driver_active = darwin_kernel_driver_active, - .detach_kernel_driver = darwin_detach_kernel_driver, - .attach_kernel_driver = darwin_attach_kernel_driver, - - .destroy_device = darwin_destroy_device, - - .submit_transfer = darwin_submit_transfer, - .cancel_transfer = darwin_cancel_transfer, - .clear_transfer_priv = darwin_clear_transfer_priv, - - .handle_events = op_handle_events, - - .clock_gettime = darwin_clock_gettime, - - .device_priv_size = sizeof(struct darwin_device_priv), - .device_handle_priv_size = sizeof(struct darwin_device_handle_priv), - .transfer_priv_size = sizeof(struct darwin_transfer_priv), - .add_iso_packet_size = 0, + .name = "Darwin", + .caps = 0, + .init = darwin_init, + .exit = darwin_exit, + .get_device_list = NULL, /* not needed */ + .get_device_descriptor = darwin_get_device_descriptor, + .get_active_config_descriptor = darwin_get_active_config_descriptor, + .get_config_descriptor = darwin_get_config_descriptor, + + .open = darwin_open, + .close = darwin_close, + .get_configuration = darwin_get_configuration, + .set_configuration = darwin_set_configuration, + .claim_interface = darwin_claim_interface, + .release_interface = darwin_release_interface, + + .set_interface_altsetting = darwin_set_interface_altsetting, + .clear_halt = darwin_clear_halt, + .reset_device = darwin_reset_device, + + .kernel_driver_active = darwin_kernel_driver_active, + .detach_kernel_driver = darwin_detach_kernel_driver, + .attach_kernel_driver = darwin_attach_kernel_driver, + + .destroy_device = darwin_destroy_device, + + .submit_transfer = darwin_submit_transfer, + .cancel_transfer = darwin_cancel_transfer, + .clear_transfer_priv = darwin_clear_transfer_priv, + + .handle_events = op_handle_events, + + .clock_gettime = darwin_clock_gettime, + + .device_priv_size = sizeof(struct darwin_device_priv), + .device_handle_priv_size = sizeof(struct darwin_device_handle_priv), + .transfer_priv_size = sizeof(struct darwin_transfer_priv), + .add_iso_packet_size = 0, }; - diff --git a/third_party/libusb/src/libusb/os/darwin_usb.h b/third_party/libusb/src/libusb/os/darwin_usb.h index 59d0a69..53b8542 100644 --- a/third_party/libusb/src/libusb/os/darwin_usb.h +++ b/third_party/libusb/src/libusb/os/darwin_usb.h @@ -1,6 +1,6 @@ /* - * darwin backend for libusb 1.0 - * Copyright (C) 2008-2009 Nathan Hjelm <hjelmn@users.sourceforge.net> + * darwin backend for libusbx 1.0 + * Copyright © 2008-2013 Nathan Hjelm <hjelmn@users.sourceforge.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,7 +28,19 @@ #include <IOKit/IOCFPlugIn.h> /* 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" @@ -71,7 +65,13 @@ #endif /* IOUSBDeviceInterface */ -#if defined (kIOUSBDeviceInterfaceID320) +#if defined (kIOUSBDeviceInterfaceID500) + +#define usb_device_t IOUSBDeviceInterface500 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID500 +#define DeviceVersion 500 + +#elif defined (kIOUSBDeviceInterfaceID320) #define usb_device_t IOUSBDeviceInterface320 #define DeviceInterfaceID kIOUSBDeviceInterfaceID320 @@ -89,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" @@ -121,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 { @@ -147,23 +144,19 @@ struct darwin_device_handle_priv { struct darwin_transfer_priv { /* Isoc */ IOUSBIsocFrame *isoc_framelist; - size_t num_iso_packets; + int num_iso_packets; /* Control */ -#if !defined (LIBUSB_NO_TIMEOUT_DEVICE) IOUSBDevRequestTO req; -#else - IOUSBDevRequest req; -#endif /* Bulk */ }; -enum { - MESSAGE_DEVICE_GONE, - MESSAGE_ASYNC_IO_COMPLETE +/* structure for signaling io completion */ +struct darwin_msg_async_io_complete { + struct usbi_transfer *itransfer; + IOReturn result; + UInt32 size; }; - - #endif diff --git a/third_party/libusb/src/libusb/os/linux_netlink.c b/third_party/libusb/src/libusb/os/linux_netlink.c new file mode 100644 index 0000000..3a68f69 --- /dev/null +++ b/third_party/libusb/src/libusb/os/linux_netlink.c @@ -0,0 +1,254 @@ +/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */ +/* + * Linux usbfs backend for libusb + * Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org> + * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * Copyright (c) 2013 Nathan Hjelm <hjelmn@mac.com> + * + * 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 <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#include "libusb.h" +#include "libusbi.h" +#include "linux_usbfs.h" + +#include <linux/netlink.h> +#include <linux/filter.h> + +#define KERNEL 1 + +static int linux_netlink_socket = -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 }; + +int linux_netlink_start_event_monitor(void) +{ + int ret; + + snl.nl_groups = KERNEL; + + linux_netlink_socket = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT); + if (-1 == linux_netlink_socket) { + return LIBUSB_ERROR_OTHER; + } + + ret = bind(linux_netlink_socket, (struct sockaddr *) &snl, sizeof(snl)); + if (0 != ret) { + return LIBUSB_ERROR_OTHER; + } + + /* TODO -- add authentication */ + /* setsockopt(linux_netlink_socket, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); */ + + ret = pthread_create(&libusb_linux_event_thread, NULL, linux_netlink_event_thread_main, NULL); + if (0 != ret) { + return LIBUSB_ERROR_OTHER; + } + + return LIBUSB_SUCCESS; +} + +int linux_netlink_stop_event_monitor(void) +{ + int r; + + 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; + } + + pthread_cancel(libusb_linux_event_thread); + + linux_netlink_socket = -1; + + return LIBUSB_SUCCESS; +} + +static const char *netlink_message_parse (const char *buffer, size_t len, const char *key) +{ + size_t keylen = strlen(key); + size_t offset; + + for (offset = 0 ; offset < len && '\0' != buffer[offset] ; offset += strlen(buffer + offset) + 1) { + if (0 == strncmp(buffer + offset, key, keylen) && + '=' == buffer[offset + keylen]) { + return buffer + offset + keylen + 1; + } + } + + return NULL; +} + +/* parse parts of netlink message common to both libudev and the kernel */ +static int linux_netlink_parse(char *buffer, size_t len, int *detached, const char **sys_name, + uint8_t *busnum, uint8_t *devaddr) { + const char *tmp; + int i; + + errno = 0; + + *sys_name = NULL; + *detached = 0; + *busnum = 0; + *devaddr = 0; + + tmp = netlink_message_parse((const char *) buffer, len, "ACTION"); + if (tmp == NULL) + return -1; + if (0 == strcmp(tmp, "remove")) { + *detached = 1; + } else if (0 != strcmp(tmp, "add")) { + usbi_dbg("unknown device action %s", tmp); + return -1; + } + + /* check that this is a usb message */ + tmp = netlink_message_parse(buffer, len, "SUBSYSTEM"); + if (NULL == tmp || 0 != strcmp(tmp, "usb")) { + /* not usb. ignore */ + return -1; + } + + tmp = netlink_message_parse(buffer, len, "BUSNUM"); + if (NULL == tmp) { + /* no bus number (likely a usb interface). ignore*/ + return -1; + } + + *busnum = (uint8_t)(strtoul(tmp, NULL, 10) & 0xff); + if (errno) { + errno = 0; + return -1; + } + + tmp = netlink_message_parse(buffer, len, "DEVNUM"); + if (NULL == tmp) { + return -1; + } + + *devaddr = (uint8_t)(strtoul(tmp, NULL, 10) & 0xff); + if (errno) { + errno = 0; + return -1; + } + + tmp = netlink_message_parse(buffer, len, "DEVPATH"); + if (NULL == tmp) { + return -1; + } + + for (i = strlen(tmp) - 1 ; i ; --i) { + if ('/' ==tmp[i]) { + *sys_name = tmp + i + 1; + break; + } + } + + /* found a usb device */ + return 0; +} + +static int linux_netlink_read_message(void) +{ + unsigned char buffer[1024]; + struct iovec iov = {.iov_base = buffer, .iov_len = sizeof(buffer)}; + struct msghdr meh = { .msg_iov=&iov, .msg_iovlen=1, + .msg_name=&snl, .msg_namelen=sizeof(snl) }; + const char *sys_name = NULL; + uint8_t busnum, devaddr; + int detached, r; + size_t len; + + /* read netlink message */ + memset(buffer, 0, sizeof(buffer)); + len = recvmsg(linux_netlink_socket, &meh, 0); + if (len < 32) { + if (errno != EAGAIN) + usbi_dbg("error recieving message from netlink"); + return -1; + } + + /* TODO -- authenticate this message is from the kernel or udevd */ + + r = linux_netlink_parse(buffer, len, &detached, &sys_name, + &busnum, &devaddr); + if (r) + return r; + + usbi_dbg("netlink hotplug found device busnum: %hhu, devaddr: %hhu, sys_name: %s, removed: %s", + busnum, devaddr, sys_name, detached ? "yes" : "no"); + + /* signal device is available (or not) to all contexts */ + if (detached) + linux_hotplug_disconnected(busnum, devaddr, sys_name); + else + linux_hotplug_enumerate(busnum, devaddr, sys_name); + + return 0; +} + +static void *linux_netlink_event_thread_main(void *arg) +{ + struct pollfd fds = {.fd = linux_netlink_socket, + .events = POLLIN}; + + /* silence compiler warning */ + (void) arg; + + while (1 == poll(&fds, 1, -1)) { + if (POLLIN != fds.revents) { + break; + } + + usbi_mutex_static_lock(&linux_hotplug_lock); + linux_netlink_read_message(); + usbi_mutex_static_unlock(&linux_hotplug_lock); + } + + return NULL; +} + +void linux_netlink_hotplug_poll(void) +{ + int r; + + usbi_mutex_static_lock(&linux_hotplug_lock); + do { + r = linux_netlink_read_message(); + } while (r == 0); + usbi_mutex_static_unlock(&linux_hotplug_lock); +} diff --git a/third_party/libusb/src/libusb/os/linux_udev.c b/third_party/libusb/src/libusb/os/linux_udev.c new file mode 100644 index 0000000..5a2aadf --- /dev/null +++ b/third_party/libusb/src/libusb/os/linux_udev.c @@ -0,0 +1,273 @@ +/* -*- Mode: C; c-basic-offset:8 ; indent-tabs-mode:t -*- */ +/* + * Linux usbfs backend for libusb + * Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org> + * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * Copyright (c) 2012-2013 Nathan Hjelm <hjelmn@mac.com> + * + * 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 <assert.h> +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/socket.h> +#include <unistd.h> +#include <libudev.h> + +#include "libusb.h" +#include "libusbi.h" +#include "linux_usbfs.h" + +/* udev context */ +static struct udev *udev_ctx = NULL; +static int udev_monitor_fd = -1; +static struct udev_monitor *udev_monitor = NULL; +static pthread_t linux_event_thread; + +static void udev_hotplug_event(struct udev_device* udev_dev); +static void *linux_udev_event_thread_main(void *arg); + +int linux_udev_start_event_monitor(void) +{ + int r; + + assert(udev_ctx == NULL); + udev_ctx = udev_new(); + if (!udev_ctx) { + usbi_err(NULL, "could not create udev context"); + 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; + } + + r = udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", 0); + if (r) { + usbi_err(NULL, "could not initialize udev monitor filter for \"usb\" subsystem"); + goto err_free_monitor; + } + + if (udev_monitor_enable_receiving(udev_monitor)) { + usbi_err(NULL, "failed to enable the udev monitor"); + goto err_free_monitor; + } + + udev_monitor_fd = udev_monitor_get_fd(udev_monitor); + + /* Some older versions of udev are not non-blocking by default, + * so make sure this is set */ + r = fcntl(udev_monitor_fd, F_GETFL); + if (r == -1) { + usbi_err(NULL, "getting udev monitor fd flags (%d)", errno); + goto err_free_monitor; + } + r = fcntl(udev_monitor_fd, F_SETFL, r | O_NONBLOCK); + if (r) { + usbi_err(NULL, "setting udev monitor fd flags (%d)", errno); + goto err_free_monitor; + } + + r = 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_free_monitor; + } + + return LIBUSB_SUCCESS; + +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) +{ + assert(udev_ctx != NULL); + assert(udev_monitor != NULL); + assert(udev_monitor_fd != -1); + + /* Cancel the event thread. This is the only way to guarantee the + thread exits since closing the monitor fd won't necessarily cause + poll to return. */ + pthread_cancel(linux_event_thread); + pthread_join(linux_event_thread, NULL); + + /* Release the udev monitor */ + udev_monitor_unref(udev_monitor); + udev_monitor = NULL; + udev_monitor_fd = -1; + + /* Clean up the udev context */ + udev_unref(udev_ctx); + udev_ctx = NULL; + + return LIBUSB_SUCCESS; +} + +static void *linux_udev_event_thread_main(void *arg) +{ + struct udev_device* udev_dev; + struct pollfd fds = {.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) { + break; + } + + usbi_mutex_static_lock(&linux_hotplug_lock); + udev_dev = udev_monitor_receive_device(udev_monitor); + if (udev_dev) + udev_hotplug_event(udev_dev); + usbi_mutex_static_unlock(&linux_hotplug_lock); + } + + usbi_dbg("udev event thread exiting"); + + return NULL; +} + +static int udev_device_info(struct libusb_context *ctx, int detached, + struct udev_device *udev_dev, uint8_t *busnum, + uint8_t *devaddr, const char **sys_name) { + const char *dev_node; + + dev_node = udev_device_get_devnode(udev_dev); + if (!dev_node) { + return LIBUSB_ERROR_OTHER; + } + + *sys_name = udev_device_get_sysname(udev_dev); + if (!*sys_name) { + return LIBUSB_ERROR_OTHER; + } + + return linux_get_device_address(ctx, detached, busnum, devaddr, + dev_node, *sys_name); +} + +static void udev_hotplug_event(struct udev_device* udev_dev) +{ + const char* udev_action; + const char* sys_name = NULL; + uint8_t busnum = 0, devaddr = 0; + int detached; + int r; + + do { + udev_action = udev_device_get_action(udev_dev); + if (!udev_action) { + break; + } + + detached = !strncmp(udev_action, "remove", 6); + + r = udev_device_info(NULL, detached, udev_dev, &busnum, &devaddr, &sys_name); + if (LIBUSB_SUCCESS != r) { + break; + } + + usbi_dbg("udev hotplug event. action: %s.", udev_action); + + if (strncmp(udev_action, "add", 3) == 0) { + linux_hotplug_enumerate(busnum, devaddr, sys_name); + } else if (detached) { + linux_hotplug_disconnected(busnum, devaddr, sys_name); + } else { + usbi_err(NULL, "ignoring udev action %s", udev_action); + } + } while (0); + + udev_device_unref(udev_dev); +} + +int linux_udev_scan_devices(struct libusb_context *ctx) +{ + struct udev_enumerate *enumerator; + struct udev_list_entry *devices, *entry; + struct udev_device *udev_dev; + const char *sys_name; + int r; + + assert(udev_ctx != NULL); + + enumerator = udev_enumerate_new(udev_ctx); + if (NULL == enumerator) { + usbi_err(ctx, "error creating udev enumerator"); + return LIBUSB_ERROR_OTHER; + } + + udev_enumerate_add_match_subsystem(enumerator, "usb"); + udev_enumerate_scan_devices(enumerator); + devices = udev_enumerate_get_list_entry(enumerator); + + udev_list_entry_foreach(entry, devices) { + const char *path = udev_list_entry_get_name(entry); + uint8_t busnum = 0, devaddr = 0; + + udev_dev = udev_device_new_from_syspath(udev_ctx, path); + + r = udev_device_info(ctx, 0, udev_dev, &busnum, &devaddr, &sys_name); + if (r) { + udev_device_unref(udev_dev); + continue; + } + + linux_enumerate_device(ctx, busnum, devaddr, sys_name); + udev_device_unref(udev_dev); + } + + udev_enumerate_unref(enumerator); + + return LIBUSB_SUCCESS; +} + +void linux_udev_hotplug_poll(void) +{ + struct udev_device* udev_dev; + + usbi_mutex_static_lock(&linux_hotplug_lock); + do { + udev_dev = udev_monitor_receive_device(udev_monitor); + if (udev_dev) { + usbi_dbg("Handling hotplug event from hotplug_poll"); + udev_hotplug_event(udev_dev); + } + } while (udev_dev); + usbi_mutex_static_unlock(&linux_hotplug_lock); +} diff --git a/third_party/libusb/src/libusb/os/linux_usbfs.c b/third_party/libusb/src/libusb/os/linux_usbfs.c index 02d182d..09288af 100644 --- a/third_party/libusb/src/libusb/os/linux_usbfs.c +++ b/third_party/libusb/src/libusb/os/linux_usbfs.c @@ -1,7 +1,9 @@ /* - * Linux usbfs backend for libusb - * Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org> - * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * Linux usbfs backend for libusbx + * Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org> + * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com> + * Copyright © 2013 Nathan Hjelm <hjelmn@mac.com> + * Copyright © 2012-2013 Hans de Goede <hdegoede@redhat.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,7 +20,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <config.h> +#include "config.h" + +#include <assert.h> #include <ctype.h> #include <dirent.h> #include <errno.h> @@ -43,23 +47,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: @@ -67,6 +71,8 @@ * Descriptors exposed by usbfs have the multi-byte fields in the device * descriptor as host endian. Multi-byte fields in the other descriptors are * bus-endian. The kernel documentation says otherwise, but it is wrong. + * + * In sysfs all descriptors are bus-endian. */ static const char *usbfs_path = NULL; @@ -101,21 +107,42 @@ 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; + +/* 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; + +/* Serialize hotplug start/stop, scan-devices, event-thread, and poll */ +usbi_mutex_static_t linux_hotplug_lock = USBI_MUTEX_INITIALIZER; -/* do we have a descriptors file? */ -static int sysfs_has_descriptors = 0; +static int linux_start_event_monitor(void); +static int linux_stop_event_monitor(void); +static int linux_scan_devices(struct libusb_context *ctx); +static int sysfs_scan_device(struct libusb_context *ctx, const char *devname); +static int detach_kernel_driver_and_claim(struct libusb_device_handle *, int); + +#if !defined(USE_UDEV) +static int linux_default_scan_devices (struct libusb_context *ctx); +#endif struct linux_device_priv { char *sysfs_dir; - unsigned char *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 { int fd; + uint32_t caps; }; enum reap_action { @@ -142,21 +169,43 @@ struct linux_transfer_priv { enum reap_action reap_action; int num_urbs; - unsigned int num_retired; + int num_retired; enum libusb_transfer_status reap_status; /* next iso packet in user-supplied transfer to be populated */ 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) @@ -299,22 +348,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; @@ -353,79 +386,103 @@ 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 (-1 == sysfs_has_descriptors) { + /* sysfs descriptors has all descriptors since Linux 2.6.26 */ + sysfs_has_descriptors = kernel_version_ge(2,6,26); + if (-1 == sysfs_has_descriptors) { + usbi_err(ctx, "error checking for sysfs descriptors"); + return LIBUSB_ERROR_OTHER; + } + } - if (!devices) { - usbi_err(ctx, "opendir devices failed errno=%d", errno); - return LIBUSB_ERROR_IO; + 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; } + } - /* 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; + 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; + } + } - /* 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 (sysfs_can_relate_devices) + usbi_dbg("sysfs can relate devices"); - if (has_busnum && has_devnum && has_configuration_value) - sysfs_can_relate_devices = 1; - if (has_descriptors) - sysfs_has_descriptors = 1; + if (sysfs_has_descriptors) + usbi_dbg("sysfs has complete descriptors"); + + usbi_mutex_static_lock(&linux_hotplug_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_lock); - /* Only need to check until we've found ONE device which - has all the attributes. */ - if (sysfs_has_descriptors && sysfs_can_relate_devices) - break; - } - closedir(devices); + return r; +} - /* Only use sysfs descriptors if the rest of - sysfs will work for libusb. */ - if (!sysfs_can_relate_devices) - sysfs_has_descriptors = 0; - } else { - usbi_dbg("sysfs usb info not available"); - sysfs_has_descriptors = 0; - sysfs_can_relate_devices = 0; +static void op_exit(void) +{ + usbi_mutex_static_lock(&linux_hotplug_lock); + assert(init_count != 0); + if (!--init_count) { + /* tear down event handler */ + (void)linux_stop_event_monitor(); } + usbi_mutex_static_unlock(&linux_hotplug_lock); +} - return 0; +static int linux_start_event_monitor(void) +{ +#if defined(USE_UDEV) + return linux_udev_start_event_monitor(); +#else + return linux_netlink_start_event_monitor(); +#endif } -static int usbfs_get_device_descriptor(struct libusb_device *dev, - unsigned char *buffer) +static int linux_stop_event_monitor(void) { - struct linux_device_priv *priv = _device_priv(dev); +#if defined(USE_UDEV) + return linux_udev_stop_event_monitor(); +#else + return linux_netlink_stop_event_monitor(); +#endif +} - /* return cached copy */ - memcpy(buffer, priv->dev_descriptor, DEVICE_DESC_LENGTH); - return 0; +static int linux_scan_devices(struct libusb_context *ctx) +{ +#if defined(USE_UDEV) + return linux_udev_scan_devices(ctx); +#else + return linux_default_scan_devices(ctx); +#endif +} + +static void op_hotplug_poll(void) +{ +#if defined(USE_UDEV) + linux_udev_hotplug_poll(); +#else + linux_netlink_hotplug_poll(); +#endif } static int _open_sysfs_attr(struct libusb_device *dev, const char *attr) @@ -481,52 +538,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) { - 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; } @@ -573,250 +592,213 @@ 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; + 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; + } - /* 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 < sizeof(tmp)) { - usbi_err(ctx, "short descriptor read %d/%d", r, sizeof(tmp)); - 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); + } - /* 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; + return LIBUSB_SUCCESS; } - return 0; + usbi_dbg("scan %s", sys_name); + + *busnum = __read_sysfs_attr(ctx, sys_name, "busnum"); + if (0 > *busnum) + return *busnum; + + *devaddr = __read_sysfs_attr(ctx, sys_name, "devnum"); + if (0 > *devaddr) + return *devaddr; + + usbi_dbg("bus=%d dev=%d", *busnum, *devaddr); + if (*busnum > 255 || *devaddr > 255) + return LIBUSB_ERROR_INVALID_PARAM; + + return LIBUSB_SUCCESS; } -static int sysfs_get_active_config_descriptor(struct libusb_device *dev, - unsigned char *buffer, size_t len) +/* 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) { - int fd; - ssize_t r; - off_t off; - int to_copy; - int config; - unsigned char tmp[6]; + struct usb_descriptor_header header; + int i; - r = sysfs_get_active_config(dev, &config); - if (r < 0) - return r; - if (config == -1) - return LIBUSB_ERROR_NOT_FOUND; + for (i = 0; size >= 0; i += header.bLength, size -= header.bLength) { + if (size == 0) + return LIBUSB_ERROR_NOT_FOUND; - usbi_dbg("active configuration %d", config); + if (size < 2) { + usbi_err(ctx, "short descriptor read %d/2", size); + return LIBUSB_ERROR_IO; + } + usbi_parse_descriptor(buffer + i, "bb", &header, 0); - /* sysfs provides access to an in-memory copy of the device descriptor, - * so we use that rather than keeping our own copy */ + if (i && header.bDescriptorType == descriptor_type) + return i; + } + usbi_err(ctx, "bLength overflow by %d bytes", -size); + return LIBUSB_ERROR_IO; +} - fd = _open_sysfs_attr(dev, "descriptors"); - if (fd < 0) - return fd; +/* 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; - /* 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); + 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; } - 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); + 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; } - /* 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 < sizeof(tmp)) { - usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, sizeof(tmp)); + /* + * 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; + } +} - /* check bConfigurationValue */ - if (tmp[5] == config) - break; +static int op_get_config_descriptor_by_value(struct libusb_device *dev, + uint8_t value, unsigned char **buffer, int *host_endian) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + struct linux_device_priv *priv = _device_priv(dev); + unsigned char *descriptors = priv->descriptors; + int size = priv->descriptors_len; + struct libusb_config_descriptor *config; - /* try the next descriptor */ - off = lseek(fd, 0 - sizeof(tmp), SEEK_CUR); - if (off < 0) - return LIBUSB_ERROR_IO; + *buffer = NULL; + /* Unlike the device desc. config descs. are always in raw format */ + *host_endian = 0; - r = seek_to_next_config(DEVICE_CTX(dev), fd, 0); - if (r < 0) - return r; - } + /* Skip device header */ + descriptors += DEVICE_DESC_LENGTH; + size -= DEVICE_DESC_LENGTH; - 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 (r < len - sizeof(tmp)) { - usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, len); - r = LIBUSB_ERROR_IO; + /* 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; } - } else { - r = 0; + size -= next; + descriptors += next; } - - close(fd); - return r; } static int op_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *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); - } -} - -/* 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; + int r, config; + unsigned char *config_desc; - 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 (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; - - /* 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 */ @@ -853,11 +835,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; @@ -884,113 +864,162 @@ 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 */ - 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; - } + close(fd); + return r; +} + +static int linux_get_parent_info(struct libusb_device *dev, const char *sysfs_dir) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + struct libusb_device *it; + char *parent_sysfs_dir, *tmp; + int ret, add_parent = 1; + + /* XXX -- can we figure out the topology when using usbfs? */ + if (NULL == sysfs_dir || 0 == strncmp(sysfs_dir, "usb", 3)) { + /* either using usbfs or finding the parent of a root hub */ + return LIBUSB_SUCCESS; + } + + parent_sysfs_dir = strdup(sysfs_dir); + if (NULL != (tmp = strrchr(parent_sysfs_dir, '.')) || + NULL != (tmp = strrchr(parent_sysfs_dir, '-'))) { + dev->port_number = atoi(tmp + 1); + *tmp = '\0'; + } else { + usbi_warn(ctx, "Can not parse sysfs_dir: %s, no parent info", + parent_sysfs_dir); + free (parent_sysfs_dir); + return LIBUSB_SUCCESS; + } + + /* is the parent a root hub? */ + if (NULL == strchr(parent_sysfs_dir, '-')) { + tmp = parent_sysfs_dir; + ret = asprintf (&parent_sysfs_dir, "usb%s", tmp); + free (tmp); + if (0 > ret) { + return LIBUSB_ERROR_NO_MEM; } } - 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; } -static int enumerate_device(struct libusb_context *ctx, - struct discovered_devs **_discdevs, uint8_t busnum, uint8_t devaddr, - const char *sysfs_dir) +int linux_enumerate_device(struct libusb_context *ctx, + uint8_t busnum, uint8_t devaddr, const char *sysfs_dir) { - struct discovered_devs *discdevs; unsigned long session_id; - int need_unref = 0; struct libusb_device *dev; int r = 0; @@ -1001,48 +1030,73 @@ static int enumerate_device(struct libusb_context *ctx, usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr, session_id); - dev = usbi_get_device_by_session_id(ctx, session_id); - if (dev) { - usbi_dbg("using existing device for %d/%d (session %ld)", - busnum, devaddr, session_id); - } else { - usbi_dbg("allocating new device for %d/%d (session %ld)", - busnum, devaddr, session_id); - dev = usbi_alloc_device(ctx, session_id); - if (!dev) - return LIBUSB_ERROR_NO_MEM; - need_unref = 1; - r = initialize_device(dev, busnum, devaddr, sysfs_dir); - if (r < 0) - goto out; - r = usbi_sanitize_device(dev); - if (r < 0) - goto out; + 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; } - discdevs = discovered_devs_append(*_discdevs, dev); - if (!discdevs) - r = LIBUSB_ERROR_NO_MEM; - else - *_discdevs = discdevs; + usbi_dbg("allocating new device for %d/%d (session %ld)", + busnum, devaddr, session_id); + dev = usbi_alloc_device(ctx, session_id); + if (!dev) + return LIBUSB_ERROR_NO_MEM; + r = initialize_device(dev, busnum, devaddr, sysfs_dir); + if (r < 0) + goto out; + r = usbi_sanitize_device(dev); + if (r < 0) + goto out; + + r = linux_get_parent_info(dev, sysfs_dir); + if (r < 0) + goto out; out: - if (need_unref) + if (r < 0) libusb_unref_device(dev); + else + usbi_connect_device(dev); + return r; } -/* open a bus directory and adds all discovered devices to discdevs. on - * failure (non-zero return) the pre-existing discdevs should be destroyed - * (and devices freed). on success, the new discdevs pointer should be used - * as it may have been moved. */ -static int usbfs_scan_busdir(struct libusb_context *ctx, - struct discovered_devs **_discdevs, uint8_t busnum) +void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_name) +{ + struct libusb_context *ctx; + + usbi_mutex_static_lock(&active_contexts_lock); + list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { + linux_enumerate_device(ctx, busnum, devaddr, sys_name); + } + usbi_mutex_static_unlock(&active_contexts_lock); +} + +void linux_hotplug_disconnected(uint8_t busnum, uint8_t devaddr, const char *sys_name) +{ + struct libusb_context *ctx; + struct libusb_device *dev; + unsigned long session_id = busnum << 8 | devaddr; + + usbi_mutex_static_lock(&active_contexts_lock); + list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { + dev = usbi_get_device_by_session_id (ctx, session_id); + if (NULL != dev) { + usbi_disconnect_device (dev); + } else { + usbi_dbg("device not found for session %x", session_id); + } + } + usbi_mutex_static_unlock(&active_contexts_lock); +} + +#if !defined(USE_UDEV) +/* open a bus directory and adds all discovered devices to the context */ +static int usbfs_scan_busdir(struct libusb_context *ctx, uint8_t busnum) { DIR *dir; char dirpath[PATH_MAX]; struct dirent *entry; - struct discovered_devs *discdevs = *_discdevs; int r = LIBUSB_ERROR_IO; snprintf(dirpath, PATH_MAX, "%s/%03d", usbfs_path, busnum); @@ -1067,7 +1121,7 @@ static int usbfs_scan_busdir(struct libusb_context *ctx, continue; } - if (enumerate_device(ctx, &discdevs, busnum, (uint8_t) devaddr, NULL)) { + if (linux_enumerate_device(ctx, busnum, (uint8_t) devaddr, NULL)) { usbi_dbg("failed to enumerate dir entry %s", entry->d_name); continue; } @@ -1075,18 +1129,14 @@ static int usbfs_scan_busdir(struct libusb_context *ctx, r = 0; } - if (!r) - *_discdevs = discdevs; closedir(dir); return r; } -static int usbfs_get_device_list(struct libusb_context *ctx, - struct discovered_devs **_discdevs) +static int usbfs_get_device_list(struct libusb_context *ctx) { struct dirent *entry; DIR *buses = opendir(usbfs_path); - struct discovered_devs *discdevs = *_discdevs; int r = 0; if (!buses) { @@ -1095,7 +1145,6 @@ static int usbfs_get_device_list(struct libusb_context *ctx, } while ((entry = readdir(buses))) { - struct discovered_devs *discdevs_new = discdevs; int busnum; if (entry->d_name[0] == '.') @@ -1106,8 +1155,7 @@ static int usbfs_get_device_list(struct libusb_context *ctx, if (!_is_usbdev_entry(entry, &busnum, &devaddr)) continue; - r = enumerate_device(ctx, &discdevs_new, busnum, - (uint8_t) devaddr, NULL); + r = linux_enumerate_device(ctx, busnum, (uint8_t) devaddr, NULL); if (r < 0) { usbi_dbg("failed to enumerate dir entry %s", entry->d_name); continue; @@ -1119,48 +1167,35 @@ static int usbfs_get_device_list(struct libusb_context *ctx, continue; } - r = usbfs_scan_busdir(ctx, &discdevs_new, busnum); + r = usbfs_scan_busdir(ctx, busnum); if (r < 0) - goto out; + break; } - discdevs = discdevs_new; } -out: closedir(buses); - *_discdevs = discdevs; return r; } +#endif -static int sysfs_scan_device(struct libusb_context *ctx, - struct discovered_devs **_discdevs, const char *devname) +static int sysfs_scan_device(struct libusb_context *ctx, const char *devname) { - int busnum; - int devaddr; - - usbi_dbg("scan %s", devname); + uint8_t busnum, devaddr; + int ret; - busnum = __read_sysfs_attr(ctx, devname, "busnum"); - if (busnum < 0) - return busnum; - - devaddr = __read_sysfs_attr(ctx, devname, "devnum"); - if (devaddr < 0) - return devaddr; - - usbi_dbg("bus=%d dev=%d", busnum, devaddr); - if (busnum > 255 || devaddr > 255) - return LIBUSB_ERROR_INVALID_PARAM; + ret = linux_get_device_address (ctx, 0, &busnum, &devaddr, NULL, devname); + if (LIBUSB_SUCCESS != ret) { + return ret; + } - return enumerate_device(ctx, _discdevs, busnum & 0xff, devaddr & 0xff, + return linux_enumerate_device(ctx, busnum & 0xff, devaddr & 0xff, devname); } -static int sysfs_get_device_list(struct libusb_context *ctx, - struct discovered_devs **_discdevs) +#if !defined(USE_UDEV) +static int sysfs_get_device_list(struct libusb_context *ctx) { - struct discovered_devs *discdevs = *_discdevs; DIR *devices = opendir(SYSFS_DEVICE_PATH); struct dirent *entry; int r = LIBUSB_ERROR_IO; @@ -1171,29 +1206,23 @@ static int sysfs_get_device_list(struct libusb_context *ctx, } while ((entry = readdir(devices))) { - struct discovered_devs *discdevs_new = discdevs; - if ((!isdigit(entry->d_name[0]) && strncmp(entry->d_name, "usb", 3)) || strchr(entry->d_name, ':')) continue; - if (sysfs_scan_device(ctx, &discdevs_new, entry->d_name)) { + if (sysfs_scan_device(ctx, entry->d_name)) { usbi_dbg("failed to enumerate dir entry %s", entry->d_name); continue; } r = 0; - discdevs = discdevs_new; } - if (!r) - *_discdevs = discdevs; closedir(devices); return r; } -static int op_get_device_list(struct libusb_context *ctx, - struct discovered_devs **_discdevs) +static int linux_default_scan_devices (struct libusb_context *ctx) { /* we can retrieve device list and descriptors from sysfs or usbfs. * sysfs is preferable, because if we use usbfs we end up resuming @@ -1206,35 +1235,32 @@ static int op_get_device_list(struct libusb_context *ctx, * adequacy of sysfs and sets sysfs_can_relate_devices. */ if (sysfs_can_relate_devices != 0) - return sysfs_get_device_list(ctx, _discdevs); + return sysfs_get_device_list(ctx); else - return usbfs_get_device_list(ctx, _discdevs); + return usbfs_get_device_list(ctx); } +#endif static int op_open(struct libusb_device_handle *handle) { struct linux_device_handle_priv *hpriv = _device_handle_priv(handle); - char filename[PATH_MAX]; + int r; - _get_usbfs_path(handle->dev, filename); - usbi_dbg("opening %s", filename); - hpriv->fd = open(filename, O_RDWR); - 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; - } + hpriv->fd = _get_usbfs_fd(handle->dev, O_RDWR, 0); + if (hpriv->fd < 0) + return hpriv->fd; + + r = ioctl(hpriv->fd, IOCTL_USBFS_GET_CAPABILITIES, &hpriv->caps); + if (r < 0) { + if (errno == ENOTTY) + usbi_dbg("getcap not available"); + else + usbi_err(HANDLE_CTX(handle), "getcap failed (%d)", errno); + hpriv->caps = 0; + if (supports_flag_zero_packet) + hpriv->caps |= USBFS_CAP_ZERO_PACKET; + if (supports_flag_bulk_continuation) + hpriv->caps |= USBFS_CAP_BULK_CONTINUATION; } return usbi_add_pollfd(HANDLE_CTX(handle), hpriv->fd, POLLOUT); @@ -1251,10 +1277,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; @@ -1283,25 +1312,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); @@ -1320,7 +1337,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); @@ -1391,7 +1408,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); } } @@ -1412,11 +1429,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; } } } @@ -1445,7 +1469,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, @@ -1453,12 +1477,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) @@ -1508,15 +1538,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); } @@ -1583,6 +1667,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer, struct usbfs_urb *urbs; int is_out = (transfer->endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT; + int bulk_buffer_len, use_bulk_continuation; int r; int i; size_t alloc_size; @@ -1590,30 +1675,67 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer, if (tpriv->urbs) return LIBUSB_ERROR_BUSY; - if (is_out && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET && - !supports_flag_zero_packet) + if (is_out && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) && + !(dpriv->caps & USBFS_CAP_ZERO_PACKET)) return LIBUSB_ERROR_NOT_SUPPORTED; - /* usbfs places a 16kb limit on bulk URBs. we divide up larger requests - * into smaller units to meet such restriction, then fire off all the - * units at once. it would be simpler if we just fired one unit at a time, - * but there is a big performance gain through doing it this way. */ - int num_urbs = transfer->length / MAX_BULK_BUFFER_LENGTH; + /* + * Older versions of usbfs place a 16kb limit on bulk URBs. We work + * around this by splitting large transfers into 16k blocks, and then + * submit all urbs at once. it would be simpler to submit one urb at + * a time, but there is a big performance gain doing it this way. + * + * Newer versions lift the 16k limit (USBFS_CAP_NO_PACKET_SIZE_LIM), + * using arbritary large transfers can still be a bad idea though, as + * the kernel needs to allocate physical contiguous memory for this, + * which may fail for large buffers. + * + * The kernel solves this problem by splitting the transfer into + * blocks itself when the host-controller is scatter-gather capable + * (USBFS_CAP_BULK_SCATTER_GATHER), which most controllers are. + * + * Last, there is the issue of short-transfers when splitting, for + * short split-transfers to work reliable USBFS_CAP_BULK_CONTINUATION + * is needed, but this is not always available. + */ + if (dpriv->caps & USBFS_CAP_BULK_SCATTER_GATHER) { + /* Good! Just submit everything in one go */ + bulk_buffer_len = transfer->length ? transfer->length : 1; + use_bulk_continuation = 0; + } else if (dpriv->caps & USBFS_CAP_BULK_CONTINUATION) { + /* Split the transfers and use bulk-continuation to + avoid issues with short-transfers */ + bulk_buffer_len = MAX_BULK_BUFFER_LENGTH; + use_bulk_continuation = 1; + } else if (dpriv->caps & USBFS_CAP_NO_PACKET_SIZE_LIM) { + /* Don't split, assume the kernel can alloc the buffer + (otherwise the submit will fail with -ENOMEM) */ + bulk_buffer_len = transfer->length ? transfer->length : 1; + use_bulk_continuation = 0; + } else { + /* Bad, splitting without bulk-continuation, short transfers + which end before the last urb will not work reliable! */ + /* Note we don't warn here as this is "normal" on kernels < + 2.6.32 and not a problem for most applications */ + bulk_buffer_len = MAX_BULK_BUFFER_LENGTH; + use_bulk_continuation = 0; + } + + int num_urbs = transfer->length / bulk_buffer_len; int last_urb_partial = 0; if (transfer->length == 0) { num_urbs = 1; - } else if ((transfer->length % MAX_BULK_BUFFER_LENGTH) > 0) { + } else if ((transfer->length % bulk_buffer_len) > 0) { last_urb_partial = 1; num_urbs++; } usbi_dbg("need %d urbs for new transfer with length %d", num_urbs, transfer->length); alloc_size = num_urbs * sizeof(struct usbfs_urb); - urbs = 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; @@ -1625,17 +1747,18 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer, urb->usercontext = itransfer; urb->type = urb_type; urb->endpoint = transfer->endpoint; - urb->buffer = transfer->buffer + (i * MAX_BULK_BUFFER_LENGTH); - if (supports_flag_bulk_continuation && !is_out) + urb->buffer = transfer->buffer + (i * bulk_buffer_len); + /* don't set the short not ok flag for the last URB */ + if (use_bulk_continuation && !is_out && (i < num_urbs - 1)) urb->flags = USBFS_URB_SHORT_NOT_OK; if (i == num_urbs - 1 && last_urb_partial) - urb->buffer_length = transfer->length % MAX_BULK_BUFFER_LENGTH; + urb->buffer_length = transfer->length % bulk_buffer_len; else if (transfer->length == 0) urb->buffer_length = 0; else - urb->buffer_length = MAX_BULK_BUFFER_LENGTH; + urb->buffer_length = bulk_buffer_len; - if (i > 0 && supports_flag_bulk_continuation) + if (i > 0 && use_bulk_continuation) urb->flags |= USBFS_URB_BULK_CONTINUATION; /* we have already checked that the flag is supported */ @@ -1667,7 +1790,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. @@ -1722,11 +1845,17 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) /* usbfs places a 32kb limit on iso URBs. we divide up larger requests * into smaller units to meet such restriction, then fire off all the * units at once. it would be simpler if we just fired one unit at a time, - * but there is a big performance gain through doing it this way. */ + * but there is a big performance gain through doing it this way. + * + * Newer kernels lift the 32k limit (USBFS_CAP_NO_PACKET_SIZE_LIM), + * using arbritary large transfers is still be a bad idea though, as + * the kernel needs to allocate physical contiguous memory for this, + * which may fail for large buffers. + */ /* calculate how many URBs we need */ for (i = 0; i < num_packets; i++) { - int space_remaining = MAX_ISO_BUFFER_LENGTH - this_urb_len; + unsigned int space_remaining = MAX_ISO_BUFFER_LENGTH - this_urb_len; packet_len = transfer->iso_packet_desc[i].length; if (packet_len > space_remaining) { @@ -1739,10 +1868,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; @@ -1753,7 +1881,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) /* allocate + initialize each URB with the correct number of packets */ for (i = 0; i < num_urbs; i++) { struct usbfs_urb *urb; - int space_remaining_in_urb = MAX_ISO_BUFFER_LENGTH; + unsigned int space_remaining_in_urb = MAX_ISO_BUFFER_LENGTH; int urb_packet_offset = 0; unsigned char *urb_buffer_orig = urb_buffer; int j; @@ -1776,12 +1904,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 */ @@ -1825,7 +1952,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. @@ -1865,10 +1992,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; @@ -2001,7 +2127,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) { @@ -2335,7 +2461,7 @@ static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready) { int r; - int i = 0; + unsigned int i = 0; usbi_mutex_lock(&ctx->open_devs_lock); for (i = 0; i < nfds && num_ready > 0; i++) { @@ -2359,7 +2485,9 @@ static int op_handle_events(struct libusb_context *ctx, continue; } - r = reap_for_handle(handle); + do { + r = reap_for_handle(handle); + } while (r == 0); if (r == 1 || r == LIBUSB_ERROR_NO_DEVICE) continue; else if (r < 0) @@ -2394,12 +2522,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 = NULL, - .get_device_list = op_get_device_list, + .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, @@ -2435,4 +2566,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/third_party/libusb/src/libusb/os/linux_usbfs.h b/third_party/libusb/src/libusb/os/linux_usbfs.h index 487acb5..499bab7 100644 --- a/third_party/libusb/src/libusb/os/linux_usbfs.h +++ b/third_party/libusb/src/libusb/os/linux_usbfs.h @@ -1,7 +1,7 @@ /* * usbfs header structures - * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org> - * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com> + * Copyright © 2007 Daniel Drake <dsd@gentoo.org> + * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,6 +21,8 @@ #ifndef LIBUSB_USBFS_H #define LIBUSB_USBFS_H +#include <linux/types.h> + #define SYSFS_DEVICE_PATH "/sys/bus/usb/devices" struct usbfs_ctrltransfer { @@ -116,6 +118,20 @@ struct usbfs_hub_portinfo { unsigned char port[127]; /* port to device num mapping */ }; +#define USBFS_CAP_ZERO_PACKET 0x01 +#define USBFS_CAP_BULK_CONTINUATION 0x02 +#define USBFS_CAP_NO_PACKET_SIZE_LIM 0x04 +#define USBFS_CAP_BULK_SCATTER_GATHER 0x08 + +#define USBFS_DISCONNECT_CLAIM_IF_DRIVER 0x01 +#define USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER 0x02 + +struct usbfs_disconnect_claim { + unsigned int interface; + unsigned int flags; + char driver[USBFS_MAXDRIVERNAME + 1]; +}; + #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) @@ -135,5 +151,31 @@ struct usbfs_hub_portinfo { #define IOCTL_USBFS_CLEAR_HALT _IOR('U', 21, unsigned int) #define IOCTL_USBFS_DISCONNECT _IO('U', 22) #define IOCTL_USBFS_CONNECT _IO('U', 23) +#define IOCTL_USBFS_CLAIM_PORT _IOR('U', 24, unsigned int) +#define IOCTL_USBFS_RELEASE_PORT _IOR('U', 25, unsigned int) +#define IOCTL_USBFS_GET_CAPABILITIES _IOR('U', 26, __u32) +#define IOCTL_USBFS_DISCONNECT_CLAIM _IOR('U', 27, struct usbfs_disconnect_claim) + +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); + +int linux_get_device_address (struct libusb_context *ctx, int detached, + uint8_t *busnum, uint8_t *devaddr, const char *dev_node, + const char *sys_name); +int linux_enumerate_device(struct libusb_context *ctx, + uint8_t busnum, uint8_t devaddr, const char *sysfs_dir); #endif diff --git a/third_party/libusb/src/libusb/os/openbsd_usb.c b/third_party/libusb/src/libusb/os/openbsd_usb.c index e31941b..f4fd454 100644 --- a/third_party/libusb/src/libusb/os/openbsd_usb.c +++ b/third_party/libusb/src/libusb/os/openbsd_usb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org> + * Copyright © 2011 Martin Pieuchot <mpi@openbsd.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -89,15 +89,18 @@ static int _access_endpoint(struct libusb_transfer *); 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, @@ -158,7 +161,9 @@ obsd_get_device_list(struct libusb_context * ctx, session_id = (di.udi_bus << 8 | di.udi_addr); dev = usbi_get_device_by_session_id(ctx, session_id); - if (dev == NULL) { + if (dev) { + dev = libusb_ref_device(dev); + } else { dev = usbi_alloc_device(ctx, session_id); if (dev == NULL) return (LIBUSB_ERROR_NO_MEM); @@ -189,6 +194,8 @@ obsd_get_device_list(struct libusb_context * ctx, if (discovered_devs_append(*discdevs, dev) == NULL) return (LIBUSB_ERROR_NO_MEM); + + libusb_unref_device(dev); } return (LIBUSB_SUCCESS); @@ -268,7 +275,7 @@ obsd_get_active_config_descriptor(struct libusb_device *dev, *host_endian = 0; - return (LIBUSB_SUCCESS); + return len; } int @@ -306,7 +313,7 @@ obsd_get_config_descriptor(struct libusb_device *dev, uint8_t idx, *host_endian = 0; - return (LIBUSB_SUCCESS); + return len; } int @@ -634,7 +641,7 @@ _sync_control_transfer(struct usbi_transfer *itransfer) 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; diff --git a/third_party/libusb/src/libusb/os/poll_posix.c b/third_party/libusb/src/libusb/os/poll_posix.c new file mode 100644 index 0000000..eeaf5dc --- /dev/null +++ b/third_party/libusb/src/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 <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> + +#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/third_party/libusb/src/libusb/os/poll_posix.h b/third_party/libusb/src/libusb/os/poll_posix.h index 0e5e7f5b..64c9bb7 100644 --- a/third_party/libusb/src/libusb/os/poll_posix.h +++ b/third_party/libusb/src/libusb/os/poll_posix.h @@ -1,10 +1,27 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #ifndef LIBUSB_POLL_POSIX_H #define LIBUSB_POLL_POSIX_H #define usbi_write write #define usbi_read read #define usbi_close close -#define usbi_pipe pipe #define usbi_poll poll +int usbi_pipe(int pipefd[2]); + #endif /* LIBUSB_POLL_POSIX_H */ diff --git a/third_party/libusb/src/libusb/os/poll_windows.c b/third_party/libusb/src/libusb/os/poll_windows.c index 7f4d9c4..7ed19ba 100644 --- a/third_party/libusb/src/libusb/os/poll_windows.c +++ b/third_party/libusb/src/libusb/os/poll_windows.c @@ -1,6 +1,7 @@ /* * poll_windows: poll compatibility wrapper for Windows - * Copyright (C) 2009-2010 Pete Batard <pbatard@gmail.com> + * Copyright © 2012-2013 RealVNC Ltd. + * Copyright © 2009-2010 Pete Batard <pete@akeo.ie> * 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 <errno.h> -#include <fcntl.h> #include <stdio.h> #include <stdlib.h> -#include <io.h> -#include <libusbi.h> +#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; i<MAX_FDS; i++) { poll_fd[i] = INVALID_WINFD; _poll_fd[i].original_handle = INVALID_HANDLE_VALUE; @@ -139,15 +167,15 @@ void init_polling(void) } is_polling_set = TRUE; } - compat_spinlock = 0; + InterlockedExchange((LONG *)&compat_spinlock, 0); } // Internal function to retrieve the table index (and lock the fd mutex) -int _fd_to_index_and_lock(int fd) +static int _fd_to_index_and_lock(int fd) { int i; - if (fd <= 0) + if (fd < 0) return -1; for (i=0; i<MAX_FDS; i++) { @@ -164,7 +192,7 @@ int _fd_to_index_and_lock(int fd) return -1; } -OVERLAPPED *create_overlapped(void) +static OVERLAPPED *create_overlapped(void) { OVERLAPPED *overlapped = (OVERLAPPED*) calloc(1, sizeof(OVERLAPPED)); if (overlapped == NULL) { @@ -178,7 +206,7 @@ OVERLAPPED *create_overlapped(void) return overlapped; } -void free_overlapped(OVERLAPPED *overlapped) +static void free_overlapped(OVERLAPPED *overlapped) { if (overlapped == NULL) return; @@ -190,20 +218,6 @@ void free_overlapped(OVERLAPPED *overlapped) free(overlapped); } -void reset_overlapped(OVERLAPPED *overlapped) -{ - HANDLE event_handle; - if (overlapped == NULL) - return; - - event_handle = overlapped->hEvent; - 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; i<MAX_FDS; i++) { if (poll_fd[i].fd < 0) { EnterCriticalSection(&_poll_fd[i].mutex); @@ -286,7 +281,13 @@ int usbi_pipe(int filedes[2]) continue; } - poll_fd[i].fd = filedes[0]; + // Use index as the unique fd number + poll_fd[i].fd = i; + // Read end of the "pipe" + filedes[0] = poll_fd[i].fd; + // We can use the same handle for both ends + filedes[1] = filedes[0]; + poll_fd[i].handle = DUMMY_HANDLE; poll_fd[i].overlapped = overlapped; // There's no polling on the write end, so we just use READ for our needs @@ -296,12 +297,7 @@ int usbi_pipe(int filedes[2]) return 0; } } - - CloseHandle(overlapped->hEvent); -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); } @@ -651,15 +638,7 @@ int usbi_close(int fd) if (_index < 0) { errno = EBADF; } else { - if (poll_fd[_index].overlapped != NULL) { - // Must be a different event for each end of the pipe - CloseHandle(poll_fd[_index].overlapped->hEvent); - 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/third_party/libusb/src/libusb/os/poll_windows.h b/third_party/libusb/src/libusb/os/poll_windows.h index 6e5bf2b..deed206 100644 --- a/third_party/libusb/src/libusb/os/poll_windows.h +++ b/third_party/libusb/src/libusb/os/poll_windows.h @@ -1,6 +1,7 @@ /* * Windows compat: POSIX compatibility wrapper - * Copyright (C) 2009-2010 Pete Batard <pbatard@gmail.com> + * Copyright © 2012-2013 RealVNC Ltd. + * Copyright © 2009-2010 Pete Batard <pete@akeo.ie> * With contributions from Michael Plante, Orin Eman et al. * Parts of poll implementation from libusb-win32, by Stephan Meyer et al. * @@ -31,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, @@ -66,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; @@ -82,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); @@ -112,4 +123,3 @@ do { \ } \ } while (0) #endif - diff --git a/third_party/libusb/src/libusb/os/threads_posix.c b/third_party/libusb/src/libusb/os/threads_posix.c index 60c57cf..46f6db7 100644 --- a/third_party/libusb/src/libusb/os/threads_posix.c +++ b/third_party/libusb/src/libusb/os/threads_posix.c @@ -1,8 +1,8 @@ /* - * libusb synchronization using POSIX Threads + * libusbx synchronization using POSIX Threads * - * Copyright (C) 2011 Vitali Lovich <vlovich@aliph.com> - * Copyright (C) 2011 Peter Stuge <peter@stuge.se> + * Copyright © 2011 Vitali Lovich <vlovich@aliph.com> + * Copyright © 2011 Peter Stuge <peter@stuge.se> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,14 +19,14 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifdef _XOPEN_SOURCE -# if _XOPEN_SOURCE < 500 -# undef _XOPEN_SOURCE -# define _XOPEN_SOURCE 500 -# endif -#else -#define _XOPEN_SOURCE 500 -#endif /* _XOPEN_SOURCE */ +#if defined(__linux__) || defined(__OpenBSD__) +# include <unistd.h> +# include <sys/syscall.h> +#elif defined(__APPLE__) +# include <mach/mach.h> +#elif defined(__CYGWIN__) +# include <windows.h> +#endif #include "threads_posix.h" @@ -41,6 +41,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 +54,22 @@ finish: return err; } + +int usbi_get_tid(void) +{ + int ret = -1; +#if 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/third_party/libusb/src/libusb/os/threads_posix.h b/third_party/libusb/src/libusb/os/threads_posix.h index 9752208..0b6a71a 100644 --- a/third_party/libusb/src/libusb/os/threads_posix.h +++ b/third_party/libusb/src/libusb/os/threads_posix.h @@ -1,7 +1,7 @@ /* - * libusb synchronization using POSIX Threads + * libusbx synchronization using POSIX Threads * - * Copyright (C) 2010 Peter Stuge <peter@stuge.se> + * Copyright © 2010 Peter Stuge <peter@stuge.se> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -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/third_party/libusb/src/libusb/os/threads_windows.c b/third_party/libusb/src/libusb/os/threads_windows.c index 1394bb0..cad27e9 100644 --- a/third_party/libusb/src/libusb/os/threads_windows.c +++ b/third_party/libusb/src/libusb/os/threads_windows.c @@ -1,7 +1,7 @@ /* - * libusb synchronization on Microsoft Windows + * libusbx synchronization on Microsoft Windows * - * Copyright (C) 2010 Michael Plante <michael.plante@gmail.com> + * Copyright © 2010 Michael Plante <michael.plante@gmail.com> * * 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,15 +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; } @@ -128,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; @@ -181,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; @@ -205,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/third_party/libusb/src/libusb/os/threads_windows.h b/third_party/libusb/src/libusb/os/threads_windows.h index 7bb144a..df8a0ee 100644 --- a/third_party/libusb/src/libusb/os/threads_windows.h +++ b/third_party/libusb/src/libusb/os/threads_windows.h @@ -1,7 +1,7 @@ /* - * libusb synchronization on Microsoft Windows + * libusbx synchronization on Microsoft Windows * - * Copyright (C) 2010 Michael Plante <michael.plante@gmail.com> + * Copyright © 2010 Michael Plante <michael.plante@gmail.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -82,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/third_party/libusb/src/libusb/os/wince_usb.c b/third_party/libusb/src/libusb/os/wince_usb.c new file mode 100644 index 0000000..e4a6633 --- /dev/null +++ b/third_party/libusb/src/libusb/os/wince_usb.c @@ -0,0 +1,1015 @@ +/* + * 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 <pbatard@gmail.com> + * 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 <libusbi.h> + +#include <stdint.h> +#include <errno.h> +#include <inttypes.h> + +#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; + } + } + // 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); + } + + // 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 %d: %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/third_party/libusb/src/libusb/os/wince_usb.h b/third_party/libusb/src/libusb/os/wince_usb.h new file mode 100644 index 0000000..3db9693 --- /dev/null +++ b/third_party/libusb/src/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 <pbatard@gmail.com> + * 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 <windows.h> +#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/third_party/libusb/src/libusb/os/windows_common.h b/third_party/libusb/src/libusb/os/windows_common.h new file mode 100644 index 0000000..1da72bd --- /dev/null +++ b/third_party/libusb/src/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 <pete@akeo.ie> + * 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)?"<NULL>":str1), ((str2==NULL)?"<NULL>":str2)) +#define safe_stricmp(str1, str2) _stricmp(((str1==NULL)?"<NULL>":str1), ((str2==NULL)?"<NULL>":str2)) +#define safe_strncmp(str1, str2, count) strncmp(((str1==NULL)?"<NULL>":str1), ((str2==NULL)?"<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/third_party/libusb/src/libusb/os/windows_usb.c b/third_party/libusb/src/libusb/os/windows_usb.c index 0739c19..51ce55d 100644 --- a/third_party/libusb/src/libusb/os/windows_usb.c +++ b/third_party/libusb/src/libusb/os/windows_usb.c @@ -1,8 +1,9 @@ /* - * windows backend for libusb 1.0 - * Copyright (c) 2009-2010 Pete Batard <pbatard@gmail.com> + * windows backend for libusbx 1.0 + * Copyright © 2009-2012 Pete Batard <pete@akeo.ie> * 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 * @@ -33,57 +34,67 @@ #include <objbase.h> #include <winioctl.h> -#include <libusbi.h> +#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 @@ -102,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)) { @@ -112,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]; @@ -130,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)], @@ -154,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. @@ -185,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); @@ -222,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; @@ -241,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); @@ -344,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) */ @@ -393,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; @@ -435,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; @@ -498,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); @@ -533,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; @@ -568,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; } @@ -580,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; i<usb_api_backend[api].nb_driver_names; i++) { - if (safe_strcmp(tok, usb_api_backend[api].driver_name_list[i]) == 0) { + if (safe_stricmp(tok, usb_api_backend[api].driver_name_list[i]) == 0) { free(tmp_str); - return true; + return i; } } tok = strtok(NULL, sep_str); } free (tmp_str); - return false; + return SUB_API_NOTSET; } /* @@ -626,6 +725,14 @@ static int auto_claim(struct libusb_transfer *transfer, int *interface_number, i int current_interface = *interface_number; int r = LIBUSB_SUCCESS; + switch(api_type) { + case USB_API_WINUSBX: + case USB_API_HID: + break; + default: + return LIBUSB_ERROR_INVALID_PARAM; + } + usbi_mutex_lock(&autoclaim_lock); if (current_interface < 0) // No serviceable interface was found { @@ -684,10 +791,10 @@ static void auto_release(struct usbi_transfer *itransfer) } /* - * init: libusb backend init function + * init: libusbx backend init function * * This function enumerates the HCDs (Host Controller Drivers) and populates our private HCD list - * In our implementation, we equate Windows' "HCD" to LibUSB's "bus". Note that bus is zero indexed. + * In our implementation, we equate Windows' "HCD" to libusbx's "bus". Note that bus is zero indexed. * HCDs are not expected to change after init (might not hold true for hot pluggable USB PCI card?) */ static int windows_init(struct libusb_context *ctx) @@ -748,7 +855,7 @@ static int windows_init(struct libusb_context *ctx) // Initialize the low level APIs (we don't care about errors at this stage) for (i=0; i<USB_API_MAX; i++) { - usb_api_backend[i].init(ctx); + usb_api_backend[i].init(SUB_API_NOTSET, ctx); } // Because QueryPerformanceCounter might report different values when @@ -874,7 +981,7 @@ static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle if (dev->num_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; i<dev->num_configurations; i++) @@ -899,15 +1006,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); } @@ -948,7 +1056,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); @@ -957,7 +1065,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) @@ -999,8 +1107,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) { @@ -1042,7 +1152,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; @@ -1053,10 +1166,12 @@ 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); } + usbi_sanitize_device(dev); + usbi_dbg("(bus: %d, addr: %d, depth: %d, port: %d): '%s'", dev->bus_number, dev->device_address, priv->depth, priv->port, device_id); @@ -1064,8 +1179,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] = { @@ -1075,8 +1190,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, @@ -1094,7 +1211,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) { @@ -1104,21 +1220,22 @@ static uint8_t get_api_type(struct libusb_context *ctx, } } - for (api=1; api<USB_API_MAX; api++) { + for (i=1; i<USB_API_MAX; i++) { for (k=0; k<3; k++) { - if (is_api_driver(lookup[k].list, api)) { - usbi_dbg("matched %s name against %s", lookup[k].designation, usb_api_backend[api].designation); - break; + j = get_sub_api(lookup[k].list, i); + if (j >= 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); @@ -1148,36 +1265,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<<api; + priv->usb_interface[interface_number].sub_api = sub_api; + if ((api == USB_API_HID) && (priv->hid == NULL)) { + priv->hid = (struct hid_device_priv*) calloc(1, sizeof(struct hid_device_priv)); + if (priv->hid == NULL) + return LIBUSB_ERROR_NO_MEM; + } return LIBUSB_SUCCESS; } +static int set_hid_interface(struct libusb_context* ctx, struct libusb_device* dev, + char* dev_interface_path) +{ + int i; + struct windows_device_priv *priv = _device_priv(dev); + + if (priv->hid == NULL) { + usbi_err(ctx, "program assertion failed: parent is not HID"); + return LIBUSB_ERROR_NO_DEVICE; + } + if (priv->hid->nb_interfaces == USB_MAXINTERFACES) { + usbi_err(ctx, "program assertion failed: max USB interfaces reached for HID device"); + return LIBUSB_ERROR_NO_DEVICE; + } + for (i=0; i<priv->hid->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]; @@ -1187,12 +1346,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; @@ -1203,14 +1360,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) { @@ -1220,23 +1380,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 @@ -1269,13 +1415,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 @@ -1309,6 +1457,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; @@ -1318,7 +1467,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); @@ -1340,6 +1489,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, @@ -1352,7 +1504,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; } @@ -1373,7 +1525,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); @@ -1405,7 +1557,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); @@ -1425,6 +1577,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; @@ -1436,10 +1589,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); @@ -1471,10 +1632,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; @@ -1492,7 +1658,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]); } @@ -1506,7 +1672,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) { @@ -1530,7 +1696,7 @@ static void windows_exit(void) // Only works if exits and inits are balanced exactly if (--concurrent_usage < 0) { // Last exit for (i=0; i<USB_API_MAX; i++) { - usb_api_backend[i].exit(); + usb_api_backend[i].exit(SUB_API_NOTSET); } exit_polling(); @@ -1591,8 +1757,9 @@ static int windows_get_config_descriptor(struct libusb_device *dev, uint8_t conf size = min(config_header->wTotalLength, len); memcpy(buffer, priv->config_descriptor[config_index], size); + *host_endian = 0; - return LIBUSB_SUCCESS; + return size; } /* @@ -1619,14 +1786,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) @@ -1677,7 +1844,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); @@ -1694,7 +1861,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); @@ -1707,19 +1874,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 @@ -1747,7 +1914,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); } @@ -1760,7 +1928,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; } @@ -1768,6 +1936,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, (short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT)); + itransfer->flags |= USBI_TRANSFER_UPDATED_FDS; return LIBUSB_SUCCESS; } @@ -1779,7 +1948,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; } @@ -1787,6 +1956,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, (short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT)); + itransfer->flags |= USBI_TRANSFER_UPDATED_FDS; return LIBUSB_SUCCESS; } @@ -1798,13 +1968,14 @@ 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; } usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN); + itransfer->flags |= USBI_TRANSFER_UPDATED_FDS; return LIBUSB_SUCCESS; } @@ -1835,7 +2006,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) @@ -1843,7 +2014,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) @@ -1867,13 +2038,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"); @@ -1884,6 +2055,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; @@ -1893,7 +2068,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; } @@ -1967,6 +2142,7 @@ static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, windows_handle_callback(transfer, io_result, io_size); } else { usbi_err(ctx, "could not find a matching transfer for fd %x", fds[i]); + usbi_mutex_unlock(&ctx->open_devs_lock); return LIBUSB_ERROR_NOT_FOUND; } } @@ -2039,8 +2215,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) @@ -2092,16 +2266,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, @@ -2138,67 +2315,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, @@ -2213,13 +2396,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, @@ -2234,13 +2417,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, @@ -2253,58 +2436,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; i<SUB_API_MAX; i++) { + WinUSBX_Set(AbortPipe); + WinUSBX_Set(ControlTransfer); + WinUSBX_Set(FlushPipe); + WinUSBX_Set(Free); + WinUSBX_Set(GetAssociatedInterface); + WinUSBX_Set(GetCurrentAlternateSetting); + WinUSBX_Set(GetDescriptor); + WinUSBX_Set(GetOverlappedResult); + WinUSBX_Set(GetPipePolicy); + WinUSBX_Set(GetPowerPolicy); + WinUSBX_Set(Initialize); + WinUSBX_Set(QueryDeviceInformation); + WinUSBX_Set(QueryInterfaceSettings); + WinUSBX_Set(QueryPipe); + WinUSBX_Set(ReadPipe); + WinUSBX_Set(ResetPipe); + WinUSBX_Set(SetCurrentAlternateSetting); + WinUSBX_Set(SetPipePolicy); + WinUSBX_Set(SetPowerPolicy); + WinUSBX_Set(WritePipe); + if (!native_winusb) { + WinUSBX_Set(ResetDevice); + } + if (WinUSBX[i].Initialize != NULL) { + WinUSBX[i].initialized = true; + usbi_dbg("initalized sub API %s", sub_api_name[i]); + } else { + usbi_warn(ctx, "Failed to initalize sub API %s", sub_api_name[i]); + WinUSBX[i].initialized = false; + } + } return LIBUSB_SUCCESS; } -static int winusb_exit(void) +static int winusbx_exit(int sub_api) { return LIBUSB_SUCCESS; } @@ -2312,7 +2568,7 @@ static int winusb_exit(void) // 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 winusb_open(struct libusb_device_handle *dev_handle) +static int winusbx_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); @@ -2321,12 +2577,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) { @@ -2347,18 +2603,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); @@ -2367,7 +2625,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); @@ -2377,32 +2635,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; i<priv->usb_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); } @@ -2411,18 +2673,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; @@ -2430,35 +2700,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()) { @@ -2480,19 +2779,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; @@ -2501,16 +2801,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; i<USB_MAXINTERFACES; i++) { if ( (handle_priv->interface_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; } } @@ -2540,7 +2847,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); @@ -2554,7 +2861,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; @@ -2562,9 +2869,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; } } @@ -2572,7 +2879,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; @@ -2583,16 +2890,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 { @@ -2608,13 +2915,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; @@ -2626,15 +2934,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); @@ -2646,7 +2954,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; @@ -2660,7 +2968,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; @@ -2668,15 +2976,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 { @@ -2690,7 +2998,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); @@ -2698,7 +3006,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) { @@ -2709,8 +3017,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; } @@ -2723,22 +3031,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)) { @@ -2749,8 +3058,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; } @@ -2765,8 +3074,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); @@ -2775,7 +3084,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; i<USB_MAXINTERFACES; i++) { @@ -2784,124 +3093,1205 @@ static int winusb_reset_device(struct libusb_device_handle *dev_handle) { // Cancel any pollable I/O usbi_remove_pollfd(ctx, wfd.fd); - usbi_free_fd(wfd.fd); + usbi_free_fd(&wfd); wfd = handle_to_winfd(winusb_handle); } if ( (winusb_handle != 0) && (winusb_handle != INVALID_HANDLE_VALUE)) { for (j=0; j<priv->usb_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; } +/* + * Internal HID Support functions (from libusb-win32) + * Note that functions that complete data transfer synchronously must return + * LIBUSB_COMPLETED instead of LIBUSB_SUCCESS + */ +static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size); +static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size); + +static int _hid_wcslen(WCHAR *str) +{ + int i = 0; + while (str[i] && (str[i] != 0x409)) { + i++; + } + return i; +} + +static int _hid_get_device_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + struct libusb_device_descriptor d; + + d.bLength = LIBUSB_DT_DEVICE_SIZE; + d.bDescriptorType = LIBUSB_DT_DEVICE; + d.bcdUSB = 0x0200; /* 2.00 */ + d.bDeviceClass = 0; + d.bDeviceSubClass = 0; + d.bDeviceProtocol = 0; + d.bMaxPacketSize0 = 64; /* fix this! */ + d.idVendor = (uint16_t)dev->vid; + d.idProduct = (uint16_t)dev->pid; + d.bcdDevice = 0x0100; + d.iManufacturer = dev->string_index[0]; + d.iProduct = dev->string_index[1]; + d.iSerialNumber = dev->string_index[2]; + d.bNumConfigurations = 1; + + if (*size > LIBUSB_DT_DEVICE_SIZE) + *size = LIBUSB_DT_DEVICE_SIZE; + memcpy(data, &d, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_config_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + char num_endpoints = 0; + size_t config_total_len = 0; + char tmp[HID_MAX_CONFIG_DESC_SIZE]; + struct libusb_config_descriptor *cd; + struct libusb_interface_descriptor *id; + struct libusb_hid_descriptor *hd; + struct libusb_endpoint_descriptor *ed; + size_t tmp_size; + + if (dev->input_report_size) + num_endpoints++; + if (dev->output_report_size) + num_endpoints++; + + config_total_len = LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE + + LIBUSB_DT_HID_SIZE + num_endpoints * LIBUSB_DT_ENDPOINT_SIZE; + + + cd = (struct libusb_config_descriptor *)tmp; + id = (struct libusb_interface_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE); + hd = (struct libusb_hid_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE + + LIBUSB_DT_INTERFACE_SIZE); + ed = (struct libusb_endpoint_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE + + LIBUSB_DT_INTERFACE_SIZE + + LIBUSB_DT_HID_SIZE); + + cd->bLength = LIBUSB_DT_CONFIG_SIZE; + cd->bDescriptorType = LIBUSB_DT_CONFIG; + cd->wTotalLength = (uint16_t) config_total_len; + cd->bNumInterfaces = 1; + cd->bConfigurationValue = 1; + cd->iConfiguration = 0; + cd->bmAttributes = 1 << 7; /* bus powered */ + cd->MaxPower = 50; + + id->bLength = LIBUSB_DT_INTERFACE_SIZE; + id->bDescriptorType = LIBUSB_DT_INTERFACE; + id->bInterfaceNumber = 0; + id->bAlternateSetting = 0; + id->bNumEndpoints = num_endpoints; + id->bInterfaceClass = 3; + id->bInterfaceSubClass = 0; + id->bInterfaceProtocol = 0; + id->iInterface = 0; + + tmp_size = LIBUSB_DT_HID_SIZE; + _hid_get_hid_descriptor(dev, hd, &tmp_size); + + if (dev->input_report_size) { + ed->bLength = LIBUSB_DT_ENDPOINT_SIZE; + ed->bDescriptorType = LIBUSB_DT_ENDPOINT; + ed->bEndpointAddress = HID_IN_EP; + ed->bmAttributes = 3; + ed->wMaxPacketSize = dev->input_report_size - 1; + ed->bInterval = 10; + ed = (struct libusb_endpoint_descriptor *)((char*)ed + LIBUSB_DT_ENDPOINT_SIZE); + } + + if (dev->output_report_size) { + ed->bLength = LIBUSB_DT_ENDPOINT_SIZE; + ed->bDescriptorType = LIBUSB_DT_ENDPOINT; + ed->bEndpointAddress = HID_OUT_EP; + ed->bmAttributes = 3; + ed->wMaxPacketSize = dev->output_report_size - 1; + ed->bInterval = 10; + } + + if (*size > config_total_len) + *size = config_total_len; + memcpy(data, tmp, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_string_descriptor(struct hid_device_priv* dev, int _index, + void *data, size_t *size) +{ + void *tmp = NULL; + size_t tmp_size = 0; + int i; + + /* language ID, EN-US */ + char string_langid[] = { + 0x09, + 0x04 + }; + + if ((*size < 2) || (*size > 255)) { + return LIBUSB_ERROR_OVERFLOW; + } + + if (_index == 0) { + tmp = string_langid; + tmp_size = sizeof(string_langid)+2; + } else { + for (i=0; i<3; i++) { + if (_index == (dev->string_index[i])) { + tmp = dev->string[i]; + tmp_size = (_hid_wcslen(dev->string[i])+1) * sizeof(WCHAR); + break; + } + } + if (i == 3) { // not found + return LIBUSB_ERROR_INVALID_PARAM; + } + } + + if(!tmp_size) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (tmp_size < *size) { + *size = tmp_size; + } + // 2 byte header + ((uint8_t*)data)[0] = (uint8_t)*size; + ((uint8_t*)data)[1] = LIBUSB_DT_STRING; + memcpy((uint8_t*)data+2, tmp, *size-2); + return LIBUSB_COMPLETED; +} + +static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + struct libusb_hid_descriptor d; + uint8_t tmp[MAX_HID_DESCRIPTOR_SIZE]; + size_t report_len = MAX_HID_DESCRIPTOR_SIZE; + + _hid_get_report_descriptor(dev, tmp, &report_len); + + d.bLength = LIBUSB_DT_HID_SIZE; + d.bDescriptorType = LIBUSB_DT_HID; + d.bcdHID = 0x0110; /* 1.10 */ + d.bCountryCode = 0; + d.bNumDescriptors = 1; + d.bClassDescriptorType = LIBUSB_DT_REPORT; + d.wClassDescriptorLength = (uint16_t)report_len; + + if (*size > LIBUSB_DT_HID_SIZE) + *size = LIBUSB_DT_HID_SIZE; + memcpy(data, &d, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size) +{ + uint8_t d[MAX_HID_DESCRIPTOR_SIZE]; + size_t i = 0; + + /* usage page (0xFFA0 == vendor defined) */ + d[i++] = 0x06; d[i++] = 0xA0; d[i++] = 0xFF; + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x01; + /* start collection (application) */ + d[i++] = 0xA1; d[i++] = 0x01; + /* input report */ + if (dev->input_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x01; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->input_report_size - 1; + /* input (data, variable, absolute) */ + d[i++] = 0x81; d[i++] = 0x00; + } + /* output report */ + if (dev->output_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x02; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->output_report_size - 1; + /* output (data, variable, absolute) */ + d[i++] = 0x91; d[i++] = 0x00; + } + /* feature report */ + if (dev->feature_report_size) { + /* usage (vendor defined) */ + d[i++] = 0x09; d[i++] = 0x03; + /* logical minimum (0) */ + d[i++] = 0x15; d[i++] = 0x00; + /* logical maximum (255) */ + d[i++] = 0x25; d[i++] = 0xFF; + /* report size (8 bits) */ + d[i++] = 0x75; d[i++] = 0x08; + /* report count */ + d[i++] = 0x95; d[i++] = (uint8_t)dev->feature_report_size - 1; + /* feature (data, variable, absolute) */ + d[i++] = 0xb2; d[i++] = 0x02; d[i++] = 0x01; + } + + /* end collection */ + d[i++] = 0xC0; + + if (*size > i) + *size = i; + memcpy(data, d, *size); + return LIBUSB_COMPLETED; +} + +static int _hid_get_descriptor(struct hid_device_priv* dev, HANDLE hid_handle, int recipient, + int type, int _index, void *data, size_t *size) +{ + switch(type) { + case LIBUSB_DT_DEVICE: + usbi_dbg("LIBUSB_DT_DEVICE"); + return _hid_get_device_descriptor(dev, data, size); + case LIBUSB_DT_CONFIG: + usbi_dbg("LIBUSB_DT_CONFIG"); + if (!_index) + return _hid_get_config_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_STRING: + usbi_dbg("LIBUSB_DT_STRING"); + return _hid_get_string_descriptor(dev, _index, data, size); + case LIBUSB_DT_HID: + usbi_dbg("LIBUSB_DT_HID"); + if (!_index) + return _hid_get_hid_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_REPORT: + usbi_dbg("LIBUSB_DT_REPORT"); + if (!_index) + return _hid_get_report_descriptor(dev, data, size); + return LIBUSB_ERROR_INVALID_PARAM; + case LIBUSB_DT_PHYSICAL: + usbi_dbg("LIBUSB_DT_PHYSICAL"); + if (HidD_GetPhysicalDescriptor(hid_handle, data, (ULONG)*size)) + return LIBUSB_COMPLETED; + return LIBUSB_ERROR_OTHER; + } + usbi_dbg("unsupported"); + return LIBUSB_ERROR_INVALID_PARAM; +} + +static int _hid_get_report(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data, + struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped, + int report_type) +{ + uint8_t *buf; + DWORD ioctl_code, read_size, expected_size = (DWORD)*size; + int r = LIBUSB_SUCCESS; + + if (tp->hid_buffer != NULL) { + usbi_dbg("program assertion failed: hid_buffer is not NULL"); + } + + if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) { + usbi_dbg("invalid size (%d)", *size); + return LIBUSB_ERROR_INVALID_PARAM; + } + + switch (report_type) { + case HID_REPORT_TYPE_INPUT: + ioctl_code = IOCTL_HID_GET_INPUT_REPORT; + break; + case HID_REPORT_TYPE_FEATURE: + ioctl_code = IOCTL_HID_GET_FEATURE; + break; + default: + usbi_dbg("unknown HID report type %d", report_type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + // Add a trailing byte to detect overflows + buf = (uint8_t*)calloc(expected_size+1, 1); + if (buf == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + buf[0] = (uint8_t)id; // Must be set always + usbi_dbg("report ID: 0x%02X", buf[0]); + + tp->hid_expected_size = expected_size; + read_size = expected_size; + + // NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0) + if (!DeviceIoControl(hid_handle, ioctl_code, buf, expected_size+1, + buf, expected_size+1, &read_size, overlapped)) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_dbg("Failed to Read HID Report: %s", windows_error_str(0)); + safe_free(buf); + return LIBUSB_ERROR_IO; + } + // Asynchronous wait + tp->hid_buffer = buf; + tp->hid_dest = (uint8_t*)data; // copy dest, as not necessarily the start of the transfer buffer + return LIBUSB_SUCCESS; + } + + // Transfer completed synchronously => copy and discard extra buffer + if (read_size == 0) { + usbi_warn(NULL, "program assertion failed - read completed synchronously, but no data was read"); + *size = 0; + } else { + if (buf[0] != id) { + usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id); + } + if ((size_t)read_size > expected_size) { + r = LIBUSB_ERROR_OVERFLOW; + usbi_dbg("OVERFLOW!"); + } else { + r = LIBUSB_COMPLETED; + } + + *size = MIN((size_t)read_size, *size); + if (id == 0) { + // Discard report ID + memcpy(data, buf+1, *size); + } else { + memcpy(data, buf, *size); + } + } + safe_free(buf); + return r; +} + +static int _hid_set_report(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data, + struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped, + int report_type) +{ + uint8_t *buf = NULL; + DWORD ioctl_code, write_size= (DWORD)*size; + + if (tp->hid_buffer != NULL) { + usbi_dbg("program assertion failed: hid_buffer is not NULL"); + } + + if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) { + usbi_dbg("invalid size (%d)", *size); + return LIBUSB_ERROR_INVALID_PARAM; + } + + switch (report_type) { + case HID_REPORT_TYPE_OUTPUT: + ioctl_code = IOCTL_HID_SET_OUTPUT_REPORT; + break; + case HID_REPORT_TYPE_FEATURE: + ioctl_code = IOCTL_HID_SET_FEATURE; + break; + default: + usbi_dbg("unknown HID report type %d", report_type); + return LIBUSB_ERROR_INVALID_PARAM; + } + + usbi_dbg("report ID: 0x%02X", id); + // When report IDs are not used (i.e. when id == 0), we must add + // a null report ID. Otherwise, we just use original data buffer + if (id == 0) { + write_size++; + } + buf = (uint8_t*) malloc(write_size); + if (buf == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + if (id == 0) { + buf[0] = 0; + memcpy(buf + 1, data, *size); + } else { + // This seems like a waste, but if we don't duplicate the + // data, we'll get issues when freeing hid_buffer + memcpy(buf, data, *size); + if (buf[0] != id) { + usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id); + } + } + + // NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0) + if (!DeviceIoControl(hid_handle, ioctl_code, buf, write_size, + buf, write_size, &write_size, overlapped)) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_dbg("Failed to Write HID Output Report: %s", windows_error_str(0)); + safe_free(buf); + return LIBUSB_ERROR_IO; + } + tp->hid_buffer = buf; + tp->hid_dest = NULL; + return LIBUSB_SUCCESS; + } + + // Transfer completed synchronously + *size = write_size; + if (write_size == 0) { + usbi_dbg("program assertion failed - write completed synchronously, but no data was written"); + } + safe_free(buf); + return LIBUSB_COMPLETED; +} + +static int _hid_class_request(struct hid_device_priv* dev, HANDLE hid_handle, int request_type, + int request, int value, int _index, void *data, struct windows_transfer_priv *tp, + size_t *size, OVERLAPPED* overlapped) +{ + int report_type = (value >> 8) & 0xFF; + int report_id = value & 0xFF; + + if ( (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_INTERFACE) + && (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_DEVICE) ) + return LIBUSB_ERROR_INVALID_PARAM; + + if (LIBUSB_REQ_OUT(request_type) && request == HID_REQ_SET_REPORT) + return _hid_set_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type); + + if (LIBUSB_REQ_IN(request_type) && request == HID_REQ_GET_REPORT) + return _hid_get_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type); + + return LIBUSB_ERROR_INVALID_PARAM; +} + + +/* + * HID API functions + */ +static int hid_init(int sub_api, struct libusb_context *ctx) +{ + DLL_LOAD(hid.dll, HidD_GetAttributes, TRUE); + DLL_LOAD(hid.dll, HidD_GetHidGuid, TRUE); + DLL_LOAD(hid.dll, HidD_GetPreparsedData, TRUE); + DLL_LOAD(hid.dll, HidD_FreePreparsedData, TRUE); + DLL_LOAD(hid.dll, HidD_GetManufacturerString, TRUE); + DLL_LOAD(hid.dll, HidD_GetProductString, TRUE); + DLL_LOAD(hid.dll, HidD_GetSerialNumberString, TRUE); + DLL_LOAD(hid.dll, HidP_GetCaps, TRUE); + DLL_LOAD(hid.dll, HidD_SetNumInputBuffers, TRUE); + DLL_LOAD(hid.dll, HidD_SetFeature, TRUE); + DLL_LOAD(hid.dll, HidD_GetFeature, TRUE); + DLL_LOAD(hid.dll, HidD_GetPhysicalDescriptor, TRUE); + DLL_LOAD(hid.dll, HidD_GetInputReport, FALSE); + DLL_LOAD(hid.dll, HidD_SetOutputReport, FALSE); + DLL_LOAD(hid.dll, HidD_FlushQueue, TRUE); + DLL_LOAD(hid.dll, HidP_GetValueCaps, TRUE); + + api_hid_available = true; + return LIBUSB_SUCCESS; +} + +static int hid_exit(int sub_api) +{ + return LIBUSB_SUCCESS; +} + +// NB: open and close must ensure that they only handle interface of +// the right API type, as these functions can be called wholesale from +// composite_open(), with interfaces belonging to different APIs +static int hid_open(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + + HIDD_ATTRIBUTES hid_attributes; + PHIDP_PREPARSED_DATA preparsed_data = NULL; + HIDP_CAPS capabilities; + HIDP_VALUE_CAPS *value_caps; + + HANDLE hid_handle = INVALID_HANDLE_VALUE; + int i, j; + // report IDs handling + ULONG size[3]; + const char* type[3] = {"input", "output", "feature"}; + int nb_ids[2]; // zero and nonzero report IDs + + CHECK_HID_AVAILABLE; + if (priv->hid == NULL) { + usbi_err(ctx, "program assertion failed - private HID structure is unitialized"); + return LIBUSB_ERROR_NOT_FOUND; + } + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if ( (priv->usb_interface[i].path != NULL) + && (priv->usb_interface[i].apib->id == USB_API_HID) ) { + hid_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + /* + * http://www.lvr.com/hidfaq.htm: Why do I receive "Access denied" when attempting to access my HID? + * "Windows 2000 and later have exclusive read/write access to HIDs that are configured as a system + * keyboards or mice. An application can obtain a handle to a system keyboard or mouse by not + * requesting READ or WRITE access with CreateFile. Applications can then use HidD_SetFeature and + * HidD_GetFeature (if the device supports Feature reports)." + */ + if (hid_handle == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "could not open HID device in R/W mode (keyboard or mouse?) - trying without"); + hid_handle = CreateFileA(priv->usb_interface[i].path, 0, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + if (hid_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not open device %s (interface %d): %s", priv->path, i, windows_error_str(0)); + switch(GetLastError()) { + case ERROR_FILE_NOT_FOUND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ACCESS_DENIED: + return LIBUSB_ERROR_ACCESS; + default: + return LIBUSB_ERROR_IO; + } + } + priv->usb_interface[i].restricted_functionality = true; + } + handle_priv->interface_handle[i].api_handle = hid_handle; + } + } + + hid_attributes.Size = sizeof(hid_attributes); + do { + if (!HidD_GetAttributes(hid_handle, &hid_attributes)) { + usbi_err(ctx, "could not gain access to HID top collection (HidD_GetAttributes)"); + break; + } + + priv->hid->vid = hid_attributes.VendorID; + priv->hid->pid = hid_attributes.ProductID; + + // Set the maximum available input buffer size + for (i=32; HidD_SetNumInputBuffers(hid_handle, i); i*=2); + usbi_dbg("set maximum input buffer size to %d", i/2); + + // Get the maximum input and output report size + if (!HidD_GetPreparsedData(hid_handle, &preparsed_data) || !preparsed_data) { + usbi_err(ctx, "could not read HID preparsed data (HidD_GetPreparsedData)"); + break; + } + if (HidP_GetCaps(preparsed_data, &capabilities) != HIDP_STATUS_SUCCESS) { + usbi_err(ctx, "could not parse HID capabilities (HidP_GetCaps)"); + break; + } + + // Find out if interrupt will need report IDs + size[0] = capabilities.NumberInputValueCaps; + size[1] = capabilities.NumberOutputValueCaps; + size[2] = capabilities.NumberFeatureValueCaps; + for (j=HidP_Input; j<=HidP_Feature; j++) { + usbi_dbg("%d HID %s report value(s) found", size[j], type[j]); + priv->hid->uses_report_ids[j] = false; + if (size[j] > 0) { + value_caps = (HIDP_VALUE_CAPS*) calloc(size[j], sizeof(HIDP_VALUE_CAPS)); + if ( (value_caps != NULL) + && (HidP_GetValueCaps((HIDP_REPORT_TYPE)j, value_caps, &size[j], preparsed_data) == HIDP_STATUS_SUCCESS) + && (size[j] >= 1) ) { + nb_ids[0] = 0; + nb_ids[1] = 0; + for (i=0; i<(int)size[j]; i++) { + usbi_dbg(" Report ID: 0x%02X", value_caps[i].ReportID); + if (value_caps[i].ReportID != 0) { + nb_ids[1]++; + } else { + nb_ids[0]++; + } + } + if (nb_ids[1] != 0) { + if (nb_ids[0] != 0) { + usbi_warn(ctx, "program assertion failed: zero and nonzero report IDs used for %s", + type[j]); + } + priv->hid->uses_report_ids[j] = true; + } + } else { + usbi_warn(ctx, " could not process %s report IDs", type[j]); + } + safe_free(value_caps); + } + } + + // Set the report sizes + priv->hid->input_report_size = capabilities.InputReportByteLength; + priv->hid->output_report_size = capabilities.OutputReportByteLength; + priv->hid->feature_report_size = capabilities.FeatureReportByteLength; + + // Fetch string descriptors + priv->hid->string_index[0] = priv->dev_descriptor.iManufacturer; + if (priv->hid->string_index[0] != 0) { + HidD_GetManufacturerString(hid_handle, priv->hid->string[0], + sizeof(priv->hid->string[0])); + } else { + priv->hid->string[0][0] = 0; + } + priv->hid->string_index[1] = priv->dev_descriptor.iProduct; + if (priv->hid->string_index[1] != 0) { + HidD_GetProductString(hid_handle, priv->hid->string[1], + sizeof(priv->hid->string[1])); + } else { + priv->hid->string[1][0] = 0; + } + priv->hid->string_index[2] = priv->dev_descriptor.iSerialNumber; + if (priv->hid->string_index[2] != 0) { + HidD_GetSerialNumberString(hid_handle, priv->hid->string[2], + sizeof(priv->hid->string[2])); + } else { + priv->hid->string[2][0] = 0; + } + } while(0); + + if (preparsed_data) { + HidD_FreePreparsedData(preparsed_data); + } + + return LIBUSB_SUCCESS; +} + +static void hid_close(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + HANDLE file_handle; + int i; + + if (!api_hid_available) + return; + + for (i = 0; i < USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].apib->id == USB_API_HID) { + file_handle = handle_priv->interface_handle[i].api_handle; + if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) { + CloseHandle(file_handle); + } + } + } +} + +static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + // NB: Disconnection detection is not possible in this function + if (priv->usb_interface[iface].path == NULL) { + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + } + + // We use dev_handle as a flag for interface claimed + if (handle_priv->interface_handle[iface].dev_handle == INTERFACE_CLAIMED) { + return LIBUSB_ERROR_BUSY; // already claimed + } + + handle_priv->interface_handle[iface].dev_handle = INTERFACE_CLAIMED; + + usbi_dbg("claimed interface %d", iface); + handle_priv->active_interface = iface; + + return LIBUSB_SUCCESS; +} + +static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + if (priv->usb_interface[iface].path == NULL) { + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + } + + if (handle_priv->interface_handle[iface].dev_handle != INTERFACE_CLAIMED) { + return LIBUSB_ERROR_NOT_FOUND; // invalid iface + } + + handle_priv->interface_handle[iface].dev_handle = INVALID_HANDLE_VALUE; + + return LIBUSB_SUCCESS; +} + +static int hid_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + + CHECK_HID_AVAILABLE; + + if (altsetting > 255) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (altsetting != 0) { + usbi_err(ctx, "set interface altsetting not supported for altsetting >0"); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + return LIBUSB_SUCCESS; +} + +static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *) transfer->buffer; + HANDLE hid_handle; + struct winfd wfd; + int current_interface, config; + size_t size; + int r = LIBUSB_ERROR_INVALID_PARAM; + + CHECK_HID_AVAILABLE; + + transfer_priv->pollable_fd = INVALID_WINFD; + safe_free(transfer_priv->hid_buffer); + transfer_priv->hid_dest = NULL; + size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; + + if (size > MAX_CTRL_BUFFER_LENGTH) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + current_interface = get_valid_interface(transfer->dev_handle, USB_API_HID); + if (current_interface < 0) { + if (auto_claim(transfer, ¤t_interface, USB_API_HID) != LIBUSB_SUCCESS) { + return LIBUSB_ERROR_NOT_FOUND; + } + } + + usbi_dbg("will use interface %d", current_interface); + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + // Always use the handle returned from usbi_create_fd (wfd.handle) + wfd = usbi_create_fd(hid_handle, RW_READ, NULL, NULL); + if (wfd.fd < 0) { + return LIBUSB_ERROR_NOT_FOUND; + } + + switch(LIBUSB_REQ_TYPE(setup->request_type)) { + case LIBUSB_REQUEST_TYPE_STANDARD: + switch(setup->request) { + case LIBUSB_REQUEST_GET_DESCRIPTOR: + r = _hid_get_descriptor(priv->hid, wfd.handle, LIBUSB_REQ_RECIPIENT(setup->request_type), + (setup->value >> 8) & 0xFF, setup->value & 0xFF, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, &size); + break; + case LIBUSB_REQUEST_GET_CONFIGURATION: + r = windows_get_configuration(transfer->dev_handle, &config); + if (r == LIBUSB_SUCCESS) { + size = 1; + ((uint8_t*)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = (uint8_t)config; + r = LIBUSB_COMPLETED; + } + break; + case LIBUSB_REQUEST_SET_CONFIGURATION: + if (setup->value == priv->active_config) { + r = LIBUSB_COMPLETED; + } else { + usbi_warn(ctx, "cannot set configuration other than the default one"); + r = LIBUSB_ERROR_INVALID_PARAM; + } + break; + case LIBUSB_REQUEST_GET_INTERFACE: + size = 1; + ((uint8_t*)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = 0; + r = LIBUSB_COMPLETED; + break; + case LIBUSB_REQUEST_SET_INTERFACE: + r = hid_set_interface_altsetting(0, transfer->dev_handle, setup->index, setup->value); + if (r == LIBUSB_SUCCESS) { + r = LIBUSB_COMPLETED; + } + break; + default: + usbi_warn(ctx, "unsupported HID control request"); + r = LIBUSB_ERROR_INVALID_PARAM; + break; + } + break; + case LIBUSB_REQUEST_TYPE_CLASS: + r =_hid_class_request(priv->hid, wfd.handle, setup->request_type, setup->request, setup->value, + setup->index, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, transfer_priv, + &size, wfd.overlapped); + break; + default: + usbi_warn(ctx, "unsupported HID control request"); + r = LIBUSB_ERROR_INVALID_PARAM; + break; + } + + if (r == LIBUSB_COMPLETED) { + // Force request to be completed synchronously. Transferred size has been set by previous call + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + // http://msdn.microsoft.com/en-us/library/ms684342%28VS.85%29.aspx + // set InternalHigh to the number of bytes transferred + wfd.overlapped->InternalHigh = (DWORD)size; + r = LIBUSB_SUCCESS; + } + + if (r == LIBUSB_SUCCESS) { + // Use priv_transfer to store data needed for async polling + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + } else { + usbi_free_fd(&wfd); + } + + return r; +} + +static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); + struct winfd wfd; + HANDLE hid_handle; + bool direction_in, ret; + int current_interface, length; + DWORD size; + int r = LIBUSB_SUCCESS; + + CHECK_HID_AVAILABLE; + + transfer_priv->pollable_fd = INVALID_WINFD; + transfer_priv->hid_dest = NULL; + safe_free(transfer_priv->hid_buffer); + + current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface); + + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN; + + wfd = usbi_create_fd(hid_handle, direction_in?RW_READ:RW_WRITE, NULL, NULL); + // Always use the handle returned from usbi_create_fd (wfd.handle) + if (wfd.fd < 0) { + return LIBUSB_ERROR_NO_MEM; + } + + // If report IDs are not in use, an extra prefix byte must be added + if ( ((direction_in) && (!priv->hid->uses_report_ids[0])) + || ((!direction_in) && (!priv->hid->uses_report_ids[1])) ) { + length = transfer->length+1; + } else { + length = transfer->length; + } + // Add a trailing byte to detect overflows on input + transfer_priv->hid_buffer = (uint8_t*)calloc(length+1, 1); + if (transfer_priv->hid_buffer == NULL) { + return LIBUSB_ERROR_NO_MEM; + } + transfer_priv->hid_expected_size = length; + + if (direction_in) { + transfer_priv->hid_dest = transfer->buffer; + usbi_dbg("reading %d bytes (report ID: 0x00)", length); + ret = ReadFile(wfd.handle, transfer_priv->hid_buffer, length+1, &size, wfd.overlapped); + } else { + if (!priv->hid->uses_report_ids[1]) { + memcpy(transfer_priv->hid_buffer+1, transfer->buffer, transfer->length); + } else { + // We could actually do without the calloc and memcpy in this case + memcpy(transfer_priv->hid_buffer, transfer->buffer, transfer->length); + } + usbi_dbg("writing %d bytes (report ID: 0x%02X)", length, transfer_priv->hid_buffer[0]); + ret = WriteFile(wfd.handle, transfer_priv->hid_buffer, length, &size, wfd.overlapped); + } + if (!ret) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_err(ctx, "HID transfer failed: %s", windows_error_str(0)); + usbi_free_fd(&wfd); + safe_free(transfer_priv->hid_buffer); + return LIBUSB_ERROR_IO; + } + } else { + // Only write operations that completed synchronously need to free up + // hid_buffer. For reads, copy_transfer_data() handles that process. + if (!direction_in) { + safe_free(transfer_priv->hid_buffer); + } + if (size == 0) { + usbi_err(ctx, "program assertion failed - no data was transferred"); + size = 1; + } + if (size > (size_t)length) { + usbi_err(ctx, "OVERFLOW!"); + r = LIBUSB_ERROR_OVERFLOW; + } + wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; + wfd.overlapped->InternalHigh = size; + } + + transfer_priv->pollable_fd = wfd; + transfer_priv->interface_number = (uint8_t)current_interface; + + return r; +} + +static int hid_abort_transfers(int sub_api, struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + current_interface = transfer_priv->interface_number; + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + CancelIo(hid_handle); + + return LIBUSB_SUCCESS; +} + +static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle) +{ + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + // Flushing the queues on all interfaces is the best we can achieve + for (current_interface = 0; current_interface < USB_MAXINTERFACES; current_interface++) { + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + if ((hid_handle != 0) && (hid_handle != INVALID_HANDLE_VALUE)) { + HidD_FlushQueue(hid_handle); + } + } + return LIBUSB_SUCCESS; +} + +static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint) +{ + struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); + struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle); + struct windows_device_priv *priv = _device_priv(dev_handle->dev); + HANDLE hid_handle; + int current_interface; + + CHECK_HID_AVAILABLE; + + current_interface = interface_by_endpoint(priv, handle_priv, endpoint); + if (current_interface < 0) { + usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear"); + return LIBUSB_ERROR_NOT_FOUND; + } + + usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface); + hid_handle = handle_priv->interface_handle[current_interface].api_handle; + + // No endpoint selection with Microsoft's implementation, so we try to flush the + // whole interface. Should be OK for most case scenarios + if (!HidD_FlushQueue(hid_handle)) { + usbi_err(ctx, "Flushing of HID queue failed: %s", windows_error_str(0)); + // Device was probably disconnected + return LIBUSB_ERROR_NO_DEVICE; + } + + return LIBUSB_SUCCESS; +} + +// This extra function is only needed for HID +static int hid_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size) { + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + int r = LIBUSB_TRANSFER_COMPLETED; + uint32_t corrected_size = io_size; + + if (transfer_priv->hid_buffer != NULL) { + // If we have a valid hid_buffer, it means the transfer was async + if (transfer_priv->hid_dest != NULL) { // Data readout + // 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(struct libusb_context *ctx) +static int composite_init(int sub_api, struct libusb_context *ctx) { return LIBUSB_SUCCESS; } -static int composite_exit(void) +static int composite_exit(int sub_api) { return LIBUSB_SUCCESS; } -static int composite_open(struct libusb_device_handle *dev_handle) +static int composite_open(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<<USB_API_WINUSB; + 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 (api=USB_API_WINUSB; api<USB_API_MAX; api++) { - if (priv->composite_api_flags & flag) { - r = usb_api_backend[api].open(dev_handle); + for (i=0; i<USB_MAXINTERFACES; i++) { + switch (priv->usb_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; i<SUB_API_MAX; i++) { // WinUSB-like drivers + if (available[i]) { + r = usb_api_backend[USB_API_WINUSBX].open(i, dev_handle); if (r != LIBUSB_SUCCESS) { return r; } } - flag <<= 1; } - return LIBUSB_SUCCESS; + if (available[SUB_API_MAX]) { // HID driver + r = hid_open(SUB_API_NOTSET, dev_handle); + } + return r; } -static void composite_close(struct libusb_device_handle *dev_handle) +static void composite_close(int sub_api, struct libusb_device_handle *dev_handle) { struct windows_device_priv *priv = _device_priv(dev_handle->dev); - unsigned api; - uint8_t flag = 1<<USB_API_WINUSB; + uint8_t i; + bool available[SUB_API_MAX]; + + for (i = 0; i<SUB_API_MAX; i++) { + available[i] = false; + } + + for (i=0; i<USB_MAXINTERFACES; i++) { + if ( (priv->usb_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 (api=USB_API_WINUSB; api<USB_API_MAX; api++) { - if (priv->composite_api_flags & flag) { - usb_api_backend[api].close(dev_handle); + for (i=0; i<SUB_API_MAX; i++) { + if (available[i]) { + usb_api_backend[USB_API_WINUSBX].close(i, dev_handle); } - flag <<= 1; } } -static int composite_claim_interface(struct libusb_device_handle *dev_handle, int iface) +static int composite_claim_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->claim_interface(dev_handle, iface); + return priv->usb_interface[iface].apib-> + claim_interface(priv->usb_interface[iface].sub_api, dev_handle, iface); } -static int composite_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) +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(dev_handle, iface, altsetting); + return priv->usb_interface[iface].apib-> + set_interface_altsetting(priv->usb_interface[iface].sub_api, dev_handle, iface, altsetting); } -static int composite_release_interface(struct libusb_device_handle *dev_handle, int iface) +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(dev_handle, iface); + return priv->usb_interface[iface].apib-> + release_interface(priv->usb_interface[iface].sub_api, dev_handle, iface); } -static int composite_submit_control_transfer(struct usbi_transfer *itransfer) +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; - - for (i=0; i<USB_MAXINTERFACES; i++) { - if (priv->usb_interface[i].path != NULL) { - usbi_dbg("using interface %d", i); - return priv->usb_interface[i].apib->submit_control_transfer(itransfer); + 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; i<USB_MAXINTERFACES; i++) { + if (priv->usb_interface[i].path != NULL) { + if ((pass == 0) && (priv->usb_interface[i].restricted_functionality)) { + usbi_dbg("trying to skip restricted interface #%d (HID keyboard or mouse?)", i); + continue; + } + usbi_dbg("using interface %d", i); + return priv->usb_interface[i].apib->submit_control_transfer(priv->usb_interface[i].sub_api, itransfer); + } } } - usbi_err(ctx, "no libusb supported interfaces to complete request"); + usbi_err(ctx, "no libusbx supported interfaces to complete request"); return LIBUSB_ERROR_NOT_FOUND; } -static int composite_submit_bulk_transfer(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); @@ -2914,10 +4304,10 @@ static int composite_submit_bulk_transfer(struct usbi_transfer *itransfer) { return LIBUSB_ERROR_NOT_FOUND; } - return priv->usb_interface[current_interface].apib->submit_bulk_transfer(itransfer); -} + return priv->usb_interface[current_interface].apib-> + submit_bulk_transfer(priv->usb_interface[current_interface].sub_api, itransfer);} -static int composite_submit_iso_transfer(struct usbi_transfer *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); @@ -2930,10 +4320,10 @@ 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); -} + return priv->usb_interface[current_interface].apib-> + submit_iso_transfer(priv->usb_interface[current_interface].sub_api, itransfer);} -static int composite_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) +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); @@ -2946,51 +4336,59 @@ static int composite_clear_halt(struct libusb_device_handle *dev_handle, unsigne return LIBUSB_ERROR_NOT_FOUND; } - return priv->usb_interface[current_interface].apib->clear_halt(dev_handle, endpoint); -} + return priv->usb_interface[current_interface].apib-> + clear_halt(priv->usb_interface[current_interface].sub_api, dev_handle, endpoint);} -static int composite_abort_control(struct usbi_transfer *itransfer) +static int composite_abort_control(int sub_api, struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); - return priv->usb_interface[transfer_priv->interface_number].apib->abort_control(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_abort_transfers(struct usbi_transfer *itransfer) +static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); - return priv->usb_interface[transfer_priv->interface_number].apib->abort_transfers(itransfer); -} + 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(struct libusb_device_handle *dev_handle) +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<<USB_API_WINUSB; - - for (api=USB_API_WINUSB; api<USB_API_MAX; api++) { - if (priv->composite_api_flags & flag) { - r = usb_api_backend[api].reset_device(dev_handle); + uint8_t i; + bool available[SUB_API_MAX]; + for (i = 0; i<SUB_API_MAX; i++) { + available[i] = false; + } + for (i=0; i<USB_MAXINTERFACES; i++) { + if ( (priv->usb_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; i<SUB_API_MAX; i++) { + if (available[i]) { + r = usb_api_backend[USB_API_WINUSBX].reset_device(i, dev_handle); if (r != LIBUSB_SUCCESS) { return r; } } - flag <<= 1; } return LIBUSB_SUCCESS; } -static int composite_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) +static int composite_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 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->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/third_party/libusb/src/libusb/os/windows_usb.h b/third_party/libusb/src/libusb/os/windows_usb.h index 7c2fae5..5d67a56 100644 --- a/third_party/libusb/src/libusb/os/windows_usb.h +++ b/third_party/libusb/src/libusb/os/windows_usb.h @@ -1,6 +1,6 @@ /* - * Windows backend for libusb 1.0 - * Copyright (C) 2009-2010 Pete Batard <pbatard@gmail.com> + * Windows backend for libusbx 1.0 + * Copyright © 2009-2012 Pete Batard <pete@akeo.ie> * 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)?"<NULL>":str1), ((str2==NULL)?"<NULL>":str2)) -#define safe_strncmp(str1, str2, count) strncmp(((str1==NULL)?"<NULL>":str1), ((str2==NULL)?"<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; i<safe_strlen(str); i++) - str[i] = (char)toupper((int)str[i]); -} #define MAX_CTRL_BUFFER_LENGTH 4096 #define MAX_USB_DEVICES 256 #define MAX_USB_STRING_LENGTH 128 +#define MAX_HID_REPORT_SIZE 1024 +#define MAX_HID_DESCRIPTOR_SIZE 256 #define MAX_GUID_STRING_LENGTH 40 #define MAX_PATH_LENGTH 128 #define MAX_KEY_LENGTH 256 -#define MAX_TIMER_SEMAPHORES 128 -#define TIMER_REQUEST_RETRY_MS 100 -#define ERR_BUFFER_SIZE 256 #define LIST_SEPARATOR ';' #define HTAB_SIZE 1021 +// Handle code for HID interface that have been claimed ("dibs") +#define INTERFACE_CLAIMED ((HANDLE)(intptr_t)0xD1B5) +// Additional return code for HID operations that completed synchronously +#define LIBUSB_COMPLETED (LIBUSB_SUCCESS + 1) + // http://msdn.microsoft.com/en-us/library/ff545978.aspx // http://msdn.microsoft.com/en-us/library/ff545972.aspx // http://msdn.microsoft.com/en-us/library/ff545982.aspx @@ -101,7 +77,9 @@ const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0 #if !defined(GUID_DEVINTERFACE_USB_HUB) const GUID GUID_DEVINTERFACE_USB_HUB = { 0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8} }; #endif -static const GUID GUID_NULL = { 0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; +#if !defined(GUID_DEVINTERFACE_LIBUSB0_FILTER) +const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = { 0xF9F3FF14, 0xAE21, 0x48A0, {0x8A, 0x25, 0x80, 0x11, 0xA7, 0xA9, 0x31, 0xD9} }; +#endif /* @@ -110,34 +88,43 @@ static const GUID GUID_NULL = { 0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0 #define USB_API_UNSUPPORTED 0 #define USB_API_HUB 1 #define USB_API_COMPOSITE 2 -#define USB_API_WINUSB 3 -#define USB_API_MAX 4 - -#define CLASS_GUID_UNSUPPORTED GUID_NULL -const GUID CLASS_GUID_LIBUSB_WINUSB = { 0x78A1C341, 0x4539, 0x11D3, {0xB8, 0x8D, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71} }; -const GUID CLASS_GUID_COMPOSITE = { 0x36FC9E60, 0xC465, 0x11cF, {0x80, 0x56, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} }; +#define USB_API_WINUSBX 3 +#define USB_API_HID 4 +#define USB_API_MAX 5 +// The following is used to indicate if the HID or composite extra props have already been set. +#define USB_API_SET (1<<USB_API_MAX) + +// Sub-APIs for WinUSB-like driver APIs (WinUSB, libusbK, libusb-win32 through the libusbK DLL) +// Must have the same values as the KUSB_DRVID enum from libusbk.h +#define SUB_API_NOTSET -1 +#define SUB_API_LIBUSBK 0 +#define SUB_API_LIBUSB0 1 +#define SUB_API_WINUSB 2 +#define SUB_API_MAX 3 + +#define WINUSBX_DRV_NAMES { "libusbK", "libusb0", "WinUSB"} struct windows_usb_api_backend { const uint8_t id; const char* designation; - const GUID *class_guid; // The Class GUID (for fallback in case the driver name cannot be read) const char **driver_name_list; // Driver name, without .sys, e.g. "usbccgp" const uint8_t nb_driver_names; - int (*init)(struct libusb_context *ctx); - int (*exit)(void); - int (*open)(struct libusb_device_handle *dev_handle); - void (*close)(struct libusb_device_handle *dev_handle); - int (*claim_interface)(struct libusb_device_handle *dev_handle, int iface); - int (*set_interface_altsetting)(struct libusb_device_handle *dev_handle, int iface, int altsetting); - int (*release_interface)(struct libusb_device_handle *dev_handle, int iface); - int (*clear_halt)(struct libusb_device_handle *dev_handle, unsigned char endpoint); - int (*reset_device)(struct libusb_device_handle *dev_handle); - int (*submit_bulk_transfer)(struct usbi_transfer *itransfer); - int (*submit_iso_transfer)(struct usbi_transfer *itransfer); - int (*submit_control_transfer)(struct usbi_transfer *itransfer); - int (*abort_control)(struct usbi_transfer *itransfer); - int (*abort_transfers)(struct usbi_transfer *itransfer); - int (*copy_transfer_data)(struct usbi_transfer *itransfer, uint32_t io_size); + int (*init)(int sub_api, struct libusb_context *ctx); + int (*exit)(int sub_api); + int (*open)(int sub_api, struct libusb_device_handle *dev_handle); + void (*close)(int sub_api, struct libusb_device_handle *dev_handle); + int (*configure_endpoints)(int sub_api, struct libusb_device_handle *dev_handle, int iface); + int (*claim_interface)(int sub_api, struct libusb_device_handle *dev_handle, int iface); + int (*set_interface_altsetting)(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting); + int (*release_interface)(int sub_api, struct libusb_device_handle *dev_handle, int iface); + int (*clear_halt)(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint); + int (*reset_device)(int sub_api, struct libusb_device_handle *dev_handle); + int (*submit_bulk_transfer)(int sub_api, struct usbi_transfer *itransfer); + int (*submit_iso_transfer)(int sub_api, struct usbi_transfer *itransfer); + int (*submit_control_transfer)(int sub_api, struct usbi_transfer *itransfer); + int (*abort_control)(int sub_api, struct usbi_transfer *itransfer); + int (*abort_transfers)(int sub_api, struct usbi_transfer *itransfer); + int (*copy_transfer_data)(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size); }; extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX]; @@ -151,21 +138,90 @@ extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX]; * private structures definition * with inline pseudo constructors/destructors */ + +// TODO (v2+): move hid desc to libusb.h? +struct libusb_hid_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdHID; + uint8_t bCountryCode; + uint8_t bNumDescriptors; + uint8_t bClassDescriptorType; + uint16_t wClassDescriptorLength; +}; +#define LIBUSB_DT_HID_SIZE 9 +#define HID_MAX_CONFIG_DESC_SIZE (LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE \ + + LIBUSB_DT_HID_SIZE + 2 * LIBUSB_DT_ENDPOINT_SIZE) +#define HID_MAX_REPORT_SIZE 1024 +#define HID_IN_EP 0x81 +#define HID_OUT_EP 0x02 +#define LIBUSB_REQ_RECIPIENT(request_type) ((request_type) & 0x1F) +#define LIBUSB_REQ_TYPE(request_type) ((request_type) & (0x03 << 5)) +#define LIBUSB_REQ_IN(request_type) ((request_type) & LIBUSB_ENDPOINT_IN) +#define LIBUSB_REQ_OUT(request_type) (!LIBUSB_REQ_IN(request_type)) + +// The following are used for HID reports IOCTLs +#define HID_CTL_CODE(id) \ + CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_NEITHER, FILE_ANY_ACCESS) +#define HID_BUFFER_CTL_CODE(id) \ + CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_BUFFERED, FILE_ANY_ACCESS) +#define HID_IN_CTL_CODE(id) \ + CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_IN_DIRECT, FILE_ANY_ACCESS) +#define HID_OUT_CTL_CODE(id) \ + CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + +#define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) +#define IOCTL_HID_GET_INPUT_REPORT HID_OUT_CTL_CODE(104) +#define IOCTL_HID_SET_FEATURE HID_IN_CTL_CODE(100) +#define IOCTL_HID_SET_OUTPUT_REPORT HID_IN_CTL_CODE(101) + +enum libusb_hid_request_type { + HID_REQ_GET_REPORT = 0x01, + HID_REQ_GET_IDLE = 0x02, + HID_REQ_GET_PROTOCOL = 0x03, + HID_REQ_SET_REPORT = 0x09, + HID_REQ_SET_IDLE = 0x0A, + HID_REQ_SET_PROTOCOL = 0x0B +}; + +enum libusb_hid_report_type { + HID_REPORT_TYPE_INPUT = 0x01, + HID_REPORT_TYPE_OUTPUT = 0x02, + HID_REPORT_TYPE_FEATURE = 0x03 +}; + +struct hid_device_priv { + uint16_t vid; + uint16_t pid; + uint8_t config; + uint8_t nb_interfaces; + bool uses_report_ids[3]; // input, ouptput, feature + uint16_t input_report_size; + uint16_t output_report_size; + uint16_t feature_report_size; + WCHAR string[3][MAX_USB_STRING_LENGTH]; + uint8_t string_index[3]; // man, prod, ser +}; + typedef struct libusb_device_descriptor USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR; struct windows_device_priv { uint8_t depth; // distance to HCD uint8_t port; // port number on the hub + uint8_t active_config; struct libusb_device *parent_dev; // access to parent is required for usermode ops - char *path; // device interface path struct windows_usb_api_backend const *apib; + char *path; // device interface path + int sub_api; // for WinUSB-like APIs struct { char *path; // each interface needs a device interface path, struct windows_usb_api_backend const *apib; // an API backend (multiple drivers support), + int sub_api; int8_t nb_endpoints; // and a set of endpoint addresses (USB_MAXENDPOINTS) uint8_t *endpoint; + bool restricted_functionality; // indicates if the interface functionality is restricted + // by Windows (eg. HID keyboards or mice cannot do R/W) } usb_interface[USB_MAXINTERFACES]; - uint8_t composite_api_flags; // composite devices require additional data - uint8_t active_config; + struct hid_device_priv *hid; USB_DEVICE_DESCRIPTOR dev_descriptor; unsigned char **config_descriptor; // list of pointers to the cached config descriptors }; @@ -182,15 +238,18 @@ static inline void windows_device_priv_init(libusb_device* dev) { p->parent_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; i<USB_MAXINTERFACES; i++) { p->usb_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; i<USB_MAXINTERFACES; i++) { safe_free(p->usb_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/third_party/libusb/src/libusb/strerror.c b/third_party/libusb/src/libusb/strerror.c new file mode 100644 index 0000000..a3c3afa --- /dev/null +++ b/third_party/libusb/src/libusb/strerror.c @@ -0,0 +1,184 @@ +/* + * libusb strerror code + * Copyright © 2013 Hans de Goede <hdegoede@redhat.com> + * + * 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 <locale.h> +#include <stdlib.h> +#include <string.h> + +#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: + * <ol> + * <li> Download the latest \c strerror.c from:<br> + * https://raw.github.com/libusbx/libusbx/master/libusb/sterror.c </li> + * <li> Open the file in an UTF-8 capable editor </li> + * <li> Add the 2 letter <a href="http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes">ISO 639-1</a> + * code for your locale at the end of \c usbi_locale_supported[]<br> + * 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 </li> + * <li> Copy the <tt>{ / * English (en) * / ... }</tt> section and add it at the end of \c usbi_localized_errors<br> + * Eg. for Chinese, the last section of \c usbi_localized_errors could look like: + * \code + * }, { / * Chinese (zh) * / + * "Success", + * ... + * "Other error", + * } + * };\endcode </li> + * <li> Translate each of the English messages from the section you copied into your language </li> + * <li> Save the file (in UTF-8 format) and send it to \c libusbx-devel@lists.sourceforge.net </li> + * </ol> + */ + +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); i++) { + if (strncasecmp(usbi_locale_supported[i], locale, 2) == 0) + break; + } + if (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/third_party/libusb/src/libusb/sync.c b/third_party/libusb/src/libusb/sync.c index 8eed47b..42e486d 100644 --- a/third_party/libusb/src/libusb/sync.c +++ b/third_party/libusb/src/libusb/sync.c @@ -1,6 +1,6 @@ /* - * Synchronous I/O functions for libusb - * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org> + * Synchronous I/O functions for libusbx + * Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org> * * 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 <config.h> +#include "config.h" #include <errno.h> #include <stdint.h> #include <stdlib.h> @@ -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), @@ -135,6 +141,10 @@ int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle, case LIBUSB_TRANSFER_OVERFLOW: r = LIBUSB_ERROR_OVERFLOW; break; + case LIBUSB_TRANSFER_ERROR: + case LIBUSB_TRANSFER_CANCELLED: + r = LIBUSB_ERROR_IO; + break; default: usbi_warn(HANDLE_CTX(dev_handle), "unrecognised status code %d", transfer->status); @@ -145,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) @@ -165,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); @@ -174,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) { @@ -205,6 +195,10 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, case LIBUSB_TRANSFER_NO_DEVICE: r = LIBUSB_ERROR_NO_DEVICE; break; + case LIBUSB_TRANSFER_ERROR: + case LIBUSB_TRANSFER_CANCELLED: + r = LIBUSB_ERROR_IO; + break; default: usbi_warn(HANDLE_CTX(dev_handle), "unrecognised status code %d", transfer->status); @@ -228,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 <tt>transferred</tt> 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. * @@ -276,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 <tt>transferred</tt> 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. * @@ -311,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/third_party/libusb/src/libusb/version.h b/third_party/libusb/src/libusb/version.h index 62446da..fbb9f4c 100644 --- a/third_party/libusb/src/libusb/version.h +++ b/third_party/libusb/src/libusb/version.h @@ -1,4 +1,21 @@ +/* + * 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 + */ + /* 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,9 +23,8 @@ #define LIBUSB_MINOR 0 #endif #ifndef LIBUSB_MICRO -#define LIBUSB_MICRO 9 +#define LIBUSB_MICRO 16 #endif -/* LIBUSB_NANO may be used for Windows internal versioning. 0 means unused. */ #ifndef LIBUSB_NANO #define LIBUSB_NANO 0 #endif diff --git a/third_party/libusb/src/libusb/version_nano.h b/third_party/libusb/src/libusb/version_nano.h new file mode 100644 index 0000000..522c6fd --- /dev/null +++ b/third_party/libusb/src/libusb/version_nano.h @@ -0,0 +1,17 @@ +/* + * 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 + */ + +#define LIBUSB_NANO 10774 diff --git a/third_party/libusb/src/msvc/config.h b/third_party/libusb/src/msvc/config.h index 43aa1f7..da47672 100644 --- a/third_party/libusb/src/msvc/config.h +++ b/third_party/libusb/src/msvc/config.h @@ -1,3 +1,19 @@ +/* + * 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 + */ + /* config.h. Manual config for MSVC. */ #ifndef _MSC_VER @@ -5,20 +21,35 @@ #error "Please make sure the msvc/ directory is removed from your build path." #endif +/* Disable: warning C4200: nonstandard extension used : zero-sized array in struct/union */ +#pragma warning(disable:4200) +/* Disable: warning C6258: Using TerminateThread does not allow proper thread clean up */ +#pragma warning(disable: 6258) +#if defined(_PREFAST_) +/* Disable "Banned API" errors when using the MS's WDK OACR/Prefast */ +#pragma warning(disable:28719) +/* Disable "The function 'InitializeCriticalSection' must be called from within a try/except block" */ +#pragma warning(disable:28125) +#endif + /* Default visibility */ #define DEFAULT_VISIBILITY /**/ -/* Debug message logging */ -//#define ENABLE_DEBUG_LOGGING 1 - -/* Message logging */ +/* Enable global message logging */ #define ENABLE_LOGGING 1 -/* Windows backend */ -#define OS_WINDOWS 1 +/* Uncomment to start with debug message logging enabled */ +// #define ENABLE_DEBUG_LOGGING 1 /* type of second poll() argument */ #define POLL_NFDS_TYPE unsigned int -/* no way to run git describe from MSVC? */ -#define LIBUSB_DESCRIBE "" +/* Windows/WinCE backend */ +#if defined(_WIN32_WCE) +#define OS_WINCE 1 +#define HAVE_MISSING_H +#else +#define OS_WINDOWS 1 +#define HAVE_SIGNAL_H 1 +#define HAVE_SYS_TYPES_H 1 +#endif diff --git a/third_party/libusb/src/msvc/ddk_build.cmd b/third_party/libusb/src/msvc/ddk_build.cmd deleted file mode 100644 index ed9b8a8..0000000 --- a/third_party/libusb/src/msvc/ddk_build.cmd +++ /dev/null @@ -1,106 +0,0 @@ -@rem default builds static library. -@rem you can pass the following arguments (case insensitive): -@rem - "DLL" to build a DLL instead of a static library -@rem - "/MT" to build a static library compatible with MSVC's /MT option (LIBCMT vs MSVCRT) -@echo off - -if Test%BUILD_ALT_DIR%==Test goto usage - -rem process commandline parameters -set TARGET=LIBRARY -set STATIC_LIBC= -set version=1.0 - -if "%1" == "" goto no_more_args -rem /I for case insensitive -if /I Test%1==TestDLL set TARGET=DYNLINK -if /I Test%1==Test/MT set STATIC_LIBC=1 -:no_more_args - -cd ..\libusb\os -echo TARGETTYPE=%TARGET% > target -copy target+..\..\msvc\libusb_sources sources >NUL 2>&1 -del target -@echo on -build -cwgZ -@echo off -if errorlevel 1 goto builderror -cd ..\.. - -set cpudir=i386 -set destType=Win32 -if %_BUILDARCH%==x86 goto isI386 -set cpudir=amd64 -set destType=x64 -:isI386 - -set srcPath=libusb\os\obj%BUILD_ALT_DIR%\%cpudir% - -set dstPath=%destType%\Debug -if %DDKBUILDENV%==chk goto isDebug -set dstPath=%destType%\Release -:isDebug - -if exist %destType% goto md2 -mkdir %destType% -:md2 -if exist %dstPath% goto md3 -mkdir %dstPath% -:md3 -if exist %dstPath%\dll goto md4 -mkdir %dstPath%\dll -:md4 -if exist %dstPath%\lib goto md5 -md %dstPath%\lib -:md5 -if exist %dstPath%\examples goto md6 -md %dstPath%\examples -:md6 -@echo on - -@if /I NOT Test%1==TestDLL goto copylib -copy %srcPath%\libusb-%version%.dll %dstPath%\dll -copy %srcPath%\libusb-%version%.pdb %dstPath%\dll -:copylib -copy %srcPath%\libusb-%version%.lib %dstPath%\lib - -@echo off - -if exist examples\listdevs_ddkbuild goto md7 -md examples\listdevs_ddkbuild -:md7 - -cd examples\listdevs_ddkbuild -copy ..\..\msvc\listdevs_sources sources >NUL 2>&1 -@echo on -build -cwgZ -@echo off -if errorlevel 1 goto buildlistdevserror -cd ..\.. - -set srcPath=examples\listdevs_ddkbuild\obj%BUILD_ALT_DIR%\%cpudir% -@echo on - -copy %srcPath%\listdevs.exe %dstPath%\examples -copy %srcPath%\listdevs.pdb %dstPath%\examples - -cd msvc -goto done - - -:builderror -cd ..\..\msvc -echo Build failed -goto done - -:buildlistdevserror -cd ..\..\msvc -echo listdevs build failed -goto done - -:usage -echo ddk_build must be run in a WDK build environment -pause -goto done - -:done diff --git a/third_party/libusb/src/msvc/errno.h b/third_party/libusb/src/msvc/errno.h new file mode 100644 index 0000000..07d15e3 --- /dev/null +++ b/third_party/libusb/src/msvc/errno.h @@ -0,0 +1,102 @@ +/* + * errno.h + * This file has no copyright assigned and is placed in the Public Domain. + * This file is a part of the mingw-runtime package. + * No warranty is given; refer to the file DISCLAIMER within the package. + * + * Error numbers and access to error reporting. + * + */ + +#ifndef _ERRNO_H_ +#define _ERRNO_H_ + +#include <crtdefs.h> + +/* + * Error numbers. + * TODO: Can't be sure of some of these assignments, I guessed from the + * names given by strerror and the defines in the Cygnus errno.h. A lot + * of the names from the Cygnus errno.h are not represented, and a few + * of the descriptions returned by strerror do not obviously match + * their error naming. + */ +#define EPERM 1 /* Operation not permitted */ +#define ENOFILE 2 /* No such file or directory */ +#define ENOENT 2 +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted function call */ +#define EIO 5 /* Input/output error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file descriptor */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Resource temporarily unavailable */ +#define ENOMEM 12 /* Not enough space */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +/* 15 - Unknown Error */ +#define EBUSY 16 /* strerror reports "Resource device" */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Improper link (cross-device link?) */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* Too many open files in system */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Inappropriate I/O control operation */ +/* 26 - Unknown Error */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Invalid seek (seek on a pipe?) */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Domain error (math functions) */ +#define ERANGE 34 /* Result too large (possibly too small) */ +/* 35 - Unknown Error */ +#define EDEADLOCK 36 /* Resource deadlock avoided (non-Cyg) */ +#define EDEADLK 36 +#if 0 +/* 37 - Unknown Error */ +#define ENAMETOOLONG 38 /* Filename too long (91 in Cyg?) */ +#define ENOLCK 39 /* No locks available (46 in Cyg?) */ +#define ENOSYS 40 /* Function not implemented (88 in Cyg?) */ +#define ENOTEMPTY 41 /* Directory not empty (90 in Cyg?) */ +#define EILSEQ 42 /* Illegal byte sequence */ +#endif + +/* + * NOTE: ENAMETOOLONG and ENOTEMPTY conflict with definitions in the + * sockets.h header provided with windows32api-0.1.2. + * You should go and put an #if 0 ... #endif around the whole block + * of errors (look at the comment above them). + */ + +#ifndef RC_INVOKED + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Definitions of errno. For _doserrno, sys_nerr and * sys_errlist, see + * stdlib.h. + */ +#if defined(_UWIN) || defined(_WIN32_WCE) +#undef errno +extern int errno; +#else +_CRTIMP int* __cdecl _errno(void); +#define errno (*_errno()) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* Not RC_INVOKED */ + +#endif /* Not _ERRNO_H_ */
\ No newline at end of file diff --git a/third_party/libusb/src/msvc/libusb.dsw b/third_party/libusb/src/msvc/libusb.dsw deleted file mode 100644 index fbe3015..0000000 --- a/third_party/libusb/src/msvc/libusb.dsw +++ /dev/null @@ -1,56 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "libusb_dll"=".\libusb_dll.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "libusb_static"=".\libusb_static.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "listdevs"=".\listdevs.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ - Begin Project Dependency - Project_Dep_Name libusb_static - End Project Dependency -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/third_party/libusb/src/msvc/libusb_dll.dsp b/third_party/libusb/src/msvc/libusb_dll.dsp deleted file mode 100644 index bad8d15..0000000 --- a/third_party/libusb/src/msvc/libusb_dll.dsp +++ /dev/null @@ -1,190 +0,0 @@ -# Microsoft Developer Studio Project File - Name="libusb_dll" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=libusb_dll - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "libusb_dll.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "libusb_dll.mak" CFG="libusb_dll - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "libusb_dll - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "libusb_dll - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "libusb_dll - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "../Win32/Release/dll" -# PROP Intermediate_Dir "../Win32/Release/dll" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "LIBUSB_DLL_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /I "../libusb" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /U "_MBCS" /D "_USRDLL" /FR /FD /EHsc /c -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"Win32/Release/dll/libusb-1.0.dll" - -!ELSEIF "$(CFG)" == "libusb_dll - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "../Win32/Debug/dll" -# PROP Intermediate_Dir "../Win32/Debug/dll" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "LIBUSB_DLL_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "." /I "../libusb" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /U "_MBCS" /D "_USRDLL" /FR /FD /EHsc /c -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo /n -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"Win32/Debug/dll/libusb-1.0.dll" -# SUBTRACT LINK32 /pdb:none /incremental:no - -!ENDIF - -# Begin Target - -# Name "libusb_dll - Win32 Release" -# Name "libusb_dll - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\libusb\core.c -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\darwin_usb.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\libusb\descriptor.c -# End Source File -# Begin Source File - -SOURCE=..\libusb\io.c -# End Source File -# Begin Source File - -SOURCE="..\libusb\libusb-1.0.rc" -# End Source File -# Begin Source File - -SOURCE="..\libusb\libusb-1.0.def" -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\linux_usbfs.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\poll_windows.c -# End Source File -# Begin Source File - -SOURCE=..\libusb\sync.c -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\threads_windows.c -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\windows_usb.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\config.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\darwin_usb.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\libusb.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\libusbi.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\linux_usbfs.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\poll_posix.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\poll_windows.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\threads_posix.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\threads_windows.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\windows_usb.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/third_party/libusb/src/msvc/libusb_sources b/third_party/libusb/src/msvc/libusb_sources deleted file mode 100644 index 8e8e65c..0000000 --- a/third_party/libusb/src/msvc/libusb_sources +++ /dev/null @@ -1,36 +0,0 @@ -#TARGETTYPE is not defined, to allow selection between static lib or DLL with ddk_build -TARGETNAME=libusb-1.0 -DLLDEF=..\libusb-1.0.def - -!IFNDEF MSC_WARNING_LEVEL -MSC_WARNING_LEVEL=/W3 -!ENDIF - -!IFDEF STATIC_LIBC -USE_LIBCMT=1 -!ELSE -USE_MSVCRT=1 -!ENDIF - -INCLUDES=..;..\..\msvc;$(DDK_INC_PATH) -C_DEFINES= $(C_DEFINES) $(LIBUSB_DEFINES) /DDDKBUILD - -# http://jpassing.com/2009/10/21/ltcg-issues-with-the-win7amd64-environment-of-wdk-7600/ -# prevents the following error when using the 64 bit static lib with Visual Studio 2010: -# "fatal error C1001: An internal error has occurred in the compiler. -# (compiler file 'f:\dd\vctools\compiler\utc\src\p2\p2symtab.c', line 1823)" -# and the following with Visual Studio 2010: -# "fatal error C1047: The object or library file 'libusb-1.0.lib' was created with -# an older compiler than other objects; rebuild old objects and libraries" -USER_C_FLAGS=/GL- - -TARGETLIBS=$(SDK_LIB_PATH)\kernel32.lib - -SOURCES=..\core.c \ - ..\descriptor.c \ - ..\io.c \ - ..\sync.c \ - threads_windows.c \ - poll_windows.c \ - windows_usb.c \ - ..\libusb-1.0.rc diff --git a/third_party/libusb/src/msvc/libusb_static.dsp b/third_party/libusb/src/msvc/libusb_static.dsp deleted file mode 100644 index b636691..0000000 --- a/third_party/libusb/src/msvc/libusb_static.dsp +++ /dev/null @@ -1,170 +0,0 @@ -# Microsoft Developer Studio Project File - Name="libusb_static" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=libusb_static - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "libusb_static.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "libusb_static.mak" CFG="libusb_static - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "libusb_static - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "libusb_static - Win32 Debug" (based on "Win32 (x86) Static Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "libusb_static - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "../Win32/Release/lib" -# PROP Intermediate_Dir "../Win32/Release/lib" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /I "../libusb" /D "WIN32" /D "NDEBUG" /D "_UNICODE" /D "UNICODE" /U "_MBCS" /D "_LIB" /FR /FD /EHsc /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo /out:"../Win32/Release/lib/libusb-1.0.lib" - -!ELSEIF "$(CFG)" == "libusb_static - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "../Win32/Debug/lib" -# PROP Intermediate_Dir "../Win32/Debug/lib" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "." /I "../libusb" /D "WIN32" /D "_DEBUG" /D "_UNICODE" /D "UNICODE" /U "_MBCS" /D "_LIB" /FR /FD /GZ /EHsc /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo /n -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo /out:"../Win32/Debug/lib/libusb-1.0.lib" - -!ENDIF - -# Begin Target - -# Name "libusb_static - Win32 Release" -# Name "libusb_static - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\libusb\core.c -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\darwin_usb.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\libusb\descriptor.c -# End Source File -# Begin Source File - -SOURCE=..\libusb\io.c -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\linux_usbfs.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\poll_windows.c -# End Source File -# Begin Source File - -SOURCE=..\libusb\sync.c -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\threads_windows.c -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\windows_usb.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\config.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\darwin_usb.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\libusb.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\libusbi.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\linux_usbfs.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\poll_posix.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\poll_windows.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\threads_posix.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\threads_windows.h -# End Source File -# Begin Source File - -SOURCE=..\libusb\os\windows_usb.h -# End Source File -# End Group -# End Target -# End Project diff --git a/third_party/libusb/src/msvc/listdevs.dsp b/third_party/libusb/src/msvc/listdevs.dsp deleted file mode 100644 index 93a93e4..0000000 --- a/third_party/libusb/src/msvc/listdevs.dsp +++ /dev/null @@ -1,103 +0,0 @@ -# Microsoft Developer Studio Project File - Name="listdevs" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=listdevs - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "listdevs.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "listdevs.mak" CFG="listdevs - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "listdevs - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "listdevs - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "listdevs - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "../Win32/Release/examples" -# PROP Intermediate_Dir "../Win32/Release/examples/listdevs" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /FD /c -# ADD CPP /nologo /MD /W3 /GX /O2 /I "../libusb" /I "." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /U "_MBCS" /FR /FD /EHsc /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 - -!ELSEIF "$(CFG)" == "listdevs - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "../Win32/Debug/examples" -# PROP Intermediate_Dir "../Win32/Debug/examples/listdevs" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /FD /GZ /c -# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../libusb" /I "." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /U "_MBCS" /FR /FD /GZ /EHsc /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo /n "../Win32/Debug/dll/core.sbr" "../Win32/Debug/dll/descriptor.sbr" "../Win32/Debug/dll/io.sbr" "../Win32/Debug/dll/sync.sbr" "../Win32/Debug/dll/poll_windows.sbr" "../Win32/Debug/dll/threads_windows.sbr" "../Win32/Debug/dll/windows_usb.sbr" -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 -# SUBTRACT LINK32 /pdb:none - -!ENDIF - -# Begin Target - -# Name "listdevs - Win32 Release" -# Name "listdevs - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\examples\listdevs.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/third_party/libusb/src/msvc/listdevs_sources b/third_party/libusb/src/msvc/listdevs_sources deleted file mode 100644 index 97a5723..0000000 --- a/third_party/libusb/src/msvc/listdevs_sources +++ /dev/null @@ -1,19 +0,0 @@ -TARGETNAME=listdevs -TARGETTYPE=PROGRAM -386_STDCALL=0 - -_NT_TARGET_VERSION= $(_NT_TARGET_VERSION_WINXP) -!IFNDEF MSC_WARNING_LEVEL -MSC_WARNING_LEVEL=/W3 -!ENDIF - -!IFDEF STATIC_LIBC -USE_LIBCMT=1 -!ELSE -USE_MSVCRT=1 -!ENDIF - -UMTYPE=console -INCLUDES=..\..\libusb;$(DDK_INC_PATH) -UMLIBS=..\..\libusb\os\obj$(BUILD_ALT_DIR)\*\libusb-1.0.lib -SOURCES=..\listdevs.c diff --git a/third_party/libusb/src/msvc/missing.c b/third_party/libusb/src/msvc/missing.c new file mode 100644 index 0000000..85d9d6f --- /dev/null +++ b/third_party/libusb/src/msvc/missing.c @@ -0,0 +1,80 @@ +/* + * Source file for missing WinCE functionality + * Copyright © 2012 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 "missing.h" + +#include <config.h> +#include <libusbi.h> + +#include <windows.h> + +// The registry path to store environment variables +#define ENVIRONMENT_REG_PATH _T("Software\\libusb\\environment") + +/* Workaround getenv not being available on WinCE. + * Instead look in HKLM\Software\libusb\environment */ +char *getenv(const char *name) +{ + static char value[MAX_PATH]; + TCHAR wValue[MAX_PATH]; + WCHAR wName[MAX_PATH]; + DWORD dwType, dwData; + HKEY hkey; + LONG rc; + + if (!name) + return NULL; + + if (MultiByteToWideChar(CP_UTF8, 0, name, -1, wName, MAX_PATH) <= 0) { + usbi_dbg("Failed to convert environment variable name to wide string"); + return NULL; + } + wName[MAX_PATH - 1] = 0; // Be sure it's NUL terminated + + rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, ENVIRONMENT_REG_PATH, 0, KEY_QUERY_VALUE, &hkey); + if (rc != ERROR_SUCCESS) { + usbi_dbg("Failed to open registry key for getenv with error %d", rc); + return NULL; + } + + // Attempt to read the key + dwData = sizeof(wValue); + rc = RegQueryValueEx(hkey, wName, NULL, &dwType, + (LPBYTE)&wValue, &dwData); + RegCloseKey(hkey); + if (rc != ERROR_SUCCESS) { + usbi_dbg("Failed to read registry key value for getenv with error %d", rc); + return NULL; + } + if (dwType != REG_SZ) { + usbi_dbg("Registry value was of type %d instead of REG_SZ", dwType); + return NULL; + } + + // Success in reading the key, convert from WCHAR to char + if (WideCharToMultiByte(CP_UTF8, 0, + wValue, dwData / sizeof(*wValue), + value, MAX_PATH, + NULL, NULL) <= 0) { + usbi_dbg("Failed to convert environment variable value to narrow string"); + return NULL; + } + value[MAX_PATH - 1] = 0; // Be sure it's NUL terminated + return value; +} diff --git a/third_party/libusb/src/msvc/missing.h b/third_party/libusb/src/msvc/missing.h new file mode 100644 index 0000000..183b9d3 --- /dev/null +++ b/third_party/libusb/src/msvc/missing.h @@ -0,0 +1,32 @@ +/* + * Header file for missing WinCE functionality + * Copyright © 2012-2013 RealVNC Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MISSING_H +#define MISSING_H + +/* Windows CE doesn't have SleepEx() - Fallback to Sleep() */ +#define SleepEx(m, a) Sleep(m) + +/* Windows CE doesn't have any APIs to query environment variables. + * + * This contains a registry based implementation of getenv. + */ +char *getenv(const char *name); + +#endif diff --git a/third_party/libusb/windows-build.patch b/third_party/libusb/windows-build.patch index d2a478c..ef2dc04 100644 --- a/third_party/libusb/windows-build.patch +++ b/third_party/libusb/windows-build.patch @@ -1,41 +1,54 @@ diff --git a/libusb/libusb.h b/libusb/libusb.h -index 58b406f..1d0dd7d 100644 +index e8e1201..15bd0d5 100644 --- a/libusb/libusb.h +++ b/libusb/libusb.h -@@ -881,6 +881,12 @@ typedef void (LIBUSB_CALL *libusb_transfer_cb_fn)(struct libusb_transfer *transf - * completed, the library populates the transfer with the results and passes - * it back to the user. - */ -+ -+#if defined(OS_WIN) +@@ -25,6 +25,12 @@ + #define LIBUSB_H + + #ifdef _MSC_VER ++// Disable warning 4200 for [0]. +#pragma warning(push) -+#pragma warning(disable:4200) -+#endif // defined(OS_WIN) ++#pragma warning(disable: 4200) ++#endif + - struct libusb_transfer { - /** Handle of the device that this transfer will be submitted to */ - libusb_device_handle *dev_handle; -@@ -939,6 +945,10 @@ struct libusb_transfer { - ; - }; ++#ifdef _MSC_VER + /* on MS environments, the inline keyword is available in C++ only */ + #if !defined(__cplusplus) + #define inline __inline +@@ -1938,4 +1944,8 @@ void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx, + } + #endif -+#if defined(OS_WIN) ++#ifdef _MSC_VER +#pragma warning(pop) -+#endif // defined(OS_WIN) ++#endif + - /** \ingroup misc - * Capabilities supported by this instance of libusb. Test if the loaded - * library supports a given capability by calling -diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h -index ddbd680..7c2fae5 100644 ---- a/libusb/os/windows_usb.h -+++ b/libusb/os/windows_usb.h -@@ -101,7 +101,7 @@ const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0 - #if !defined(GUID_DEVINTERFACE_USB_HUB) - const GUID GUID_DEVINTERFACE_USB_HUB = { 0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8} }; #endif --const GUID GUID_NULL = { 0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; -+static const GUID GUID_NULL = { 0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; - +diff --git a/libusb/os/threads_posix.c b/libusb/os/threads_posix.c +index 9769f58..46f6db7 100644 +--- a/libusb/os/threads_posix.c ++++ b/libusb/os/threads_posix.c +@@ -20,11 +20,6 @@ + */ - /* + #if defined(__linux__) || defined(__OpenBSD__) +-# if defined(__linux__) +-# define _GNU_SOURCE +-# else +-# define _BSD_SOURCE +-# endif + # include <unistd.h> + # include <sys/syscall.h> + #elif defined(__APPLE__) +diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c +index 63357b1..51ce55d 100644 +--- a/libusb/os/windows_usb.c ++++ b/libusb/os/windows_usb.c +@@ -2142,6 +2142,7 @@ static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, + windows_handle_callback(transfer, io_result, io_size); + } else { + usbi_err(ctx, "could not find a matching transfer for fd %x", fds[i]); ++ usbi_mutex_unlock(&ctx->open_devs_lock); + return LIBUSB_ERROR_NOT_FOUND; + } + } |