summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-20 18:16:43 +0000
committerpfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-20 18:16:43 +0000
commitafed56f762adab7c30f9f91cc6e51308e75c8d5f (patch)
tree611fc8b0640055507f8cf74de8aa45eeaead2381
parent792431fdd053b8f1b3793bfe1eef3001aecaad27 (diff)
downloadchromium_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
-rw-r--r--third_party/libusb/README.chromium13
-rw-r--r--third_party/libusb/libusb.gyp67
-rw-r--r--third_party/libusb/src/AUTHORS43
-rw-r--r--third_party/libusb/src/INSTALL234
-rw-r--r--third_party/libusb/src/Makefile.am28
-rw-r--r--third_party/libusb/src/NEWS65
-rw-r--r--third_party/libusb/src/PORTING95
-rw-r--r--third_party/libusb/src/README22
-rw-r--r--third_party/libusb/src/RELEASE-NOTES35
-rw-r--r--third_party/libusb/src/THANKS8
-rw-r--r--third_party/libusb/src/TODO9
-rwxr-xr-xthird_party/libusb/src/autogen.sh19
-rw-r--r--third_party/libusb/src/config.h16
-rw-r--r--third_party/libusb/src/configure.ac222
-rw-r--r--third_party/libusb/src/doc/Makefile.am10
-rw-r--r--third_party/libusb/src/doc/doxygen.cfg.in1294
-rw-r--r--third_party/libusb/src/examples/Makefile.am21
-rw-r--r--third_party/libusb/src/examples/dpfp.c507
-rw-r--r--third_party/libusb/src/examples/dpfp_threaded.c545
-rw-r--r--third_party/libusb/src/examples/listdevs.c64
-rw-r--r--third_party/libusb/src/libusb-1.0.pc.in12
-rw-r--r--third_party/libusb/src/libusb/Makefile.am49
-rw-r--r--third_party/libusb/src/libusb/core.c837
-rw-r--r--third_party/libusb/src/libusb/descriptor.c745
-rw-r--r--third_party/libusb/src/libusb/hotplug.c320
-rw-r--r--third_party/libusb/src/libusb/hotplug.h82
-rw-r--r--third_party/libusb/src/libusb/interrupt.c4
-rw-r--r--third_party/libusb/src/libusb/interrupt.h2
-rw-r--r--third_party/libusb/src/libusb/io.c642
-rw-r--r--third_party/libusb/src/libusb/libusb-1.0.def120
-rw-r--r--third_party/libusb/src/libusb/libusb-1.0.rc56
-rw-r--r--third_party/libusb/src/libusb/libusb.h640
-rw-r--r--third_party/libusb/src/libusb/libusbi.h299
-rw-r--r--third_party/libusb/src/libusb/os/darwin_usb.c1115
-rw-r--r--third_party/libusb/src/libusb/os/darwin_usb.h89
-rw-r--r--third_party/libusb/src/libusb/os/linux_netlink.c254
-rw-r--r--third_party/libusb/src/libusb/os/linux_udev.c273
-rw-r--r--third_party/libusb/src/libusb/os/linux_usbfs.c1288
-rw-r--r--third_party/libusb/src/libusb/os/linux_usbfs.h46
-rw-r--r--third_party/libusb/src/libusb/os/openbsd_usb.c17
-rw-r--r--third_party/libusb/src/libusb/os/poll_posix.c51
-rw-r--r--third_party/libusb/src/libusb/os/poll_posix.h19
-rw-r--r--third_party/libusb/src/libusb/os/poll_windows.c201
-rw-r--r--third_party/libusb/src/libusb/os/poll_windows.h18
-rw-r--r--third_party/libusb/src/libusb/os/threads_posix.c42
-rw-r--r--third_party/libusb/src/libusb/os/threads_posix.h6
-rw-r--r--third_party/libusb/src/libusb/os/threads_windows.c29
-rw-r--r--third_party/libusb/src/libusb/os/threads_windows.h7
-rw-r--r--third_party/libusb/src/libusb/os/wince_usb.c1015
-rw-r--r--third_party/libusb/src/libusb/os/wince_usb.h131
-rw-r--r--third_party/libusb/src/libusb/os/windows_common.h108
-rw-r--r--third_party/libusb/src/libusb/os/windows_usb.c2110
-rw-r--r--third_party/libusb/src/libusb/os/windows_usb.h532
-rw-r--r--third_party/libusb/src/libusb/strerror.c184
-rw-r--r--third_party/libusb/src/libusb/sync.c87
-rw-r--r--third_party/libusb/src/libusb/version.h20
-rw-r--r--third_party/libusb/src/libusb/version_nano.h17
-rw-r--r--third_party/libusb/src/msvc/config.h47
-rw-r--r--third_party/libusb/src/msvc/ddk_build.cmd106
-rw-r--r--third_party/libusb/src/msvc/errno.h102
-rw-r--r--third_party/libusb/src/msvc/libusb.dsw56
-rw-r--r--third_party/libusb/src/msvc/libusb_dll.dsp190
-rw-r--r--third_party/libusb/src/msvc/libusb_sources36
-rw-r--r--third_party/libusb/src/msvc/libusb_static.dsp170
-rw-r--r--third_party/libusb/src/msvc/listdevs.dsp103
-rw-r--r--third_party/libusb/src/msvc/listdevs_sources19
-rw-r--r--third_party/libusb/src/msvc/missing.c80
-rw-r--r--third_party/libusb/src/msvc/missing.h32
-rw-r--r--third_party/libusb/windows-build.patch75
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(&timestamp_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,
&reg_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, &current_interface, USB_API_WINUSB) != LIBUSB_SUCCESS) {
+ if (auto_claim(transfer, &current_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, &current_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;
+ }
+ }