aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:04:02 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:04:02 +0000
commite7754421333bfa6eebd6c3c58926d1d4583f3064 (patch)
tree224d68527b55af2749b702b4ba564006ff6676cb
parentd41e4b08208a3eee7262194ceb3089c52ef6c141 (diff)
parentf8c697b87eae8fc259edd1dc5695e2753163bbfe (diff)
downloadlibusb-android14-mainline-permission-release.tar.gz
Change-Id: I37d050272d50922434967b0469d4a1d1b41a6093
-rw-r--r--.github/cifuzz.yml26
-rw-r--r--.github/workflows/linux.yml39
-rw-r--r--.github/workflows/macos.yml36
-rw-r--r--.github/workflows/msys2.yml21
-rwxr-xr-x.private/ci-container-build.sh70
-rwxr-xr-x.private/post-rewrite.sh2
-rwxr-xr-x.private/pre-commit.sh2
-rw-r--r--.travis.yml1
-rw-r--r--AUTHORS39
-rw-r--r--Android.bp1
-rw-r--r--ChangeLog31
-rw-r--r--METADATA6
-rw-r--r--Xcode/libusb.xcodeproj/project.pbxproj83
-rw-r--r--android/README144
-rw-r--r--android/examples/unrooted_android.c300
-rw-r--r--android/examples/unrooted_android.h36
-rw-r--r--android/jni/examples.mk42
-rw-r--r--android/jni/libusb.mk10
-rw-r--r--android/jni/tests.mk8
-rw-r--r--appveyor.yml12
-rwxr-xr-xautogen.sh6
-rwxr-xr-xbootstrap.sh2
-rw-r--r--configure.ac46
-rw-r--r--doc/Makefile.in2
-rw-r--r--doc/doxygen.cfg.in215
-rw-r--r--examples/dpfp.c27
-rw-r--r--examples/ezusb.c20
-rw-r--r--examples/sam3u_benchmark.c6
-rw-r--r--examples/xusb.c14
l---------include/libusb/hotplug.h1
-rw-r--r--libusb/Makefile.am4
-rw-r--r--libusb/core.c454
-rw-r--r--libusb/descriptor.c26
-rw-r--r--libusb/hotplug.c256
-rw-r--r--libusb/hotplug.h105
-rw-r--r--libusb/io.c146
-rw-r--r--libusb/libusb-1.0.def6
-rw-r--r--libusb/libusb.h55
-rw-r--r--libusb/libusbi.h153
-rw-r--r--libusb/os/darwin_usb.c668
-rw-r--r--libusb/os/darwin_usb.h52
-rw-r--r--libusb/os/events_posix.c6
-rw-r--r--libusb/os/events_windows.c4
-rw-r--r--libusb/os/haiku_pollfs.cpp10
-rw-r--r--libusb/os/haiku_usb_backend.cpp6
-rw-r--r--libusb/os/linux_netlink.c18
-rw-r--r--libusb/os/linux_udev.c8
-rw-r--r--libusb/os/linux_usbfs.c269
-rw-r--r--libusb/os/netbsd_usb.c40
-rw-r--r--libusb/os/openbsd_usb.c38
-rw-r--r--libusb/os/sunos_usb.c218
-rw-r--r--libusb/os/windows_common.c155
-rw-r--r--libusb/os/windows_common.h35
-rw-r--r--libusb/os/windows_usbdk.c3
-rw-r--r--libusb/os/windows_winusb.c475
-rw-r--r--libusb/os/windows_winusb.h69
-rw-r--r--libusb/sync.c2
-rw-r--r--libusb/version.h2
-rw-r--r--libusb/version_nano.h2
-rw-r--r--msvc/libusb_dll_2013.vcxproj1
-rw-r--r--msvc/libusb_dll_2013.vcxproj.filters3
-rw-r--r--msvc/libusb_dll_2015.vcxproj1
-rw-r--r--msvc/libusb_dll_2015.vcxproj.filters3
-rw-r--r--msvc/libusb_dll_2017.vcxproj1
-rw-r--r--msvc/libusb_dll_2017.vcxproj.filters3
-rw-r--r--msvc/libusb_dll_2019.vcxproj1
-rw-r--r--msvc/libusb_dll_2019.vcxproj.filters3
-rw-r--r--msvc/libusb_static_2013.vcxproj1
-rw-r--r--msvc/libusb_static_2013.vcxproj.filters3
-rw-r--r--msvc/libusb_static_2015.vcxproj1
-rw-r--r--msvc/libusb_static_2015.vcxproj.filters3
-rw-r--r--msvc/libusb_static_2017.vcxproj1
-rw-r--r--msvc/libusb_static_2017.vcxproj.filters3
-rw-r--r--msvc/libusb_static_2019.vcxproj1
-rw-r--r--msvc/libusb_static_2019.vcxproj.filters3
-rw-r--r--test0
-rw-r--r--tests/Makefile.am13
-rw-r--r--tests/stress.c7
-rw-r--r--tests/umockdev.c1175
79 files changed, 4238 insertions, 1522 deletions
diff --git a/.github/cifuzz.yml b/.github/cifuzz.yml
new file mode 100644
index 0000000..2a80713
--- /dev/null
+++ b/.github/cifuzz.yml
@@ -0,0 +1,26 @@
+name: CIFuzz
+on: [pull_request]
+jobs:
+ Fuzzing:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Build Fuzzers
+ id: build
+ uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
+ with:
+ oss-fuzz-project-name: 'libusb'
+ dry-run: false
+ language: c
+ - name: Run Fuzzers
+ uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
+ with:
+ oss-fuzz-project-name: 'libusb'
+ fuzz-seconds: 600
+ dry-run: false
+ language: c
+ - name: Upload Crash
+ uses: actions/upload-artifact@v1
+ if: failure() && steps.build.outcome == 'success'
+ with:
+ name: artifacts
+ path: ./out/artifacts
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
new file mode 100644
index 0000000..ce5679e
--- /dev/null
+++ b/.github/workflows/linux.yml
@@ -0,0 +1,39 @@
+name: linux
+
+# Controls when the action will run. Triggers the workflow on push or pull request
+# events but only for the master branch
+on: [push, pull_request]
+
+# A workflow run is made up of one or more jobs that can run
+# sequentially or in parallel
+jobs:
+ # This workflow contains a single job called "build"
+ build:
+ runs-on: ubuntu-latest
+
+ # Steps represent a sequence of tasks that will be executed as part of the job
+ steps:
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job
+ # can access it
+ - uses: actions/checkout@v2
+
+ - name: setup prerequisites
+ shell: bash
+ run: |
+ sudo apt update
+ sudo apt install autoconf automake libtool libudev-dev m4
+
+ - name: bootstrap
+ shell: bash
+ run: ./bootstrap.sh
+
+ - name: netlink
+ shell: bash
+ run: .private/ci-build.sh --build-dir build-netlink -- --disable-udev
+
+ - name: udev
+ shell: bash
+ run: .private/ci-build.sh --build-dir build-udev -- --enable-udev
+
+ - name: umockdev test
+ run: .private/ci-container-build.sh docker.io/amd64/ubuntu:rolling
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
new file mode 100644
index 0000000..6304b91
--- /dev/null
+++ b/.github/workflows/macos.yml
@@ -0,0 +1,36 @@
+name: macOS
+
+# Controls when the action will run. Triggers the workflow on push or pull request
+# events but only for the master branch
+on: [push, pull_request]
+
+# A workflow run is made up of one or more jobs that can run
+# sequentially or in parallel
+jobs:
+ # This workflow contains a single job called "build"
+ build:
+ runs-on: macos-latest
+
+ # Steps represent a sequence of tasks that will be executed as part of the job
+ steps:
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job
+ # can access it
+ - uses: actions/checkout@v2
+
+ - name: setup prerequisites
+ shell: bash
+ run: |
+ brew update
+ brew install autoconf automake libtool m4
+
+ - name: bootstrap
+ shell: bash
+ run: ./bootstrap.sh
+
+ - name: compile
+ shell: bash
+ run: .private/ci-build.sh --build-dir build
+
+ - name: Xcode
+ shell: bash
+ run: cd Xcode && xcodebuild -project libusb.xcodeproj
diff --git a/.github/workflows/msys2.yml b/.github/workflows/msys2.yml
new file mode 100644
index 0000000..116ad18
--- /dev/null
+++ b/.github/workflows/msys2.yml
@@ -0,0 +1,21 @@
+name: MSYS2 build
+on: [push, pull_request]
+
+jobs:
+ build:
+ runs-on: windows-latest
+ defaults:
+ run:
+ shell: msys2 {0}
+ steps:
+ - uses: actions/checkout@v2
+ - uses: msys2/setup-msys2@v2
+ with:
+ msystem: MINGW64
+ update: true
+ install: git mingw-w64-x86_64-cc mingw-w64-x86_64-autotools
+ - name: CI-Build
+ run: |
+ echo 'Running in MSYS2!'
+ ./bootstrap.sh
+ ./.private/ci-build.sh --build-dir build-msys2
diff --git a/.private/ci-container-build.sh b/.private/ci-container-build.sh
new file mode 100755
index 0000000..ac8e707
--- /dev/null
+++ b/.private/ci-container-build.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+set -eu
+
+# keep container around if $DEBUG is set
+[ -n "${DEBUG:-}" ] || OPTS="--rm"
+
+if type podman >/dev/null 2>&1; then
+ RUNC=podman
+else
+ RUNC="sudo docker"
+fi
+
+MOUNT_MODE=":ro"
+
+$RUNC run --interactive ${RUNC_OPTIONS:-} ${OPTS:-} --volume `pwd`:/source${MOUNT_MODE:-} ${1:-docker.io/amd64/ubuntu:rolling} /bin/bash << EOF
+set -ex
+
+# avoid meson exit code 125; https://github.com/containers/podman/issues/11540
+trap '[ \$? -eq 0 ] || exit 1' EXIT
+
+# go-faster apt
+echo 'Acquire::Languages "none";' > /etc/apt/apt.conf.d/90nolanguages
+
+# upgrade
+export DEBIAN_FRONTEND=noninteractive
+apt-get update
+apt-get install -y eatmydata
+eatmydata apt-get -y --purge dist-upgrade
+
+# install build and test dependencies
+eatmydata apt-get install -y make libtool libudev-dev pkg-config umockdev libumockdev-dev
+
+# run build as user
+useradd build
+su -s /bin/bash - build << EOG
+set -ex
+
+mkdir "/tmp/builddir"
+cd "/tmp/builddir"
+
+CFLAGS="-O2"
+
+# enable extra warnings
+CFLAGS+=" -Winline"
+CFLAGS+=" -Wmissing-include-dirs"
+CFLAGS+=" -Wnested-externs"
+CFLAGS+=" -Wpointer-arith"
+CFLAGS+=" -Wredundant-decls"
+CFLAGS+=" -Wswitch-enum"
+export CFLAGS
+
+echo ""
+echo "Configuring ..."
+/source/configure --enable-examples-build --enable-tests-build
+
+echo ""
+echo "Building ..."
+make -j4 -k
+
+echo ""
+echo "Running umockdev tests ..."
+tests/umockdev
+
+echo "Running stress tests ..."
+tests/stress
+EOG
+EOF
+
+
diff --git a/.private/post-rewrite.sh b/.private/post-rewrite.sh
index 60ec3e4..d1f9999 100755
--- a/.private/post-rewrite.sh
+++ b/.private/post-rewrite.sh
@@ -6,7 +6,7 @@
# .git/hooks/ with the following content:
# #!/bin/sh
# if [ -x .private/post-rewrite.sh ]; then
-# source .private/post-rewrite.sh
+# . .private/post-rewrite.sh
# fi
#
# NOTE: These versioning hooks are intended to be used *INTERNALLY* by the
diff --git a/.private/pre-commit.sh b/.private/pre-commit.sh
index 48328f8..1c30c0f 100755
--- a/.private/pre-commit.sh
+++ b/.private/pre-commit.sh
@@ -8,7 +8,7 @@
# .git/hooks/ with the following content:
# #!/bin/sh
# if [ -x .private/pre-commit.sh ]; then
-# source .private/pre-commit.sh
+# . .private/pre-commit.sh
# fi
#
# NOTE: These versioning hooks are intended to be used *INTERNALLY* by the
diff --git a/.travis.yml b/.travis.yml
index 35326e3..e92f0c4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -47,7 +47,6 @@ addons:
- automake
- libtool
- m4
- update: true
before_script:
- ./bootstrap.sh
diff --git a/AUTHORS b/AUTHORS
index a366189..8f91512 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -12,6 +12,7 @@ Copyright © 2013-2018 Chris Dickens <christopher.a.dickens@gmail.com>
Other contributors:
Aaron Luft
+Adam Korcz
Adrian Bunk
Adrien Destugues
Akshay Jaggi
@@ -24,10 +25,12 @@ Alexander Stein
Alex Vatchenko
Andrew Aldridge
Andrew Fernandes
+Andrew Goodney
Andy Chunyu
Andy McFadden
Angus Gratton
Anil Nair
+Ankur Verma
Anthony Clay
Antonio Ospite
Artem Egorkine
@@ -38,21 +41,28 @@ Baruch Siach
Bastien Nocera
Bei Zhang
Bence Csokas
+Benjamin Berg
Benjamin Dobell
+Bohdan Tymkiv
Brent Rector
+Bruno Harbulot
Carl Karsten
-Chris Zhu
Christophe Zeitouny
+Chris Zhu
Chunyu Xie
Colin Walters
+Craig Hutchinson
Dave Camarillo
David Engraf
Davidlohr Bueso
David Moore
Dmitry Fleytman
Dmitry Kostjuchenko
+Dmitry Zakablukov
Doug Johnston
Evan Hunter
+Evan Miller
+Fabrice Fontaine
Federico Manzan
Felipe Balbi
Florian Albrechtskirchinger
@@ -60,10 +70,12 @@ Francesco Montorsi
Francisco Facioni
Frank Li
Frederik Carlier
+Freek Dijkstra
Gaurav Gupta
Graeme Gill
Greg Kroah-Hartman
Gustavo Zacarias
+Haidong Zheng
Hans Ulrich Niedermann
Harry Mallon
Hector Martin
@@ -76,6 +88,7 @@ Jakub Klama
James Hanko
Jeffrey Nichols
Jie Zhang
+Jim Chen
Johann Richard
John Keeping
John Sheu
@@ -86,35 +99,48 @@ Josh Gao
Joshua Blake
Joshua Hou
Juan Cruz Viotti
+Julian Scheel
Justin Bischoff
Karsten Koenig
+Keith Ahluwalia
Kenjiro Tsuji
-KIMURA Masaru
+Kimura Masaru
Konrad Rzepecki
Kuangye Guo
Lars Kanis
Lars Wirzenius
Lei Chen
Léo Lam
+Liang Yunwang
Luca Longinotti
+Luz Paz
+Mac Wang
+Marco Trevisan (Treviño)
Marcus Meissner
+Mark Kuo
Markus Heidelberg
Martin Ettl
Martin Koegler
+Martin Ling
Martin Thierer
+Mathias Hjärtström
Matthew Stapleton
Matthias Bolte
+Michael Dickens
Michel Zou
Mike Frysinger
Mikhail Gusarov
Mikolaj Kucharski
Morgan Leborgne
Moritz Fischer
+Nancy Li
Nia Alarie
Nicholas Corgan
Omri Iluz
Orin Eman
+Ozkan Sezer
Patrick Stewart
+Paul Cercueil
Paul Fertser
Paul Qureshi
Pekka Nikander
@@ -123,12 +149,15 @@ Pino Toscano
Rob Walker
Romain Vimont
Roman Kalashnikov
+Ryan Hileman
+Ryan Schmidt
Saleem Rashid
Sameeh Jubran
Sean McBride
Sebastian Pipping
Sebastian von Ohr
Sergey Serb
+Shawn Hoffman
Simon Haggett
Simon Newton
Slash Gordon
@@ -157,13 +186,17 @@ Vladimir Beloborodov
William Orr
William Skellenger
Xiaofan Chen
+Yegor Yefremov
Zhiqiang Liu
Zoltán Kovács
Сергей Валерьевич
Ларионов Даниил
Роман Донченко
-liangyunwang
+jonner
+orbitcowboy
+osy
parafin
RipleyTom
+Seneral
saur0n
winterrace
diff --git a/Android.bp b/Android.bp
index 4befb96..93e5c1b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -96,6 +96,7 @@ cc_library {
host_ldlibs: [
"-framework CoreFoundation",
"-framework IOKit",
+ "-framework Security",
"-lobjc"
],
diff --git a/ChangeLog b/ChangeLog
index df47bd5..326a9b3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,33 @@
For detailed information about the changes below, please see the git log or
visit: http://log.libusb.info
+2022-04-10: v1.0.26
+* Fix regression with transfer free's after closing device
+* Fix regression with destroyed context if API is misused
+* Workaround for applications using missing default context
+* Fix hotplog enumeration regression
+* Fix Windows isochronous transfer regression since 1.0.24
+* Fix macOS exit crash in some multi-context cases
+* Build fixes for various platforms and configurations
+* Fix Windows HID multi-interface product string retrieval
+* Update isochronous OUT packet actual lengths on Windows
+* Add interface bound checking for broken devices
+* Add umockdev tests on Linux
+
+2022-01-31: v1.0.25
+* Linux: Fix regression with some particular devices
+* Linux: Fix regression with libusb_handle_events_timeout_completed()
+* Linux: Fix regression with cpu usage in libusb_bulk_transfer
+* Darwin (macOS): Add support for detaching kernel drivers with authorization.
+* Darwin (macOS): Do not drop partial data on timeout.
+* Darwin (macOS): Silence pipe error in set_interface_alt_setting().
+* Windows: Fix HID backend missing byte
+* Windows: Fix segfault with libusbk driver
+* Windows: Fix regression when using libusb0 driver
+* Windows: Support LIBUSB_TRANSFER_ADD_ZERO_PACKET on winusb
+* New NO_DEVICE_DISCOVERY option replaces WEAK_AUTHORITY option
+* Various other bug fixes and improvements
+
2020-12-09: v1.0.24
* Add new platform abstraction (#252)
* Add Null POSIX backend
@@ -12,7 +39,7 @@ visit: http://log.libusb.info
* Darwin (macOS): use IOUSBDevice as darwin_device_class explicitly (#693)
* Linux: Drop support for kernel older than 2.6.32
* Linux: Provide an event thread name (#689)
-* Linux: Wait until all USBs have been reaped before freeing them (#607)
+* Linux: Wait until all URBs have been reaped before freeing them (#607)
* NetBSD: Recognize device timeouts (#710)
* OpenBSD: Allow opening ugen devices multiple times (#763)
* OpenBSD: Support libusb_get_port_number() (#764)
@@ -61,7 +88,7 @@ visit: http://log.libusb.info
* Windows: Fix enumeration problems on Windows 8 and later
* Windows: Major rework of poll() emulation
* Windows: Numerous HID API fixes
-* Windows: Support cancelation of individual transfers (Vista and later)
+* Windows: Support cancellation of individual transfers (Vista and later)
* Various other bug fixes and improvements
2016-10-01: v1.0.21
diff --git a/METADATA b/METADATA
index 5c9b974..b1d55ae 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@ third_party {
type: GIT
value: "https://github.com/libusb/libusb"
}
- version: "v1.0.24"
+ version: "v1.0.26"
license_type: RESTRICTED
last_upgrade_date {
- year: 2020
- month: 12
+ year: 2022
+ month: 8
day: 10
}
}
diff --git a/Xcode/libusb.xcodeproj/project.pbxproj b/Xcode/libusb.xcodeproj/project.pbxproj
index fcda7e2..759a102 100644
--- a/Xcode/libusb.xcodeproj/project.pbxproj
+++ b/Xcode/libusb.xcodeproj/project.pbxproj
@@ -56,7 +56,6 @@
008FC0211628BC5200BC5BE2 /* ezusb.c in Sources */ = {isa = PBXBuildFile; fileRef = 008FBFDC1628BA0E00BC5BE2 /* ezusb.c */; };
008FC0301628BC7400BC5BE2 /* listdevs.c in Sources */ = {isa = PBXBuildFile; fileRef = 008FBFE71628BA0E00BC5BE2 /* listdevs.c */; };
1438D77A17A2ED9F00166101 /* hotplug.c in Sources */ = {isa = PBXBuildFile; fileRef = 1438D77817A2ED9F00166101 /* hotplug.c */; };
- 1438D77B17A2ED9F00166101 /* hotplug.h in Headers */ = {isa = PBXBuildFile; fileRef = 1438D77917A2ED9F00166101 /* hotplug.h */; };
1438D77F17A2F0EA00166101 /* strerror.c in Sources */ = {isa = PBXBuildFile; fileRef = 1438D77E17A2F0EA00166101 /* strerror.c */; };
2018D95F24E453BA001589B2 /* events_posix.c in Sources */ = {isa = PBXBuildFile; fileRef = 2018D95E24E453BA001589B2 /* events_posix.c */; };
2018D96124E453D0001589B2 /* events_posix.h in Headers */ = {isa = PBXBuildFile; fileRef = 2018D96024E453D0001589B2 /* events_posix.h */; };
@@ -66,6 +65,32 @@
20951C0625630F8F00ED6351 /* ezusb.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBFDD1628BA0E00BC5BE2 /* ezusb.h */; };
20951C0F25630FD300ED6351 /* libusb_testlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 008A23CA236C849A004854AA /* libusb_testlib.h */; };
20951C152563125200ED6351 /* libusb.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBF5A1628B7E800BC5BE2 /* libusb.h */; };
+ CEA0F5EF26321FAA00ADF3EC /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBFA41628B84200BC5BE2 /* config.h */; };
+ CEA0F5F026321FAA00ADF3EC /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBFA41628B84200BC5BE2 /* config.h */; };
+ CEA0F5F126321FAA00ADF3EC /* libusb.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBF5A1628B7E800BC5BE2 /* libusb.h */; };
+ CEA0F5F226321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 008FBF311628B79300BC5BE2 /* libusb-1.0.0.dylib */; };
+ CEA0F5F326321FAA00ADF3EC /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBFA41628B84200BC5BE2 /* config.h */; };
+ CEA0F5F426321FAA00ADF3EC /* libusb.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBF5A1628B7E800BC5BE2 /* libusb.h */; };
+ CEA0F5F526321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 008FBF311628B79300BC5BE2 /* libusb-1.0.0.dylib */; };
+ CEA0F5F626321FAA00ADF3EC /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBFA41628B84200BC5BE2 /* config.h */; };
+ CEA0F5F726321FAA00ADF3EC /* libusb.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBF5A1628B7E800BC5BE2 /* libusb.h */; };
+ CEA0F5F826321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 008FBF311628B79300BC5BE2 /* libusb-1.0.0.dylib */; };
+ CEA0F5F926321FAA00ADF3EC /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBFA41628B84200BC5BE2 /* config.h */; };
+ CEA0F5FA26321FAA00ADF3EC /* libusb.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBF5A1628B7E800BC5BE2 /* libusb.h */; };
+ CEA0F5FB26321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 008FBF311628B79300BC5BE2 /* libusb-1.0.0.dylib */; };
+ CEA0F5FC26321FAA00ADF3EC /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBFA41628B84200BC5BE2 /* config.h */; };
+ CEA0F5FD26321FAA00ADF3EC /* libusb.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBF5A1628B7E800BC5BE2 /* libusb.h */; };
+ CEA0F5FE26321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 008FBF311628B79300BC5BE2 /* libusb-1.0.0.dylib */; };
+ CEA0F5FF26321FAA00ADF3EC /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBFA41628B84200BC5BE2 /* config.h */; };
+ CEA0F60026321FAA00ADF3EC /* libusb.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBF5A1628B7E800BC5BE2 /* libusb.h */; };
+ CEA0F60126321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 008FBF311628B79300BC5BE2 /* libusb-1.0.0.dylib */; };
+ CEA0F60226321FAA00ADF3EC /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBFA41628B84200BC5BE2 /* config.h */; };
+ CEA0F60326321FAA00ADF3EC /* libusb.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBF5A1628B7E800BC5BE2 /* libusb.h */; };
+ CEA0F60426321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 008FBF311628B79300BC5BE2 /* libusb-1.0.0.dylib */; };
+ CEA0F60526321FAA00ADF3EC /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBFA41628B84200BC5BE2 /* config.h */; };
+ CEA0F60626321FAA00ADF3EC /* libusb.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBF5A1628B7E800BC5BE2 /* libusb.h */; };
+ CEA0F60726321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 008FBF311628B79300BC5BE2 /* libusb-1.0.0.dylib */; };
+ CEA45DFB2634CDFA002FA97D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEDCEA6E2632200A00F7AA49 /* Security.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -240,7 +265,6 @@
008FC0151628BC0300BC5BE2 /* fxload */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fxload; sourceTree = BUILT_PRODUCTS_DIR; };
008FC0261628BC6B00BC5BE2 /* listdevs */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = listdevs; sourceTree = BUILT_PRODUCTS_DIR; };
1438D77817A2ED9F00166101 /* hotplug.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = hotplug.c; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
- 1438D77917A2ED9F00166101 /* hotplug.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = hotplug.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
1438D77E17A2F0EA00166101 /* strerror.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = strerror.c; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
1443EE8416417E63007E0579 /* common.xcconfig */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = text.xcconfig; path = common.xcconfig; sourceTree = SOURCE_ROOT; tabWidth = 4; usesTabs = 1; };
1443EE8516417E63007E0579 /* debug.xcconfig */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = text.xcconfig; path = debug.xcconfig; sourceTree = SOURCE_ROOT; tabWidth = 4; usesTabs = 1; };
@@ -254,6 +278,7 @@
20468D6E243298C100650534 /* sam3u_benchmark.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sam3u_benchmark.c; sourceTree = "<group>"; usesTabs = 1; };
20468D75243298D300650534 /* testlibusb */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testlibusb; sourceTree = BUILT_PRODUCTS_DIR; };
20468D7C2432990000650534 /* testlibusb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testlibusb.c; sourceTree = "<group>"; usesTabs = 1; };
+ CEDCEA6E2632200A00F7AA49 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -261,7 +286,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 006AD4261C8C5AD9007F8C6A /* libusb-1.0.0.dylib in Frameworks */,
+ CEA0F5F826321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -269,7 +294,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 006AD4261C8C5AD9007F8C6A /* libusb-1.0.0.dylib in Frameworks */,
+ CEA0F60126321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -279,6 +304,7 @@
files = (
008FBFAB1628B8CB00BC5BE2 /* libobjc.dylib in Frameworks */,
008FBFA91628B88000BC5BE2 /* IOKit.framework in Frameworks */,
+ CEA45DFB2634CDFA002FA97D /* Security.framework in Frameworks */,
008FBFA71628B87000BC5BE2 /* CoreFoundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -287,7 +313,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 006AD4261C8C5AD9007F8C6A /* libusb-1.0.0.dylib in Frameworks */,
+ CEA0F60726321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -303,7 +329,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 006AD4261C8C5AD9007F8C6A /* libusb-1.0.0.dylib in Frameworks */,
+ CEA0F5F226321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -311,7 +337,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 006AD4261C8C5AD9007F8C6A /* libusb-1.0.0.dylib in Frameworks */,
+ CEA0F5F526321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -319,7 +345,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 006AD4261C8C5AD9007F8C6A /* libusb-1.0.0.dylib in Frameworks */,
+ CEA0F5FB26321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -327,7 +353,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 006AD4261C8C5AD9007F8C6A /* libusb-1.0.0.dylib in Frameworks */,
+ CEA0F5FE26321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -335,7 +361,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 006AD4261C8C5AD9007F8C6A /* libusb-1.0.0.dylib in Frameworks */,
+ CEA0F60426321FAA00ADF3EC /* libusb-1.0.0.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -389,7 +415,6 @@
008FBF541628B7E800BC5BE2 /* core.c */,
008FBF551628B7E800BC5BE2 /* descriptor.c */,
1438D77817A2ED9F00166101 /* hotplug.c */,
- 1438D77917A2ED9F00166101 /* hotplug.h */,
008FBF561628B7E800BC5BE2 /* io.c */,
008FBF5A1628B7E800BC5BE2 /* libusb.h */,
008FBF671628B7E800BC5BE2 /* libusbi.h */,
@@ -453,6 +478,7 @@
008FBFAA1628B8CB00BC5BE2 /* libobjc.dylib */,
008FBFA81628B88000BC5BE2 /* IOKit.framework */,
008FBFA61628B87000BC5BE2 /* CoreFoundation.framework */,
+ CEDCEA6E2632200A00F7AA49 /* Security.framework */,
);
name = Apple;
path = ../libusb;
@@ -469,7 +495,6 @@
008FBFA51628B84200BC5BE2 /* config.h in Headers */,
008FBF931628B7E800BC5BE2 /* darwin_usb.h in Headers */,
2018D96124E453D0001589B2 /* events_posix.h in Headers */,
- 1438D77B17A2ED9F00166101 /* hotplug.h in Headers */,
008FBF901628B7E800BC5BE2 /* libusbi.h in Headers */,
008FBF9B1628B7E800BC5BE2 /* threads_posix.h in Headers */,
008FBFA11628B7E800BC5BE2 /* version.h in Headers */,
@@ -481,7 +506,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
- 008FBFA51628B84200BC5BE2 /* config.h in Headers */,
+ CEA0F5EF26321FAA00ADF3EC /* config.h in Headers */,
20951C152563125200ED6351 /* libusb.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -490,8 +515,8 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
- 008FBFA51628B84200BC5BE2 /* config.h in Headers */,
- 20951C152563125200ED6351 /* libusb.h in Headers */,
+ CEA0F5F026321FAA00ADF3EC /* config.h in Headers */,
+ CEA0F5F126321FAA00ADF3EC /* libusb.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -499,9 +524,9 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
- 008FBFA51628B84200BC5BE2 /* config.h in Headers */,
+ CEA0F5F326321FAA00ADF3EC /* config.h in Headers */,
20951C0625630F8F00ED6351 /* ezusb.h in Headers */,
- 20951C152563125200ED6351 /* libusb.h in Headers */,
+ CEA0F5F426321FAA00ADF3EC /* libusb.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -509,8 +534,8 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
- 008FBFA51628B84200BC5BE2 /* config.h in Headers */,
- 20951C152563125200ED6351 /* libusb.h in Headers */,
+ CEA0F5F626321FAA00ADF3EC /* config.h in Headers */,
+ CEA0F5F726321FAA00ADF3EC /* libusb.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -518,8 +543,8 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
- 008FBFA51628B84200BC5BE2 /* config.h in Headers */,
- 20951C152563125200ED6351 /* libusb.h in Headers */,
+ CEA0F5F926321FAA00ADF3EC /* config.h in Headers */,
+ CEA0F5FA26321FAA00ADF3EC /* libusb.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -527,8 +552,8 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
- 008FBFA51628B84200BC5BE2 /* config.h in Headers */,
- 20951C152563125200ED6351 /* libusb.h in Headers */,
+ CEA0F5FC26321FAA00ADF3EC /* config.h in Headers */,
+ CEA0F5FD26321FAA00ADF3EC /* libusb.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -536,9 +561,9 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
- 008FBFA51628B84200BC5BE2 /* config.h in Headers */,
+ CEA0F5FF26321FAA00ADF3EC /* config.h in Headers */,
20951C0F25630FD300ED6351 /* libusb_testlib.h in Headers */,
- 20951C152563125200ED6351 /* libusb.h in Headers */,
+ CEA0F60026321FAA00ADF3EC /* libusb.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -546,8 +571,8 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
- 008FBFA51628B84200BC5BE2 /* config.h in Headers */,
- 20951C152563125200ED6351 /* libusb.h in Headers */,
+ CEA0F60226321FAA00ADF3EC /* config.h in Headers */,
+ CEA0F60326321FAA00ADF3EC /* libusb.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -555,8 +580,8 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
- 008FBFA51628B84200BC5BE2 /* config.h in Headers */,
- 20951C152563125200ED6351 /* libusb.h in Headers */,
+ CEA0F60526321FAA00ADF3EC /* config.h in Headers */,
+ CEA0F60626321FAA00ADF3EC /* libusb.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/android/README b/android/README
index 32024ef..4af4c3d 100644
--- a/android/README
+++ b/android/README
@@ -48,69 +48,105 @@ application package (APK) file, provided ndk-build is invoked before
the package is built.
-For a rooted device it is possible to install libusb into the system
-image of a running device:
+Runtime Permissions:
+--------------------
- 1. Enable ADB on the device.
+The Runtime Permissions on Android can be transferred from Java to Native
+over the following approach:
- 2. Connect the device to a machine running ADB.
+ JAVA:
- 3. Execute the following commands on the machine
- running ADB:
+ --> Obtain USB permissions over the android.hardware.usb.UsbManager class
- # Make the system partition writable
- adb shell su -c "mount -o remount,rw /system"
+ usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
+ HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
+ for (UsbDevice usbDevice : deviceList.values()) {
+ usbManager.requestPermission(usbDevice, mPermissionIntent);
+ }
- # Install libusb
- adb push obj/local/armeabi/libusb1.0.so /sdcard/
- adb shell su -c "cat > /system/lib/libusb1.0.so < /sdcard/libusb1.0.so"
- adb shell rm /sdcard/libusb1.0.so
+ --> Get the native FileDescriptor of the UsbDevice and transfer it to
+ Native over JNI or JNA
- # Install the samples and tests
- for B in listdevs fxload xusb sam3u_benchmark hotplugtest stress
- do
- adb push "obj/local/armeabi/$B" /sdcard/
- adb shell su -c "cat > /system/bin/$B < /sdcard/$B"
- adb shell su -c "chmod 0755 /system/bin/$B"
- adb shell rm "/sdcard/$B"
- done
+ UsbDeviceConnection usbDeviceConnection = usbManager.openDevice(camDevice);
+ int fileDescriptor = usbDeviceConnection.getFileDescriptor();
- # Make the system partition read only again
- adb shell su -c "mount -o remount,ro /system"
+ --> JNA sample method:
- # Run listdevs to
- adb shell su -c "listdevs"
+ JNA.INSTANCE.set_the_native_Descriptor(fileDescriptor);
- 4. If your device only has a single OTG port then ADB can generally
- be switched to using Wifi with the following commands when connected
- via USB:
+ NATIVE:
- adb shell netcfg
- # Note the wifi IP address of the phone
- adb tcpip 5555
- # Use the IP address from netcfg
- adb connect 192.168.1.123:5555
+ --> Initialize libusb on Android
-Runtime Permissions:
---------------------
+ set_the_native_Descriptor(int fileDescriptor) {
+ libusb_context *ctx;
+ libusb_device_handle *devh;
+ libusb_set_option(&ctx, LIBUSB_OPTION_NO_DEVICE_DISCOVERY, NULL);
+ libusb_init(&ctx);
+ libusb_wrap_sys_device(NULL, (intptr_t)fileDescriptor, &devh);
+ }
+ /* From this point you can regularly use all libusb functions as usual */
+
+ About LIBUSB_OPTION_NO_DEVICE_DISCOVERY:
+
+ The method libusb_set_option(&ctx, LIBUSB_OPTION_NO_DEVICE_DISCOVERY, NULL)
+ does not affect the ctx.
+ It allows initializing libusb on unrooted Android devices by skipping
+ the device enumeration.
+
+Rooted Devices:
+---------------
+
+ For rooted devices the code using libusb could be executed as root
+ using the "su" command. An alternative would be to use the "su" command
+ to change the permissions on the appropriate /dev/bus/usb/ files.
+
+ Users have reported success in using android.hardware.usb.UsbManager
+ to request permission to use the UsbDevice and then opening the
+ device. The difficulties in this method is that there is no guarantee
+ that it will continue to work in the future Android versions, it
+ requires invoking Java APIs and running code to match each
+ android.hardware.usb.UsbDevice to a libusb_device.
+
+ For a rooted device it is possible to install libusb into the system
+ image of a running device:
+
+ 1. Enable ADB on the device.
+
+ 2. Connect the device to a machine running ADB.
+
+ 3. Execute the following commands on the machine
+ running ADB:
+
+ # Make the system partition writable
+ adb shell su -c "mount -o remount,rw /system"
+
+ # Install libusb
+ adb push obj/local/armeabi/libusb1.0.so /sdcard/
+ adb shell su -c "cat > /system/lib/libusb1.0.so < /sdcard/libusb1.0.so"
+ adb shell rm /sdcard/libusb1.0.so
+
+ # Install the samples and tests
+ for B in listdevs fxload xusb sam3u_benchmark hotplugtest stress
+ do
+ adb push "obj/local/armeabi/$B" /sdcard/
+ adb shell su -c "cat > /system/bin/$B < /sdcard/$B"
+ adb shell su -c "chmod 0755 /system/bin/$B"
+ adb shell rm "/sdcard/$B"
+ done
+
+ # Make the system partition read only again
+ adb shell su -c "mount -o remount,ro /system"
+
+ # Run listdevs to
+ adb shell su -c "listdevs"
+
+ 4. If your device only has a single OTG port then ADB can generally
+ be switched to using Wifi with the following commands when connected
+ via USB:
-The default system configuration on most Android device will not allow
-access to USB devices. There are several options for changing this.
-
-If you have control of the system image then you can modify the
-ueventd.rc used in the image to change the permissions on
-/dev/bus/usb/*/*. If using this approach then it is advisable to
-create a new Android permission to protect access to these files.
-It is not advisable to give all applications read and write permissions
-to these files.
-
-For rooted devices the code using libusb could be executed as root
-using the "su" command. An alternative would be to use the "su" command
-to change the permissions on the appropriate /dev/bus/usb/ files.
-
-Users have reported success in using android.hardware.usb.UsbManager
-to request permission to use the UsbDevice and then opening the
-device. The difficulties in this method is that there is no guarantee
-that it will continue to work in the future Android versions, it
-requires invoking Java APIs and running code to match each
-android.hardware.usb.UsbDevice to a libusb_device.
+ adb shell netcfg
+ # Note the wifi IP address of the phone
+ adb tcpip 5555
+ # Use the IP address from netcfg
+ adb connect 192.168.1.123:5555
diff --git a/android/examples/unrooted_android.c b/android/examples/unrooted_android.c
new file mode 100644
index 0000000..33793c7
--- /dev/null
+++ b/android/examples/unrooted_android.c
@@ -0,0 +1,300 @@
+/*
+ * libusb example program for reading out USB descriptors on unrooted Android
+ * (based on testlibusb.c)
+ *
+ * Copyright 2020-2021 Peter Stoiber
+ *
+ * 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
+ *
+ * Please contact the author if you need another license.
+ * This Repository is provided "as is", without warranties of any kind.
+*/
+
+/*
+ * This example creates a shared object which can be accessed over JNA or JNI from Java or Kotlin in Android.
+ * Hint: If you are using Android Studio, set the "Debug type" to "Java Only" to receive debug messages.
+ */
+
+/*
+ * Usage:
+ * First, you have to connect your USB device from the Java side.
+ * Use the android.hardware.usb class to find the USB device, claim the interfaces, and open the usb_device_connection
+ * Obtain the native File Descriptor --> usb_device_connection.getFileDescriptor()
+ * Pass the received int value to the unrooted_usb_description method of this code (over JNA)
+ */
+
+/*
+ * libusb can only be included in Android projects using NDK for now. (CMake is not supported at the moment)
+ * Clone the libusb git repo into your Android project and include the Android.mk file in your build.gradle.
+ */
+
+/*
+ Example JNA Approach:
+ public interface unrooted_sample extends Library {
+ public static final unrooted_sample INSTANCE = Native.load("unrooted_android", unrooted_sample.class);
+ public int unrooted_usb_description (int fileDescriptor);
+ }
+ unrooted_sample.INSTANCE.unrooted_usb_description( usbDeviceConnection.getFileDescriptor());
+ */
+
+#include <jni.h>
+#include <string.h>
+#include "unrooted_android.h"
+#include "libusb.h"
+#include <android/log.h>
+#define LOG_TAG "LibUsb"
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+
+int verbose = 0;
+
+static void print_endpoint_comp(const struct libusb_ss_endpoint_companion_descriptor *ep_comp)
+{
+ LOGD(" USB 3.0 Endpoint Companion:\n");
+ LOGD(" bMaxBurst: %u\n", ep_comp->bMaxBurst);
+ LOGD(" bmAttributes: %02xh\n", ep_comp->bmAttributes);
+ LOGD(" wBytesPerInterval: %u\n", ep_comp->wBytesPerInterval);
+}
+
+static void print_endpoint(const struct libusb_endpoint_descriptor *endpoint)
+{
+ int i, ret;
+
+ LOGD(" Endpoint:\n");
+ LOGD(" bEndpointAddress: %02xh\n", endpoint->bEndpointAddress);
+ LOGD(" bmAttributes: %02xh\n", endpoint->bmAttributes);
+ LOGD(" wMaxPacketSize: %u\n", endpoint->wMaxPacketSize);
+ LOGD(" bInterval: %u\n", endpoint->bInterval);
+ LOGD(" bRefresh: %u\n", endpoint->bRefresh);
+ LOGD(" bSynchAddress: %u\n", endpoint->bSynchAddress);
+
+ for (i = 0; i < endpoint->extra_length;) {
+ if (LIBUSB_DT_SS_ENDPOINT_COMPANION == endpoint->extra[i + 1]) {
+ struct libusb_ss_endpoint_companion_descriptor *ep_comp;
+
+ ret = libusb_get_ss_endpoint_companion_descriptor(NULL, endpoint, &ep_comp);
+ if (LIBUSB_SUCCESS != ret)
+ continue;
+
+ print_endpoint_comp(ep_comp);
+
+ libusb_free_ss_endpoint_companion_descriptor(ep_comp);
+ }
+
+ i += endpoint->extra[i];
+ }
+}
+
+static void print_altsetting(const struct libusb_interface_descriptor *interface)
+{
+ uint8_t i;
+
+ LOGD(" Interface:\n");
+ LOGD(" bInterfaceNumber: %u\n", interface->bInterfaceNumber);
+ LOGD(" bAlternateSetting: %u\n", interface->bAlternateSetting);
+ LOGD(" bNumEndpoints: %u\n", interface->bNumEndpoints);
+ LOGD(" bInterfaceClass: %u\n", interface->bInterfaceClass);
+ LOGD(" bInterfaceSubClass: %u\n", interface->bInterfaceSubClass);
+ LOGD(" bInterfaceProtocol: %u\n", interface->bInterfaceProtocol);
+ LOGD(" iInterface: %u\n", interface->iInterface);
+
+ for (i = 0; i < interface->bNumEndpoints; i++)
+ print_endpoint(&interface->endpoint[i]);
+}
+
+static void print_2_0_ext_cap(struct libusb_usb_2_0_extension_descriptor *usb_2_0_ext_cap)
+{
+ LOGD(" USB 2.0 Extension Capabilities:\n");
+ LOGD(" bDevCapabilityType: %u\n", usb_2_0_ext_cap->bDevCapabilityType);
+ LOGD(" bmAttributes: %08xh\n", usb_2_0_ext_cap->bmAttributes);
+}
+
+static void print_ss_usb_cap(struct libusb_ss_usb_device_capability_descriptor *ss_usb_cap)
+{
+ LOGD(" USB 3.0 Capabilities:\n");
+ LOGD(" bDevCapabilityType: %u\n", ss_usb_cap->bDevCapabilityType);
+ LOGD(" bmAttributes: %02xh\n", ss_usb_cap->bmAttributes);
+ LOGD(" wSpeedSupported: %u\n", ss_usb_cap->wSpeedSupported);
+ LOGD(" bFunctionalitySupport: %u\n", ss_usb_cap->bFunctionalitySupport);
+ LOGD(" bU1devExitLat: %u\n", ss_usb_cap->bU1DevExitLat);
+ LOGD(" bU2devExitLat: %u\n", ss_usb_cap->bU2DevExitLat);
+}
+
+static void print_bos(libusb_device_handle *handle)
+{
+ struct libusb_bos_descriptor *bos;
+ uint8_t i;
+ int ret;
+
+ ret = libusb_get_bos_descriptor(handle, &bos);
+ if (ret < 0)
+ return;
+
+ LOGD(" Binary Object Store (BOS):\n");
+ LOGD(" wTotalLength: %u\n", bos->wTotalLength);
+ LOGD(" bNumDeviceCaps: %u\n", bos->bNumDeviceCaps);
+
+ for (i = 0; i < bos->bNumDeviceCaps; i++) {
+ struct libusb_bos_dev_capability_descriptor *dev_cap = bos->dev_capability[i];
+
+ if (dev_cap->bDevCapabilityType == LIBUSB_BT_USB_2_0_EXTENSION) {
+ struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension;
+
+ ret = libusb_get_usb_2_0_extension_descriptor(NULL, dev_cap, &usb_2_0_extension);
+ if (ret < 0)
+ return;
+
+ print_2_0_ext_cap(usb_2_0_extension);
+ libusb_free_usb_2_0_extension_descriptor(usb_2_0_extension);
+ } else if (dev_cap->bDevCapabilityType == LIBUSB_BT_SS_USB_DEVICE_CAPABILITY) {
+ struct libusb_ss_usb_device_capability_descriptor *ss_dev_cap;
+
+ ret = libusb_get_ss_usb_device_capability_descriptor(NULL, dev_cap, &ss_dev_cap);
+ if (ret < 0)
+ return;
+
+ print_ss_usb_cap(ss_dev_cap);
+ libusb_free_ss_usb_device_capability_descriptor(ss_dev_cap);
+ }
+ }
+
+ libusb_free_bos_descriptor(bos);
+}
+
+static void print_interface(const struct libusb_interface *interface)
+{
+ int i;
+
+ for (i = 0; i < interface->num_altsetting; i++)
+ print_altsetting(&interface->altsetting[i]);
+}
+
+static void print_configuration(struct libusb_config_descriptor *config)
+{
+ uint8_t i;
+
+ LOGD(" Configuration:\n");
+ LOGD(" wTotalLength: %u\n", config->wTotalLength);
+ LOGD(" bNumInterfaces: %u\n", config->bNumInterfaces);
+ LOGD(" bConfigurationValue: %u\n", config->bConfigurationValue);
+ LOGD(" iConfiguration: %u\n", config->iConfiguration);
+ LOGD(" bmAttributes: %02xh\n", config->bmAttributes);
+ LOGD(" MaxPower: %u\n", config->MaxPower);
+
+ for (i = 0; i < config->bNumInterfaces; i++)
+ print_interface(&config->interface[i]);
+}
+
+static void print_device(libusb_device *dev, libusb_device_handle *handle)
+{
+ struct libusb_device_descriptor desc;
+ unsigned char string[256];
+ const char *speed;
+ int ret;
+ uint8_t i;
+
+ switch (libusb_get_device_speed(dev)) {
+ case LIBUSB_SPEED_LOW: speed = "1.5M"; break;
+ case LIBUSB_SPEED_FULL: speed = "12M"; break;
+ case LIBUSB_SPEED_HIGH: speed = "480M"; break;
+ case LIBUSB_SPEED_SUPER: speed = "5G"; break;
+ case LIBUSB_SPEED_SUPER_PLUS: speed = "10G"; break;
+ default: speed = "Unknown";
+ }
+
+ ret = libusb_get_device_descriptor(dev, &desc);
+ if (ret < 0) {
+ LOGD("failed to get device descriptor");
+ return;
+ }
+
+ LOGD("Dev (bus %u, device %u): %04X - %04X speed: %s\n",
+ libusb_get_bus_number(dev), libusb_get_device_address(dev),
+ desc.idVendor, desc.idProduct, speed);
+
+ if (!handle)
+ libusb_open(dev, &handle);
+
+ if (handle) {
+ if (desc.iManufacturer) {
+ ret = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, string, sizeof(string));
+ if (ret > 0)
+ LOGD(" Manufacturer: %s\n", (char *)string);
+ }
+
+ if (desc.iProduct) {
+ ret = libusb_get_string_descriptor_ascii(handle, desc.iProduct, string, sizeof(string));
+ if (ret > 0)
+ LOGD(" Product: %s\n", (char *)string);
+ }
+
+ if (desc.iSerialNumber && verbose) {
+ ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, string, sizeof(string));
+ if (ret > 0)
+ LOGD(" Serial Number: %s\n", (char *)string);
+ }
+ }
+
+ if (verbose) {
+ for (i = 0; i < desc.bNumConfigurations; i++) {
+ struct libusb_config_descriptor *config;
+
+ ret = libusb_get_config_descriptor(dev, i, &config);
+ if (LIBUSB_SUCCESS != ret) {
+ LOGD(" Couldn't retrieve descriptors\n");
+ continue;
+ }
+
+ print_configuration(config);
+
+ libusb_free_config_descriptor(config);
+ }
+
+ if (handle && desc.bcdUSB >= 0x0201)
+ print_bos(handle);
+ }
+
+ if (handle)
+ libusb_close(handle);
+}
+
+
+/* fileDescriptor = the native file descriptor obtained in Java and transferred to native over JNA, for example */
+int unrooted_usb_description(int fileDescriptor)
+{
+ libusb_context *ctx = NULL;
+ libusb_device_handle *devh = NULL;
+ int r = 0;
+ verbose = 1;
+ r = libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY, NULL);
+ if (r != LIBUSB_SUCCESS) {
+ LOGD("libusb_set_option failed: %d\n", r);
+ return -1;
+ }
+ r = libusb_init(&ctx);
+ if (r < 0) {
+ LOGD("libusb_init failed: %d\n", r);
+ return r;
+ }
+ r = libusb_wrap_sys_device(ctx, (intptr_t)fileDescriptor, &devh);
+ if (r < 0) {
+ LOGD("libusb_wrap_sys_device failed: %d\n", r);
+ return r;
+ } else if (devh == NULL) {
+ LOGD("libusb_wrap_sys_device returned invalid handle\n");
+ return r;
+ }
+ print_device(libusb_get_device(devh), devh);
+ return r;
+}
diff --git a/android/examples/unrooted_android.h b/android/examples/unrooted_android.h
new file mode 100644
index 0000000..7ccd408
--- /dev/null
+++ b/android/examples/unrooted_android.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2021 Peter Stoiber
+ *
+ * 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
+ *
+ * Please contact the author if you need another license.
+ * This Repository is provided "as is", without warranties of any kind.
+*/
+
+#ifndef unrooted_android_H
+#define unrooted_android_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int unrooted_usb_description(int fileDescriptor);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/android/jni/examples.mk b/android/jni/examples.mk
index d0ffb82..237141a 100644
--- a/android/jni/examples.mk
+++ b/android/jni/examples.mk
@@ -20,6 +20,12 @@ LOCAL_PATH := $(call my-dir)
LIBUSB_ROOT_REL := ../..
LIBUSB_ROOT_ABS := $(LOCAL_PATH)/../..
+ifeq ($(USE_PC_NAME),1)
+ LIBUSB_MODULE := usb-1.0
+else
+ LIBUSB_MODULE := libusb1.0
+endif
+
# dpfp
include $(CLEAR_VARS)
@@ -31,7 +37,7 @@ LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/.. \
$(LIBUSB_ROOT_ABS)
-LOCAL_SHARED_LIBRARIES += libusb1.0
+LOCAL_SHARED_LIBRARIES += $(LIBUSB_MODULE)
LOCAL_MODULE := dpfp
LOCAL_LICENSE_KINDS := SPDX-license-identifier-LGPL SPDX-license-identifier-LGPL-2.1 SPDX-license-identifier-LGPL-3.0
@@ -53,7 +59,7 @@ LOCAL_C_INCLUDES += \
LOCAL_CFLAGS := -DDPFP_THREADED -pthread
-LOCAL_SHARED_LIBRARIES += libusb1.0
+LOCAL_SHARED_LIBRARIES += $(LIBUSB_MODULE)
LOCAL_MODULE := dpfp_threaded
LOCAL_LICENSE_KINDS := SPDX-license-identifier-LGPL SPDX-license-identifier-LGPL-2.1 SPDX-license-identifier-LGPL-3.0
@@ -74,7 +80,7 @@ LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/.. \
$(LIBUSB_ROOT_ABS)
-LOCAL_SHARED_LIBRARIES += libusb1.0
+LOCAL_SHARED_LIBRARIES += $(LIBUSB_MODULE)
LOCAL_MODULE := fxload
LOCAL_LICENSE_KINDS := SPDX-license-identifier-LGPL SPDX-license-identifier-LGPL-2.1 SPDX-license-identifier-LGPL-3.0
@@ -94,7 +100,7 @@ LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/.. \
$(LIBUSB_ROOT_ABS)
-LOCAL_SHARED_LIBRARIES += libusb1.0
+LOCAL_SHARED_LIBRARIES += $(LIBUSB_MODULE)
LOCAL_MODULE := hotplugtest
LOCAL_LICENSE_KINDS := SPDX-license-identifier-LGPL SPDX-license-identifier-LGPL-2.1 SPDX-license-identifier-LGPL-3.0
@@ -114,7 +120,7 @@ LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/.. \
$(LIBUSB_ROOT_ABS)
-LOCAL_SHARED_LIBRARIES += libusb1.0
+LOCAL_SHARED_LIBRARIES += $(LIBUSB_MODULE)
LOCAL_MODULE := listdevs
LOCAL_LICENSE_KINDS := SPDX-license-identifier-LGPL SPDX-license-identifier-LGPL-2.1 SPDX-license-identifier-LGPL-3.0
@@ -134,7 +140,7 @@ LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/.. \
$(LIBUSB_ROOT_ABS)
-LOCAL_SHARED_LIBRARIES += libusb1.0
+LOCAL_SHARED_LIBRARIES += $(LIBUSB_MODULE)
LOCAL_MODULE := sam3u_benchmark
LOCAL_LICENSE_KINDS := SPDX-license-identifier-LGPL SPDX-license-identifier-LGPL-2.1 SPDX-license-identifier-LGPL-3.0
@@ -154,7 +160,7 @@ LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/.. \
$(LIBUSB_ROOT_ABS)
-LOCAL_SHARED_LIBRARIES += libusb1.0
+LOCAL_SHARED_LIBRARIES += $(LIBUSB_MODULE)
LOCAL_MODULE := xusb
LOCAL_LICENSE_KINDS := SPDX-license-identifier-LGPL SPDX-license-identifier-LGPL-2.1 SPDX-license-identifier-LGPL-3.0
@@ -162,3 +168,25 @@ LOCAL_LICENSE_CONDITIONS := restricted
LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../COPYING $(LOCAL_PATH)/../../NOTICE
include $(BUILD_EXECUTABLE)
+
+# unrooted_android
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ $(LIBUSB_ROOT_REL)/android/examples/unrooted_android.c
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/.. \
+ $(LIBUSB_ROOT_ABS)
+
+LOCAL_SHARED_LIBRARIES += libusb1.0
+
+LOCAL_LDLIBS += -llog
+
+LOCAL_MODULE := unrooted_android
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-LGPL SPDX-license-identifier-LGPL-2.1 SPDX-license-identifier-LGPL-3.0
+LOCAL_LICENSE_CONDITIONS := restricted
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../COPYING $(LOCAL_PATH)/../../NOTICE
+include $(BUILD_SHARED_LIBRARY)
diff --git a/android/jni/libusb.mk b/android/jni/libusb.mk
index c7ac77c..d40c88a 100644
--- a/android/jni/libusb.mk
+++ b/android/jni/libusb.mk
@@ -48,7 +48,15 @@ LOCAL_CFLAGS := -fvisibility=hidden -pthread
LOCAL_LDLIBS := -llog
-LOCAL_MODULE := libusb1.0
+ifeq ($(USE_PC_NAME),1)
+ LOCAL_MODULE := usb-1.0
+else
+ LOCAL_MODULE := libusb1.0
+ $(warning Building to legacy library name libusb1.0, which differs from pkg-config.)
+ $(warning Use ndk-build USE_PC_NAME=1 to change the module name to the compatible usb-1.0.)
+ $(warning USE_PC_NAME=1 may be the default in the future.)
+endif
+
LOCAL_LICENSE_KINDS := SPDX-license-identifier-LGPL SPDX-license-identifier-LGPL-2.1 SPDX-license-identifier-LGPL-3.0
LOCAL_LICENSE_CONDITIONS := restricted
LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../COPYING $(LOCAL_PATH)/../../NOTICE
diff --git a/android/jni/tests.mk b/android/jni/tests.mk
index 56315c5..8d7a04b 100644
--- a/android/jni/tests.mk
+++ b/android/jni/tests.mk
@@ -20,6 +20,12 @@ LOCAL_PATH := $(call my-dir)
LIBUSB_ROOT_REL := ../..
LIBUSB_ROOT_ABS := $(LOCAL_PATH)/../..
+ifeq ($(USE_PC_NAME),1)
+ LIBUSB_MODULE := usb-1.0
+else
+ LIBUSB_MODULE := libusb1.0
+endif
+
# stress
include $(CLEAR_VARS)
@@ -32,7 +38,7 @@ LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/.. \
$(LIBUSB_ROOT_ABS)
-LOCAL_SHARED_LIBRARIES += libusb1.0
+LOCAL_SHARED_LIBRARIES += $(LIBUSB_MODULE)
LOCAL_MODULE := stress
LOCAL_LICENSE_KINDS := SPDX-license-identifier-LGPL SPDX-license-identifier-LGPL-2.1 SPDX-license-identifier-LGPL-3.0
diff --git a/appveyor.yml b/appveyor.yml
index 36cfa96..c70f3eb 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -42,6 +42,8 @@ for:
- cmd: msbuild "%APPVEYOR_BUILD_FOLDER%\msvc\libusb_2015.sln" /m /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
- cmd: C:\msys64\usr\bin\bash -l "%APPVEYOR_BUILD_FOLDER%\.private\appveyor_build.sh" MinGW
- cmd: C:\cygwin\bin\bash -l "%APPVEYOR_BUILD_FOLDER%\.private\appveyor_build.sh" cygwin
+ after_build:
+ - cmd: 7z a "libusb-build_%APPVEYOR_BUILD_WORKER_IMAGE%_%PLATFORM%_%CONFIGURATION%.7z" tag_* README-build.txt %PLATFORM%\%CONFIGURATION%\dll\*.* %PLATFORM%\%CONFIGURATION%\lib\*.* %PLATFORM%\%CONFIGURATION%\examples\*.exe %PLATFORM%\%CONFIGURATION%\tests\*.exe C:\msys64\home\appveyor\libusb-MinGW-Win32 C:\cygwin\home\appveyor\libusb-cygwin-Win32
-
matrix:
@@ -56,6 +58,8 @@ for:
- cmd: msbuild "%APPVEYOR_BUILD_FOLDER%\msvc\libusb_2015.sln" /m /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
- cmd: C:\msys64\usr\bin\bash -l "%APPVEYOR_BUILD_FOLDER%\.private\appveyor_build.sh" MinGW
- cmd: C:\cygwin64\bin\bash -l "%APPVEYOR_BUILD_FOLDER%\.private\appveyor_build.sh" cygwin
+ after_build:
+ - cmd: 7z a "libusb-build_%APPVEYOR_BUILD_WORKER_IMAGE%_%PLATFORM%_%CONFIGURATION%.7z" tag_* README-build.txt %PLATFORM%\%CONFIGURATION%\dll\*.* %PLATFORM%\%CONFIGURATION%\lib\*.* %PLATFORM%\%CONFIGURATION%\examples\*.exe %PLATFORM%\%CONFIGURATION%\tests\*.exe C:\msys64\home\appveyor\libusb-MinGW-x64 C:\cygwin64\home\appveyor\libusb-cygwin-x64
-
matrix:
@@ -70,3 +74,11 @@ for:
- image: Visual Studio 2019
build:
project: msvc\libusb_2019.sln
+
+after_build:
+ - cmd: ECHO This was built by %APPVEYOR_BUILD_WORKER_IMAGE% from %APPVEYOR_REPO_NAME% commit %APPVEYOR_REPO_COMMIT% > README-build.txt
+ - cmd: ECHO > tag_%APPVEYOR_REPO_TAG_NAME%_commit_%APPVEYOR_REPO_COMMIT%
+ - cmd: 7z a "libusb-build_%APPVEYOR_BUILD_WORKER_IMAGE%_%PLATFORM%_%CONFIGURATION%.7z" tag_* README-build.txt %PLATFORM%\%CONFIGURATION%\dll\*.* %PLATFORM%\%CONFIGURATION%\lib\*.* %PLATFORM%\%CONFIGURATION%\examples\*.exe %PLATFORM%\%CONFIGURATION%\tests\*.exe
+
+artifacts:
+ - path: "libusb-build_%APPVEYOR_BUILD_WORKER_IMAGE%_%PLATFORM%_%CONFIGURATION%.7z"
diff --git a/autogen.sh b/autogen.sh
index 62d68e5..2ffb10e 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -2,7 +2,9 @@
set -e
-./bootstrap.sh
+srcdir="$(dirname "$0")"
+
+"$srcdir"/bootstrap.sh
if [ -z "$NOCONFIGURE" ]; then
- exec ./configure --enable-examples-build --enable-tests-build "$@"
+ exec "$srcdir"/configure --enable-examples-build --enable-tests-build "$@"
fi
diff --git a/bootstrap.sh b/bootstrap.sh
index cfd2b45..fc555d5 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -2,6 +2,8 @@
set -e
+cd "$(dirname "$0")"
+
if [ ! -d m4 ]; then
mkdir m4
fi
diff --git a/configure.ac b/configure.ac
index 5b880f6..d4f1251 100644
--- a/configure.ac
+++ b/configure.ac
@@ -124,6 +124,7 @@ case $host in
platform=windows
test "x$enable_shared" = xyes && create_import_lib=yes
EXTRA_CFLAGS="-mwin32 -fno-omit-frame-pointer"
+ EXTRA_LDFLAGS="-static-libgcc"
;;
*)
AC_MSG_RESULT([Null])
@@ -152,6 +153,7 @@ if test "x$platform" = xposix; then
AC_SEARCH_LIBS([pthread_create], [pthread],
[test "x$ac_cv_search_pthread_create" != "xnone required" && AC_SUBST(THREAD_LIBS, [-lpthread])],
[], [])
+ AC_SEARCH_LIBS([__atomic_fetch_add_4], [atomic])
elif test "x$platform" = xwindows; then
AC_DEFINE([PLATFORM_WINDOWS], [1], [Define to 1 if compiling for a Windows platform.])
else
@@ -161,7 +163,8 @@ fi
case $backend in
darwin)
AC_CHECK_FUNCS([pthread_threadid_np])
- LIBS="${LIBS} -lobjc -Wl,-framework,IOKit -Wl,-framework,CoreFoundation"
+ LIBS="${LIBS} -lobjc -Wl,-framework,IOKit -Wl,-framework,CoreFoundation -Wl,-framework,Security"
+ AC_CHECK_HEADERS([IOKit/usb/IOUSBHostFamilyDefinitions.h])
;;
haiku)
LIBS="${LIBS} -lbe"
@@ -170,12 +173,20 @@ linux)
AC_SEARCH_LIBS([clock_gettime], [rt], [], [], [])
AC_CHECK_FUNCS([pthread_setname_np])
AC_ARG_ENABLE([udev],
- [AC_HELP_STRING([--enable-udev], [use udev for device enumeration and hotplug support (recommended) [default=yes]])],
+ [AS_HELP_STRING([--enable-udev], [use udev for device enumeration and hotplug support (recommended) [default=yes]])],
[use_udev=$enableval], [use_udev=yes])
if test "x$use_udev" = xyes; then
dnl system has udev. use it or fail!
AC_CHECK_HEADER([libudev.h], [], [AC_MSG_ERROR([udev support requested but libudev header not installed])])
AC_CHECK_LIB([udev], [udev_new], [], [AC_MSG_ERROR([udev support requested but libudev not installed])])
+
+ # We can build umockdev tests (if available)
+ PKG_PROG_PKG_CONFIG
+ PKG_CHECK_MODULES(UMOCKDEV, umockdev-1.0 >= 0.16.0, ac_have_umockdev=yes, ac_have_umockdev=no)
+ PKG_CHECK_MODULES(UMOCKDEV_HOTPLUG, umockdev-1.0 >= 0.17.7, ac_umockdev_hotplug=yes, ac_umockdev_hotplug=no)
+ if test "x$ac_umockdev_hotplug" = xyes; then
+ AC_DEFINE([UMOCKDEV_HOTPLUG], [1], [UMockdev hotplug code is not racy])
+ fi
else
AC_CHECK_HEADERS([asm/types.h])
AC_CHECK_HEADER([linux/netlink.h], [], [AC_MSG_ERROR([Linux netlink header not found])])
@@ -199,17 +210,37 @@ dnl headers not available on all platforms but required on others
AC_CHECK_HEADERS([sys/time.h])
if test "x$platform" = xposix; then
- dnl the clock_gettime() function needs certain clock IDs defined
- AC_CHECK_FUNCS([clock_gettime], [have_clock_gettime=yes], [have_clock_gettime=])
+ dnl check availability of clock_gettime()
+ if test "x$backend" = xdarwin; then
+ dnl need to verify that OS X target is 10.12 or later for clock_gettime()
+ AC_MSG_CHECKING([whether OS X target version is 10.12 or later])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+ #include <AvailabilityMacros.h>
+ #if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
+ # error "Target OS X version is too old"
+ #endif
+ ], [])],
+ [AC_MSG_RESULT([yes])
+ osx_10_12_or_later=yes],
+ [AC_MSG_RESULT([no])
+ osx_10_12_or_later=])
+ if test "x$osx_10_12_or_later" = xyes; then
+ AC_CHECK_FUNCS([clock_gettime], [have_clock_gettime=yes], [have_clock_gettime=])
+ else
+ AC_MSG_NOTICE([clock_gettime() is not available on target OS X version])
+ fi
+ else
+ AC_CHECK_FUNCS([clock_gettime], [have_clock_gettime=yes], [AC_MSG_ERROR([clock_gettime() is required on this platform])])
+ fi
+
if test "x$have_clock_gettime" = xyes; then
+ dnl the clock_gettime() function needs certain clock IDs defined
AC_CHECK_DECL([CLOCK_MONOTONIC], [], [AC_MSG_ERROR([C library headers missing definition for CLOCK_MONOTONIC])], [[#include <time.h>]])
dnl use the monotonic clock for condition variable timed waits if possible
AC_CHECK_FUNCS([pthread_condattr_setclock], [need_clock_realtime=], [need_clock_realtime=yes])
if test "x$need_clock_realtime" = xyes; then
AC_CHECK_DECL([CLOCK_REALTIME], [], [AC_MSG_ERROR([C library headers missing definition for CLOCK_REALTIME])], [[#include <time.h>]])
fi
- elif test "x$backend" != xdarwin; then
- AC_MSG_ERROR([clock_gettime() is required on this platform])
fi
fi
@@ -335,6 +366,7 @@ AC_ARG_ENABLE([tests-build],
AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$build_examples" != xno])
AM_CONDITIONAL([BUILD_TESTS], [test "x$build_tests" != xno])
+AM_CONDITIONAL([BUILD_UMOCKDEV_TEST], [test "x$ac_have_umockdev" = xyes -a "x$log_enabled" != xno])
AM_CONDITIONAL([CREATE_IMPORT_LIB], [test "x$create_import_lib" = xyes])
AM_CONDITIONAL([OS_DARWIN], [test "x$backend" = xdarwin])
AM_CONDITIONAL([OS_HAIKU], [test "x$backend" = xhaiku])
@@ -372,6 +404,8 @@ AC_SUBST(AM_CXXFLAGS)
AC_SUBST(LT_LDFLAGS)
+AC_SUBST([EXTRA_LDFLAGS])
+
dnl set name of html output directory for doxygen
AC_SUBST(DOXYGEN_HTMLDIR, [api-1.0])
diff --git a/doc/Makefile.in b/doc/Makefile.in
index 45c3209..6568c4f 100644
--- a/doc/Makefile.in
+++ b/doc/Makefile.in
@@ -1,5 +1,5 @@
LIBUSB_SRC_DIR = @top_srcdir@/libusb
-EXCLUDED_FILES = hotplug.h libusbi.h version.h version_nano.h
+EXCLUDED_FILES = libusbi.h version.h version_nano.h
LIBUSB_SRC = $(wildcard $(LIBUSB_SRC_DIR)/*.c) $(wildcard $(LIBUSB_SRC_DIR)/*.h)
LIBUSB_DOC_SRC = $(filter-out $(addprefix $(LIBUSB_SRC_DIR)/,$(EXCLUDED_FILES)),$(LIBUSB_SRC))
diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in
index 569e465..b6e6219 100644
--- a/doc/doxygen.cfg.in
+++ b/doc/doxygen.cfg.in
@@ -1,4 +1,4 @@
-# Doxyfile 1.8.16
+# Doxyfile 1.9.0
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -217,6 +217,14 @@ QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
+# By default Python docstrings are displayed as preformatted text and doxygen's
+# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
+# doxygen's special commands can be used and the contents of the docstring
+# documentation blocks is shown as doxygen documentation.
+# The default value is: YES.
+
+PYTHON_DOCSTRING = YES
+
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements.
# The default value is: YES.
@@ -253,12 +261,6 @@ TAB_SIZE = 4
ALIASES =
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding "class=itcl::class"
-# will allow you to use the command class in the itcl::class meaning.
-
-TCL_SUBST =
-
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
@@ -299,19 +301,22 @@ OPTIMIZE_OUTPUT_SLICE = NO
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
-# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
-# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
# tries to guess whether the code is fixed or free formatted code, this is the
-# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
-# .inc files as Fortran files (default is PHP), and .f files as C (default is
-# Fortran), use: inc=Fortran f=C.
+# default for Fortran type files). For instance to make doxygen treat .inc files
+# as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
-# the files are not read by doxygen.
+# the files are not read by doxygen. When specifying no_extension you should add
+# * to the FILE_PATTERNS.
+#
+# Note see also the list of default file extension mappings.
EXTENSION_MAPPING =
@@ -445,6 +450,19 @@ TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
+# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
+# during processing. When set to 0 doxygen will based this on the number of
+# cores available in the system. You can set it explicitly to a value larger
+# than 0 to get more control over the balance between CPU load and processing
+# speed. At this moment only the input processing can be done using multiple
+# threads. Since this is still an experimental feature the default is set to 1,
+# which efficively disables parallel processing. Please report any issues you
+# encounter. Generating dot graphs in parallel is controlled by the
+# DOT_NUM_THREADS setting.
+# Minimum value: 0, maximum value: 32, default value: 1.
+
+NUM_PROC_THREADS = 1
+
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
@@ -508,6 +526,13 @@ EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
+# If this flag is set to YES, the name of an unnamed parameter in a declaration
+# will be determined by the corresponding definition. By default unnamed
+# parameters remain unnamed in the output.
+# The default value is: YES.
+
+RESOLVE_UNNAMED_PARAMS = YES
+
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
# members will be included in the various overviews, but no documentation
@@ -525,8 +550,8 @@ HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
-# (class|struct|union) declarations. If set to NO, these declarations will be
-# included in the documentation.
+# declarations. If set to NO, these declarations will be included in the
+# documentation.
# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = NO
@@ -545,11 +570,18 @@ HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
-# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
-# names in lower-case letters. If set to YES, upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# (including Cygwin) ands Mac users are advised to set this option to NO.
+# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
+# able to match the capabilities of the underlying filesystem. In case the
+# filesystem is case sensitive (i.e. it supports files in the same directory
+# whose names only differ in casing), the option must be set to YES to properly
+# deal with such files in case they appear in the input. For filesystems that
+# are not case sensitive the option should be be set to NO to properly deal with
+# output files written for symbols that only differ in casing, such as for two
+# classes, one named CLASS and the other named Class, and to also support
+# references to files without having to specify the exact matching casing. On
+# Windows (including Cygwin) and MacOS, users should typically set this option
+# to NO, whereas on Linux or other Unix flavors it should typically be set to
+# YES.
# The default value is: system dependent.
CASE_SENSE_NAMES = YES
@@ -788,7 +820,10 @@ WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
-# a warning is encountered.
+# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
+# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
+# at the end of the doxygen process doxygen will return with a non-zero status.
+# Possible values are: NO, YES and FAIL_ON_WARNINGS.
# The default value is: NO.
WARN_AS_ERROR = NO
@@ -824,8 +859,8 @@ 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. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
-# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
-# possible encodings.
+# documentation (see:
+# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
@@ -838,11 +873,15 @@ INPUT_ENCODING = UTF-8
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# read by doxygen.
#
+# Note the list of default checked file patterns might differ from the list of
+# default file extension mappings.
+#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
-# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
-# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
+# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
+# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl,
+# *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.c \
*.h
@@ -860,8 +899,7 @@ RECURSIVE = NO
# Note that relative paths are relative to the directory from which doxygen is
# run.
-EXCLUDE = @top_srcdir@/libusb/hotplug.h \
- @top_srcdir@/libusb/libusbi.h \
+EXCLUDE = @top_srcdir@/libusb/libusbi.h \
@top_srcdir@/libusb/version.h \
@top_srcdir@/libusb/version_nano.h \
@top_srcdir@/libusb/os
@@ -1072,13 +1110,6 @@ VERBATIM_HEADERS = YES
ALPHABETICAL_INDEX = YES
-# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
-# which the alphabetical index list will be split.
-# Minimum value: 1, maximum value: 20, default value: 5.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-COLS_IN_ALPHA_INDEX = 5
-
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored
@@ -1217,9 +1248,9 @@ HTML_TIMESTAMP = YES
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
-# are dynamically created via Javascript. If disabled, the navigation index will
+# are dynamically created via JavaScript. If disabled, the navigation index will
# consists of multiple levels of tabs that are statically embedded in every HTML
-# page. Disable this option to support browsers that do not have Javascript,
+# page. Disable this option to support browsers that do not have JavaScript,
# like the Qt help browser.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
@@ -1249,10 +1280,11 @@ HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
-# environment (see: https://developer.apple.com/xcode/), introduced with OSX
-# 10.5 (Leopard). To create a documentation set, doxygen will generate a
-# Makefile in the HTML output directory. Running make will produce the docset in
-# that directory and running make install will install the docset in
+# environment (see:
+# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
+# create a documentation set, doxygen will generate a Makefile in the HTML
+# output directory. Running make will produce the docset in that directory and
+# running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
# genXcode/_index.html for more information.
@@ -1294,8 +1326,8 @@ DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
-# Windows.
+# (see:
+# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows.
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
@@ -1325,7 +1357,7 @@ CHM_FILE =
HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated
-# (YES) or that it should be included in the master .chm file (NO).
+# (YES) or that it should be included in the main .chm file (NO).
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
@@ -1370,7 +1402,8 @@ QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
-# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1378,8 +1411,8 @@ QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
-# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
-# folders).
+# Folders (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1387,16 +1420,16 @@ QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
-# filters).
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
-# filters).
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
@@ -1408,9 +1441,9 @@ QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
-# The QHG_LOCATION tag can be used to specify the location of Qt's
-# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
-# generated .qhp file.
+# The QHG_LOCATION tag can be used to specify the location (absolute path
+# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
+# run qhelpgenerator on the generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
@@ -1487,6 +1520,17 @@ TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
+# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
+# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
+# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
+# the HTML output. These images will generally look nicer at scaled resolutions.
+# Possible values are: png (the default) and svg (looks nicer but requires the
+# pdf2svg or inkscape tool).
+# The default value is: png.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FORMULA_FORMAT = png
+
# Use this tag to change the font size of LaTeX formulas included as images in
# the HTML documentation. When you change the font size after a successful
# doxygen run you need to manually remove any form_*.png images from the HTML
@@ -1507,8 +1551,14 @@ FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
+
+FORMULA_MACROFILE =
+
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
-# https://www.mathjax.org) which uses client side Javascript for the rendering
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
@@ -1520,7 +1570,7 @@ USE_MATHJAX = NO
# When MathJax is enabled you can set the default output format to be used for
# the MathJax output. See the MathJax site (see:
-# http://docs.mathjax.org/en/latest/output.html) for more details.
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details.
# Possible values are: HTML-CSS (which is slower, but has the best
# compatibility), NativeMML (i.e. MathML) and SVG.
# The default value is: HTML-CSS.
@@ -1536,7 +1586,7 @@ MATHJAX_FORMAT = HTML-CSS
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from https://www.mathjax.org before deployment.
-# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
+# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
@@ -1550,7 +1600,8 @@ MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
-# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
@@ -1578,7 +1629,7 @@ MATHJAX_CODEFILE =
SEARCHENGINE = NO
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a web server instead of a web client using Javascript. There
+# implemented using a web server instead of a web client using JavaScript. There
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
@@ -1597,7 +1648,8 @@ SERVER_BASED_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: https://xapian.org/).
+# Xapian (see:
+# https://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
@@ -1610,8 +1662,9 @@ EXTERNAL_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: https://xapian.org/). See the section "External Indexing and
-# Searching" for details.
+# Xapian (see:
+# https://xapian.org/). See the section "External Indexing and Searching" for
+# details.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL =
@@ -1706,7 +1759,7 @@ COMPACT_LATEX = NO
# The default value is: a4.
# This tag requires that the tag GENERATE_LATEX is set to YES.
-PAPER_TYPE = a4wide
+PAPER_TYPE = a4
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. The package can be specified just
@@ -1775,9 +1828,11 @@ LATEX_EXTRA_FILES =
PDF_HYPERLINKS = NO
-# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
-# the PDF file directly from the LaTeX files. Set this option to YES, to get a
-# higher quality PDF documentation.
+# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
+# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
+# files. Set this option to YES, to get a higher quality PDF documentation.
+#
+# See also section LATEX_CMD_NAME for selecting the engine.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -2291,10 +2346,32 @@ UML_LOOK = NO
# but if the number exceeds 15, the total amount of fields shown is limited to
# 10.
# Minimum value: 0, maximum value: 100, default value: 10.
-# This tag requires that the tag HAVE_DOT is set to YES.
+# This tag requires that the tag UML_LOOK is set to YES.
UML_LIMIT_NUM_FIELDS = 10
+# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
+# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
+# tag is set to YES, doxygen will add type and arguments for attributes and
+# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
+# will not generate fields with class member information in the UML graphs. The
+# class diagrams will look similar to the default class diagrams but using UML
+# notation for the relationships.
+# Possible values are: NO, YES and NONE.
+# The default value is: NO.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+DOT_UML_DETAILS = NO
+
+# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
+# to display on a single line. If the actual line length exceeds this threshold
+# significantly it will wrapped across multiple lines. Some heuristics are apply
+# to avoid ugly line breaks.
+# Minimum value: 0, maximum value: 1000, default value: 17.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_WRAP_THRESHOLD = 17
+
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
# collaboration graphs will show the relations between templates and their
# instances.
@@ -2484,9 +2561,11 @@ DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
-# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
# files that are used to generate the various graphs.
+#
+# Note: This setting is not only used for dot files but also for msc and
+# plantuml temporary files.
# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
DOT_CLEANUP = YES
diff --git a/examples/dpfp.c b/examples/dpfp.c
index a3a76df..6828650 100644
--- a/examples/dpfp.c
+++ b/examples/dpfp.c
@@ -217,7 +217,11 @@ static thread_return_t __stdcall poll_thread_main(void *arg)
static int find_dpfp_device(void)
{
devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a);
- return devh ? 0 : -ENODEV;
+ if (!devh) {
+ errno = ENODEV;
+ return -1;
+ }
+ return 0;
}
static int print_f0_data(void)
@@ -316,13 +320,16 @@ 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;
+ if (!buf) {
+ errno = ENOMEM;
+ return -1;
+ }
transfer = libusb_alloc_transfer(0);
if (!transfer) {
free(buf);
- return -ENOMEM;
+ errno = ENOMEM;
+ return -1;
}
printf("async set mode %02x\n", data);
@@ -547,12 +554,16 @@ static int do_init(void)
static int alloc_transfers(void)
{
img_transfer = libusb_alloc_transfer(0);
- if (!img_transfer)
- return -ENOMEM;
+ if (!img_transfer) {
+ errno = ENOMEM;
+ return -1;
+ }
irq_transfer = libusb_alloc_transfer(0);
- if (!irq_transfer)
- return -ENOMEM;
+ if (!irq_transfer) {
+ errno = ENOMEM;
+ return -1;
+ }
libusb_fill_bulk_transfer(img_transfer, devh, EP_DATA, imgbuf,
sizeof(imgbuf), cb_img, NULL, 0);
diff --git a/examples/ezusb.c b/examples/ezusb.c
index 6abd47d..4bed12a 100644
--- a/examples/ezusb.c
+++ b/examples/ezusb.c
@@ -139,7 +139,11 @@ static int ezusb_write(libusb_device_handle *device, const char *label,
else
logerror("%s ==> %d\n", label, status);
}
- return (status < 0) ? -EIO : 0;
+ if (status < 0) {
+ errno = EIO;
+ return -1;
+ }
+ return 0;
}
/*
@@ -162,7 +166,11 @@ static int ezusb_read(libusb_device_handle *device, const char *label,
else
logerror("%s ==> %d\n", label, status);
}
- return (status < 0) ? -EIO : 0;
+ if (status < 0) {
+ errno = EIO;
+ return -1;
+ }
+ return 0;
}
/*
@@ -195,7 +203,7 @@ static bool ezusb_cpucs(libusb_device_handle *device, uint32_t addr, bool doRun)
}
/*
- * Send an FX3 jumpt to address command
+ * Send an FX3 jump to address command
* Returns false on error.
*/
static bool ezusb_fx3_jump(libusb_device_handle *device, uint32_t addr)
@@ -514,7 +522,8 @@ static int ram_poke(void *context, uint32_t addr, bool external,
if (external) {
logerror("can't write %u bytes external memory at 0x%08x\n",
(unsigned)len, addr);
- return -EINVAL;
+ errno = EINVAL;
+ return -1;
}
break;
case skip_internal: /* CPU must be running */
@@ -538,7 +547,8 @@ static int ram_poke(void *context, uint32_t addr, bool external,
case _undef:
default:
logerror("bug\n");
- return -EDOM;
+ errno = EDOM;
+ return -1;
}
ctx->total += len;
diff --git a/examples/sam3u_benchmark.c b/examples/sam3u_benchmark.c
index 33e8913..8979775 100644
--- a/examples/sam3u_benchmark.c
+++ b/examples/sam3u_benchmark.c
@@ -122,8 +122,10 @@ static int benchmark_in(uint8_t ep)
num_iso_pack = 16;
xfr = libusb_alloc_transfer(num_iso_pack);
- if (!xfr)
- return -ENOMEM;
+ if (!xfr) {
+ errno = ENOMEM;
+ return -1;
+ }
if (ep == EP_ISO_IN) {
libusb_fill_iso_transfer(xfr, devh, ep, buf,
diff --git a/examples/xusb.c b/examples/xusb.c
index bf328fe..5c65298 100644
--- a/examples/xusb.c
+++ b/examples/xusb.c
@@ -220,7 +220,7 @@ static int display_ps3_status(libusb_device_handle *handle)
printf("\tRIGHT 3 pressed\n");
break;
case 0x08:
- printf("\tSTART presed\n");
+ printf("\tSTART pressed\n");
break;
case 0x10:
printf("\tUP pressed\n");
@@ -246,7 +246,7 @@ static int display_ps3_status(libusb_device_handle *handle)
printf("\tLEFT 1 pressed\n");
break;
case 0x08:
- printf("\tRIGHT 1 presed\n");
+ printf("\tRIGHT 1 pressed\n");
break;
case 0x10:
printf("\tTRIANGLE pressed\n");
@@ -863,6 +863,8 @@ static int test_device(uint16_t vid, uint16_t pid)
printf("\nReading first configuration descriptor:\n");
CALL_CHECK_CLOSE(libusb_get_config_descriptor(dev, 0, &conf_desc), handle);
+ printf(" total length: %d\n", conf_desc->wTotalLength);
+ printf(" descriptor length: %d\n", conf_desc->bLength);
nb_ifaces = conf_desc->bNumInterfaces;
printf(" nb interfaces: %d\n", nb_ifaces);
if (nb_ifaces > 0)
@@ -914,6 +916,8 @@ static int test_device(uint16_t vid, uint16_t pid)
libusb_set_auto_detach_kernel_driver(handle, 1);
for (iface = 0; iface < nb_ifaces; iface++)
{
+ int ret = libusb_kernel_driver_active(handle, iface);
+ printf("\nKernel driver attached for interface %d: %d\n", iface, ret);
printf("\nClaiming interface %d...\n", iface);
r = libusb_claim_interface(handle, iface);
if (r != LIBUSB_SUCCESS) {
@@ -930,12 +934,16 @@ static int test_device(uint16_t vid, uint16_t pid)
printf(" String (0x%02X): \"%s\"\n", string_index[i], string);
}
}
- // Read the OS String Descriptor
+
+ printf("\nReading OS string descriptor:");
r = libusb_get_string_descriptor(handle, MS_OS_DESC_STRING_INDEX, 0, (unsigned char*)string, MS_OS_DESC_STRING_LENGTH);
if (r == MS_OS_DESC_STRING_LENGTH && memcmp(ms_os_desc_string, string, sizeof(ms_os_desc_string)) == 0) {
// If this is a Microsoft OS String Descriptor,
// attempt to read the WinUSB extended Feature Descriptors
+ printf("\n");
read_ms_winsub_feature_descriptors(handle, string[MS_OS_DESC_VENDOR_CODE_OFFSET], first_iface);
+ } else {
+ printf(" no descriptor\n");
}
switch(test_mode) {
diff --git a/include/libusb/hotplug.h b/include/libusb/hotplug.h
deleted file mode 120000
index 1f6670a..0000000
--- a/include/libusb/hotplug.h
+++ /dev/null
@@ -1 +0,0 @@
-../../libusb/hotplug.h \ No newline at end of file
diff --git a/libusb/Makefile.am b/libusb/Makefile.am
index c78006e..3475c9a 100644
--- a/libusb/Makefile.am
+++ b/libusb/Makefile.am
@@ -80,9 +80,9 @@ all-local: .libs/libusb-1.0.dll.a
endif
endif
-libusb_1_0_la_LDFLAGS = $(LT_LDFLAGS)
+libusb_1_0_la_LDFLAGS = $(LT_LDFLAGS) $(EXTRA_LDFLAGS)
libusb_1_0_la_SOURCES = libusbi.h version.h version_nano.h \
- core.c descriptor.c hotplug.h hotplug.c io.c strerror.c sync.c \
+ core.c descriptor.c hotplug.c io.c strerror.c sync.c \
$(PLATFORM_SRC) $(OS_SRC)
pkginclude_HEADERS = libusb.h
diff --git a/libusb/core.c b/libusb/core.c
index 07d459c..ec429b7 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -21,7 +21,6 @@
*/
#include "libusbi.h"
-#include "hotplug.h"
#include "version.h"
#ifdef __ANDROID__
@@ -33,17 +32,21 @@
#include <syslog.h>
#endif
-struct libusb_context *usbi_default_context;
static const struct libusb_version libusb_version_internal =
{ LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO,
LIBUSB_RC, "http://libusb.info" };
-static int default_context_refcnt;
-static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
static struct timespec timestamp_origin;
#if defined(ENABLE_LOGGING) && !defined(USE_SYSTEM_LOGGING_FACILITY)
static libusb_log_cb log_handler;
#endif
+struct libusb_context *usbi_default_context;
+struct libusb_context *usbi_fallback_context;
+static int default_context_refcnt;
+static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
+static struct usbi_option default_context_options[LIBUSB_OPTION_MAX];
+
+
usbi_mutex_static_t active_contexts_lock = USBI_MUTEX_INITIALIZER;
struct list_head active_contexts_list;
@@ -310,7 +313,8 @@ if (cfg != desired)
* - libusb is able to send a packet of zero length to an endpoint simply by
* submitting a transfer of zero length.
* - The \ref libusb_transfer_flags::LIBUSB_TRANSFER_ADD_ZERO_PACKET
- * "LIBUSB_TRANSFER_ADD_ZERO_PACKET" flag is currently only supported on Linux.
+ * "LIBUSB_TRANSFER_ADD_ZERO_PACKET" flag is currently supported on Linux,
+ * Darwin and Windows (WinUSB).
*/
/**
@@ -577,7 +581,9 @@ libusb_free_device_list(list, 1);
*
* The libusb_get_device_list() function can be used to obtain a list of
* devices currently connected to the system. This is known as device
- * discovery.
+ * discovery. Devices can also be discovered with the hotplug mechanism,
+ * whereby a callback function registered with libusb_hotplug_register_callback()
+ * will be called when a device of interest is connected or disconnected.
*
* Just because you have a reference to a device does not mean it is
* necessarily usable. The device may have been unplugged, you may not have
@@ -608,7 +614,7 @@ libusb_free_device_list(list, 1);
*
* With the above information in mind, the process of opening a device can
* be viewed as follows:
- * -# Discover devices using libusb_get_device_list().
+ * -# Discover devices using libusb_get_device_list() or libusb_hotplug_register_callback().
* -# Choose the device that you want to operate, and call libusb_open().
* -# Unref all devices in the discovered device list.
* -# Free the discovered device list.
@@ -633,7 +639,7 @@ libusb_free_device_list(list, 1);
* which grows when required. it can be freed once discovery has completed,
* eliminating the need for a list node in the libusb_device structure
* itself. */
-#define DISCOVERED_DEVICES_SIZE_STEP 8
+#define DISCOVERED_DEVICES_SIZE_STEP 16
static struct discovered_devs *discovered_devs_alloc(void)
{
@@ -674,7 +680,7 @@ struct discovered_devs *discovered_devs_append(
}
/* exceeded capacity, need to grow */
- usbi_dbg("need to increase capacity");
+ usbi_dbg(DEVICE_CTX(dev), "need to increase capacity");
capacity = discdevs->capacity + DISCOVERED_DEVICES_SIZE_STEP;
/* can't use usbi_reallocf here because in failure cases it would
* free the existing discdevs without unreferencing its devices. */
@@ -704,16 +710,14 @@ struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
if (!dev)
return NULL;
- usbi_mutex_init(&dev->lock);
+ usbi_atomic_store(&dev->refcnt, 1);
dev->ctx = ctx;
- dev->refcnt = 1;
dev->session_data = session_id;
dev->speed = LIBUSB_SPEED_UNKNOWN;
- if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
- usbi_connect_device (dev);
- }
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
+ usbi_connect_device(dev);
return dev;
}
@@ -722,39 +726,26 @@ void usbi_connect_device(struct libusb_device *dev)
{
struct libusb_context *ctx = DEVICE_CTX(dev);
- dev->attached = 1;
+ usbi_atomic_store(&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 message list is ready. This prevents an event from getting raised
- * during initial enumeration. */
- if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) {
- usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED);
- }
+ usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED);
}
void usbi_disconnect_device(struct libusb_device *dev)
{
struct libusb_context *ctx = DEVICE_CTX(dev);
- usbi_mutex_lock(&dev->lock);
- dev->attached = 0;
- usbi_mutex_unlock(&dev->lock);
+ usbi_atomic_store(&dev->attached, 0);
usbi_mutex_lock(&ctx->usb_devs_lock);
list_del(&dev->list);
usbi_mutex_unlock(&ctx->usb_devs_lock);
- /* Signal that an event has occurred for this device if we support hotplug AND
- * the hotplug message list 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_msgs.next) {
- usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT);
- }
+ usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT);
}
/* Perform some final sanity checks on a newly discovered device. If this
@@ -775,7 +766,7 @@ int usbi_sanitize_device(struct libusb_device *dev)
usbi_err(DEVICE_CTX(dev), "too many configurations");
return LIBUSB_ERROR_IO;
} else if (0 == num_configurations) {
- usbi_dbg("zero configurations, maybe an unauthorized device");
+ usbi_dbg(DEVICE_CTX(dev), "zero configurations, maybe an unauthorized device");
}
return 0;
@@ -830,7 +821,7 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
int r = 0;
ssize_t i, len;
- usbi_dbg(" ");
+ usbi_dbg(ctx, " ");
if (!discdevs)
return LIBUSB_ERROR_NO_MEM;
@@ -1173,9 +1164,11 @@ out:
DEFAULT_VISIBILITY
libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev)
{
- usbi_mutex_lock(&dev->lock);
- dev->refcnt++;
- usbi_mutex_unlock(&dev->lock);
+ long refcnt;
+
+ refcnt = usbi_atomic_inc(&dev->refcnt);
+ assert(refcnt >= 2);
+
return dev;
}
@@ -1186,17 +1179,16 @@ libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev)
*/
void API_EXPORTED libusb_unref_device(libusb_device *dev)
{
- int refcnt;
+ long refcnt;
if (!dev)
return;
- usbi_mutex_lock(&dev->lock);
- refcnt = --dev->refcnt;
- usbi_mutex_unlock(&dev->lock);
+ refcnt = usbi_atomic_dec(&dev->refcnt);
+ assert(refcnt >= 0);
if (refcnt == 0) {
- usbi_dbg("destroy device %d.%d", dev->bus_number, dev->device_address);
+ usbi_dbg(DEVICE_CTX(dev), "destroy device %d.%d", dev->bus_number, dev->device_address);
libusb_unref_device(dev->parent_dev);
@@ -1208,7 +1200,6 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev)
usbi_disconnect_device(dev);
}
- usbi_mutex_destroy(&dev->lock);
free(dev);
}
}
@@ -1218,8 +1209,10 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev)
* handle for the underlying device. The handle allows you to use libusb to
* perform I/O on the device in question.
*
- * Must call libusb_set_option(NULL, LIBUSB_OPTION_WEAK_AUTHORITY)
- * before libusb_init if don't have authority to access the usb device directly.
+ * Call libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY) before
+ * libusb_init() if you want to skip enumeration of USB devices. In particular,
+ * this might be needed on Android if you don't have authority to access USB
+ * devices in general.
*
* On Linux, the system device handle must be a valid file descriptor opened
* on the device node.
@@ -1233,6 +1226,8 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev)
*
* This is a non-blocking function; no requests are sent over the bus.
*
+ * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107
+ *
* \param ctx the context to operate on, or NULL for the default context
* \param sys_dev the platform-specific system device handle
* \param dev_handle output location for the returned device handle pointer. Only
@@ -1251,7 +1246,7 @@ int API_EXPORTED libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev,
size_t priv_size = usbi_backend.device_handle_priv_size;
int r;
- usbi_dbg("wrap_sys_device 0x%" PRIxPTR, (uintptr_t)sys_dev);
+ usbi_dbg(ctx, "wrap_sys_device 0x%" PRIxPTR, (uintptr_t)sys_dev);
ctx = usbi_get_context(ctx);
@@ -1266,7 +1261,7 @@ int API_EXPORTED libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev,
r = usbi_backend.wrap_sys_device(ctx, _dev_handle, sys_dev);
if (r < 0) {
- usbi_dbg("wrap_sys_device 0x%" PRIxPTR " returns %d", (uintptr_t)sys_dev, r);
+ usbi_dbg(ctx, "wrap_sys_device 0x%" PRIxPTR " returns %d", (uintptr_t)sys_dev, r);
usbi_mutex_destroy(&_dev_handle->lock);
free(_dev_handle);
return r;
@@ -1306,11 +1301,11 @@ int API_EXPORTED libusb_open(libusb_device *dev,
struct libusb_device_handle *_dev_handle;
size_t priv_size = usbi_backend.device_handle_priv_size;
int r;
- usbi_dbg("open %d.%d", dev->bus_number, dev->device_address);
- if (!dev->attached) {
+ usbi_dbg(DEVICE_CTX(dev), "open %d.%d", dev->bus_number, dev->device_address);
+
+ if (!usbi_atomic_load(&dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
- }
_dev_handle = calloc(1, PTR_ALIGN(sizeof(*_dev_handle)) + priv_size);
if (!_dev_handle)
@@ -1322,7 +1317,7 @@ int API_EXPORTED libusb_open(libusb_device *dev,
r = usbi_backend.open(_dev_handle);
if (r < 0) {
- usbi_dbg("open %d.%d returns %d", dev->bus_number, dev->device_address, r);
+ usbi_dbg(DEVICE_CTX(dev), "open %d.%d returns %d", dev->bus_number, dev->device_address, r);
libusb_unref_device(dev);
usbi_mutex_destroy(&_dev_handle->lock);
free(_dev_handle);
@@ -1428,7 +1423,7 @@ static void do_close(struct libusb_context *ctx,
* just making sure that we don't attempt to process the transfer after
* the device handle is invalid
*/
- usbi_dbg("Removed transfer %p from the in-flight list because device handle %p closed",
+ usbi_dbg(ctx, "Removed transfer %p from the in-flight list because device handle %p closed",
transfer, dev_handle);
}
usbi_mutex_unlock(&ctx->flying_transfers_lock);
@@ -1462,9 +1457,9 @@ void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
if (!dev_handle)
return;
- usbi_dbg(" ");
-
ctx = HANDLE_CTX(dev_handle);
+ usbi_dbg(ctx, " ");
+
handling_events = usbi_handling_events(ctx);
/* Similarly to libusb_open(), we want to interrupt all event handlers
@@ -1546,27 +1541,28 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle,
{
int r = LIBUSB_ERROR_NOT_SUPPORTED;
uint8_t tmp = 0;
+ struct libusb_context *ctx = HANDLE_CTX(dev_handle);
- usbi_dbg(" ");
+ usbi_dbg(ctx, " ");
if (usbi_backend.get_configuration)
r = usbi_backend.get_configuration(dev_handle, &tmp);
if (r == LIBUSB_ERROR_NOT_SUPPORTED) {
- usbi_dbg("falling back to control message");
+ usbi_dbg(ctx, "falling back to control message");
r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN,
LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000);
if (r == 1) {
r = 0;
} else if (r == 0) {
- usbi_err(HANDLE_CTX(dev_handle), "zero bytes returned in ctrl transfer?");
+ usbi_err(ctx, "zero bytes returned in ctrl transfer?");
r = LIBUSB_ERROR_IO;
} else {
- usbi_dbg("control failed, error %d", r);
+ usbi_dbg(ctx, "control failed, error %d", r);
}
}
if (r == 0) {
- usbi_dbg("active config %u", tmp);
+ usbi_dbg(ctx, "active config %u", tmp);
*config = (int)tmp;
}
@@ -1630,7 +1626,7 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle,
int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle,
int configuration)
{
- usbi_dbg("configuration %d", configuration);
+ usbi_dbg(HANDLE_CTX(dev_handle), "configuration %d", configuration);
if (configuration < -1 || configuration > (int)UINT8_MAX)
return LIBUSB_ERROR_INVALID_PARAM;
return usbi_backend.set_configuration(dev_handle, configuration);
@@ -1669,11 +1665,11 @@ int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev_handle,
{
int r = 0;
- usbi_dbg("interface %d", interface_number);
+ usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
- if (!dev_handle->dev->attached)
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
usbi_mutex_lock(&dev_handle->lock);
@@ -1713,7 +1709,7 @@ int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle,
{
int r;
- usbi_dbg("interface %d", interface_number);
+ usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
@@ -1756,19 +1752,19 @@ out:
int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_handle,
int interface_number, int alternate_setting)
{
- usbi_dbg("interface %d altsetting %d",
+ usbi_dbg(HANDLE_CTX(dev_handle), "interface %d altsetting %d",
interface_number, alternate_setting);
if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
if (alternate_setting < 0 || alternate_setting > (int)UINT8_MAX)
return LIBUSB_ERROR_INVALID_PARAM;
- usbi_mutex_lock(&dev_handle->lock);
- if (!dev_handle->dev->attached) {
+ if (!usbi_atomic_load(&dev_handle->dev->attached)) {
usbi_mutex_unlock(&dev_handle->lock);
return LIBUSB_ERROR_NO_DEVICE;
}
+ usbi_mutex_lock(&dev_handle->lock);
if (!(dev_handle->claimed_interfaces & (1U << interface_number))) {
usbi_mutex_unlock(&dev_handle->lock);
return LIBUSB_ERROR_NOT_FOUND;
@@ -1798,8 +1794,8 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand
int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle,
unsigned char endpoint)
{
- usbi_dbg("endpoint %x", endpoint);
- if (!dev_handle->dev->attached)
+ usbi_dbg(HANDLE_CTX(dev_handle), "endpoint 0x%x", endpoint);
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
return usbi_backend.clear_halt(dev_handle, endpoint);
@@ -1826,8 +1822,8 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle,
*/
int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle)
{
- usbi_dbg(" ");
- if (!dev_handle->dev->attached)
+ usbi_dbg(HANDLE_CTX(dev_handle), " ");
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.reset_device)
@@ -1860,12 +1856,12 @@ int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle)
int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle,
uint32_t num_streams, unsigned char *endpoints, int num_endpoints)
{
- usbi_dbg("streams %u eps %d", (unsigned)num_streams, num_endpoints);
+ usbi_dbg(HANDLE_CTX(dev_handle), "streams %u eps %d", (unsigned)num_streams, num_endpoints);
if (!num_streams || !endpoints || num_endpoints <= 0)
return LIBUSB_ERROR_INVALID_PARAM;
- if (!dev_handle->dev->attached)
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.alloc_streams)
@@ -1890,12 +1886,12 @@ int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle,
int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle,
unsigned char *endpoints, int num_endpoints)
{
- usbi_dbg("eps %d", num_endpoints);
+ usbi_dbg(HANDLE_CTX(dev_handle), "eps %d", num_endpoints);
if (!endpoints || num_endpoints <= 0)
return LIBUSB_ERROR_INVALID_PARAM;
- if (!dev_handle->dev->attached)
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.free_streams)
@@ -1933,7 +1929,7 @@ DEFAULT_VISIBILITY
unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle,
size_t length)
{
- if (!dev_handle->dev->attached)
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return NULL;
if (usbi_backend.dev_mem_alloc)
@@ -1979,12 +1975,12 @@ int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle,
int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle,
int interface_number)
{
- usbi_dbg("interface %d", interface_number);
+ usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
- if (!dev_handle->dev->attached)
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.kernel_driver_active)
@@ -1997,7 +1993,7 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle,
* Detach a kernel driver from an interface. If successful, you will then be
* able to claim the interface and perform I/O.
*
- * This functionality is not available on Darwin or Windows.
+ * This functionality is not available on Windows.
*
* Note that libusb itself also talks to the device through a special kernel
* driver, if this driver is already attached to the device, this call will
@@ -2017,12 +2013,12 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle,
int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
int interface_number)
{
- usbi_dbg("interface %d", interface_number);
+ usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
- if (!dev_handle->dev->attached)
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.detach_kernel_driver)
@@ -2033,10 +2029,9 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
/** \ingroup libusb_dev
* Re-attach an interface's kernel driver, which was previously detached
- * using libusb_detach_kernel_driver(). This call is only effective on
- * Linux and returns LIBUSB_ERROR_NOT_SUPPORTED on all other platforms.
+ * using libusb_detach_kernel_driver().
*
- * This functionality is not available on Darwin or Windows.
+ * This functionality is not available on Windows.
*
* \param dev_handle a device handle
* \param interface_number the interface to attach the driver from
@@ -2054,12 +2049,12 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle,
int interface_number)
{
- usbi_dbg("interface %d", interface_number);
+ usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number);
if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
return LIBUSB_ERROR_INVALID_PARAM;
- if (!dev_handle->dev->attached)
+ if (!usbi_atomic_load(&dev_handle->dev->attached))
return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend.attach_kernel_driver)
@@ -2130,6 +2125,8 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
* If ENABLE_DEBUG_LOGGING is defined then per context callback function will
* never be called.
*
+ * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107
+ *
* \param ctx context on which to assign log handler, or NULL for the default
* context. Parameter ignored if only LIBUSB_LOG_CB_GLOBAL mode is requested.
* \param cb pointer to the callback function, or NULL to stop log
@@ -2170,6 +2167,9 @@ void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb,
* Some options require one or more arguments to be provided. Consult each
* option's documentation for specific requirements.
*
+ * If the context ctx is NULL, the option will be added to a list of default
+ * options that will be applied to all subsequently created contexts.
+ *
* Since version 1.0.22, \ref LIBUSB_API_VERSION >= 0x01000106
*
* \param ctx context on which to operate
@@ -2185,40 +2185,63 @@ void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb,
int API_EXPORTED libusb_set_option(libusb_context *ctx,
enum libusb_option option, ...)
{
- int arg, r = LIBUSB_SUCCESS;
+ int arg = 0, r = LIBUSB_SUCCESS;
va_list ap;
- ctx = usbi_get_context(ctx);
-
va_start(ap, option);
- switch (option) {
- case LIBUSB_OPTION_LOG_LEVEL:
+ if (LIBUSB_OPTION_LOG_LEVEL == option) {
arg = va_arg(ap, int);
if (arg < LIBUSB_LOG_LEVEL_NONE || arg > LIBUSB_LOG_LEVEL_DEBUG) {
r = LIBUSB_ERROR_INVALID_PARAM;
- break;
}
+ }
+ va_end(ap);
+
+ if (LIBUSB_SUCCESS != r) {
+ return r;
+ }
+
+ if (option >= LIBUSB_OPTION_MAX) {
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ if (NULL == ctx) {
+ usbi_mutex_static_lock(&default_context_lock);
+ default_context_options[option].is_set = 1;
+ if (LIBUSB_OPTION_LOG_LEVEL == option) {
+ default_context_options[option].arg.ival = arg;
+ }
+ usbi_mutex_static_unlock(&default_context_lock);
+ }
+
+ ctx = usbi_get_context(ctx);
+ if (NULL == ctx) {
+ return LIBUSB_SUCCESS;
+ }
+
+ switch (option) {
+ case LIBUSB_OPTION_LOG_LEVEL:
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
if (!ctx->debug_fixed)
ctx->debug = (enum libusb_log_level)arg;
#endif
break;
- /* Handle all backend-specific options here */
+ /* Handle all backend-specific options here */
case LIBUSB_OPTION_USE_USBDK:
- case LIBUSB_OPTION_WEAK_AUTHORITY:
+ case LIBUSB_OPTION_NO_DEVICE_DISCOVERY:
if (usbi_backend.set_option)
- r = usbi_backend.set_option(ctx, option, ap);
- else
- r = LIBUSB_ERROR_NOT_SUPPORTED;
+ return usbi_backend.set_option(ctx, option, ap);
+
+ return LIBUSB_ERROR_NOT_SUPPORTED;
break;
+ case LIBUSB_OPTION_MAX:
default:
- r = LIBUSB_ERROR_INVALID_PARAM;
+ return LIBUSB_ERROR_INVALID_PARAM;
}
- va_end(ap);
- return r;
+ return LIBUSB_SUCCESS;;
}
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
@@ -2249,113 +2272,125 @@ static enum libusb_log_level get_env_debug_level(void)
* context will be created. If there was already a default context, it will
* be reused (and nothing will be initialized/reinitialized).
*
- * \param context Optional output location for context pointer.
+ * \param ctx Optional output location for context pointer.
* Only valid on return code 0.
* \returns 0 on success, or a LIBUSB_ERROR code on failure
* \see libusb_contexts
*/
-int API_EXPORTED libusb_init(libusb_context **context)
+int API_EXPORTED libusb_init(libusb_context **ctx)
{
- struct libusb_device *dev, *next;
size_t priv_size = usbi_backend.context_priv_size;
- struct libusb_context *ctx;
- static int first_init = 1;
- int r = 0;
+ struct libusb_context *_ctx;
+ int r;
usbi_mutex_static_lock(&default_context_lock);
- if (!timestamp_origin.tv_sec)
- usbi_get_monotonic_time(&timestamp_origin);
-
- if (!context && usbi_default_context) {
- usbi_dbg("reusing default context");
+ if (!ctx && default_context_refcnt > 0) {
+ usbi_dbg(usbi_default_context, "reusing default context");
default_context_refcnt++;
usbi_mutex_static_unlock(&default_context_lock);
return 0;
}
- ctx = calloc(1, PTR_ALIGN(sizeof(*ctx)) + priv_size);
- if (!ctx) {
- r = LIBUSB_ERROR_NO_MEM;
- goto err_unlock;
+ /* check for first init */
+ if (!active_contexts_list.next) {
+ list_init(&active_contexts_list);
+ usbi_get_monotonic_time(&timestamp_origin);
+ }
+
+ _ctx = calloc(1, PTR_ALIGN(sizeof(*_ctx)) + priv_size);
+ if (!_ctx) {
+ usbi_mutex_static_unlock(&default_context_lock);
+ return LIBUSB_ERROR_NO_MEM;
}
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
- ctx->debug = get_env_debug_level();
- if (ctx->debug != LIBUSB_LOG_LEVEL_NONE)
- ctx->debug_fixed = 1;
+ if (NULL == ctx && default_context_options[LIBUSB_OPTION_LOG_LEVEL].is_set) {
+ _ctx->debug = default_context_options[LIBUSB_OPTION_LOG_LEVEL].arg.ival;
+ } else {
+ _ctx->debug = get_env_debug_level();
+ }
+ if (_ctx->debug != LIBUSB_LOG_LEVEL_NONE)
+ _ctx->debug_fixed = 1;
#endif
- /* 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_mutex_init(&_ctx->usb_devs_lock);
+ usbi_mutex_init(&_ctx->open_devs_lock);
+ list_init(&_ctx->usb_devs);
+ list_init(&_ctx->open_devs);
+
+ /* apply default options to all new contexts */
+ for (enum libusb_option option = 0 ; option < LIBUSB_OPTION_MAX ; option++) {
+ if (LIBUSB_OPTION_LOG_LEVEL == option || !default_context_options[option].is_set) {
+ continue;
+ }
+ r = libusb_set_option(_ctx, option);
+ if (LIBUSB_SUCCESS != r)
+ goto err_free_ctx;
+ }
+
+ /* default context must be initialized before calling usbi_dbg */
+ if (!ctx) {
+ usbi_default_context = _ctx;
+ default_context_refcnt = 1;
+ usbi_dbg(usbi_default_context, "created default context");
}
- usbi_dbg("libusb v%u.%u.%u.%u%s", libusb_version_internal.major, libusb_version_internal.minor,
+ usbi_dbg(_ctx, "libusb v%u.%u.%u.%u%s", libusb_version_internal.major, libusb_version_internal.minor,
libusb_version_internal.micro, libusb_version_internal.nano, libusb_version_internal.rc);
- usbi_mutex_init(&ctx->usb_devs_lock);
- usbi_mutex_init(&ctx->open_devs_lock);
- usbi_mutex_init(&ctx->hotplug_cbs_lock);
- list_init(&ctx->usb_devs);
- list_init(&ctx->open_devs);
- list_init(&ctx->hotplug_cbs);
- ctx->next_hotplug_cb_handle = 1;
+ r = usbi_io_init(_ctx);
+ if (r < 0)
+ goto err_free_ctx;
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);
+ list_add(&_ctx->list, &active_contexts_list);
usbi_mutex_static_unlock(&active_contexts_lock);
if (usbi_backend.init) {
- r = usbi_backend.init(ctx);
+ r = usbi_backend.init(_ctx);
if (r)
- goto err_free_ctx;
+ goto err_io_exit;
}
- r = usbi_io_init(ctx);
- if (r < 0)
- goto err_backend_exit;
+ /* Initialize hotplug after the initial enumeration is done. */
+ usbi_hotplug_init(_ctx);
- usbi_mutex_static_unlock(&default_context_lock);
+ if (ctx) {
+ *ctx = _ctx;
- if (context)
- *context = ctx;
+ if (!usbi_fallback_context) {
+ usbi_fallback_context = _ctx;
+ usbi_warn(usbi_fallback_context, "installing new context as implicit default");
+ }
+ }
- return 0;
+ usbi_mutex_static_unlock(&default_context_lock);
-err_backend_exit:
- if (usbi_backend.exit)
- usbi_backend.exit(ctx);
-err_free_ctx:
- if (ctx == usbi_default_context) {
- usbi_default_context = NULL;
- default_context_refcnt--;
- }
+ return 0;
+err_io_exit:
usbi_mutex_static_lock(&active_contexts_lock);
- list_del(&ctx->list);
+ list_del(&_ctx->list);
usbi_mutex_static_unlock(&active_contexts_lock);
- usbi_mutex_lock(&ctx->usb_devs_lock);
- for_each_device_safe(ctx, dev, next) {
- list_del(&dev->list);
- libusb_unref_device(dev);
+ usbi_hotplug_exit(_ctx);
+ usbi_io_exit(_ctx);
+
+err_free_ctx:
+ if (!ctx) {
+ /* clear default context that was not fully initialized */
+ usbi_default_context = NULL;
+ default_context_refcnt = 0;
}
- usbi_mutex_unlock(&ctx->usb_devs_lock);
- usbi_mutex_destroy(&ctx->open_devs_lock);
- usbi_mutex_destroy(&ctx->usb_devs_lock);
- usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
+ usbi_mutex_destroy(&_ctx->open_devs_lock);
+ usbi_mutex_destroy(&_ctx->usb_devs_lock);
+
+ free(_ctx);
- free(ctx);
-err_unlock:
usbi_mutex_static_unlock(&default_context_lock);
+
return r;
}
@@ -2366,90 +2401,66 @@ err_unlock:
*/
void API_EXPORTED libusb_exit(libusb_context *ctx)
{
- struct libusb_device *dev, *next;
- struct timeval tv = { 0, 0 };
- int destroying_default_context = 0;
-
- usbi_dbg(" ");
+ struct libusb_context *_ctx;
+ struct libusb_device *dev;
- ctx = usbi_get_context(ctx);
+ usbi_mutex_static_lock(&default_context_lock);
/* if working with default context, only actually do the deinitialization
* if we're the last user */
- usbi_mutex_static_lock(&default_context_lock);
- if (ctx == usbi_default_context) {
+ if (!ctx) {
if (!usbi_default_context) {
- usbi_dbg("no default context, not initialized?");
+ usbi_dbg(ctx, "no default context, not initialized?");
usbi_mutex_static_unlock(&default_context_lock);
return;
}
if (--default_context_refcnt > 0) {
- usbi_dbg("not destroying default context");
+ usbi_dbg(ctx, "not destroying default context");
usbi_mutex_static_unlock(&default_context_lock);
return;
}
- usbi_dbg("destroying default context");
- /*
- * Setting this flag without unlocking the default context, as
- * we are actually destroying the default context.
- * usbi_default_context is not set to NULL yet, as all activities
- * would only stop after usbi_backend->exit() returns.
- */
- destroying_default_context = 1;
+ usbi_dbg(ctx, "destroying default context");
+ _ctx = usbi_default_context;
} else {
- /* Unlock default context, as we're not modifying it. */
- usbi_mutex_static_unlock(&default_context_lock);
+ usbi_dbg(ctx, " ");
+ _ctx = ctx;
}
usbi_mutex_static_lock(&active_contexts_lock);
- list_del(&ctx->list);
+ list_del(&_ctx->list);
usbi_mutex_static_unlock(&active_contexts_lock);
- if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
- usbi_hotplug_deregister(ctx, 1);
-
- /*
- * Ensure any pending unplug events are read from the hotplug
- * pipe. The usb_device-s hold in the events are no longer part
- * of usb_devs, but the events still hold a reference!
- *
- * Note we don't do this if the application has left devices
- * open (which implies a buggy app) to avoid packet completion
- * handlers running when the app does not expect them to run.
- */
- if (list_empty(&ctx->open_devs))
- libusb_handle_events_timeout(ctx, &tv);
+ if (usbi_backend.exit)
+ usbi_backend.exit(_ctx);
- usbi_mutex_lock(&ctx->usb_devs_lock);
- for_each_device_safe(ctx, dev, next) {
- list_del(&dev->list);
- libusb_unref_device(dev);
- }
- usbi_mutex_unlock(&ctx->usb_devs_lock);
- }
+ if (!ctx)
+ usbi_default_context = NULL;
+ if (ctx == usbi_fallback_context)
+ usbi_fallback_context = NULL;
- /* a few sanity checks. don't bother with locking because unless
- * there is an application bug, nobody will be accessing these. */
- if (!list_empty(&ctx->usb_devs))
- usbi_warn(ctx, "some libusb_devices were leaked");
- if (!list_empty(&ctx->open_devs))
- usbi_warn(ctx, "application left some devices open");
+ usbi_mutex_static_unlock(&default_context_lock);
- usbi_io_exit(ctx);
- if (usbi_backend.exit)
- usbi_backend.exit(ctx);
+ /* Don't bother with locking after this point because unless there is
+ * an application bug, nobody will be accessing the context. */
- usbi_mutex_destroy(&ctx->open_devs_lock);
- usbi_mutex_destroy(&ctx->usb_devs_lock);
- usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
- free(ctx);
+ usbi_hotplug_exit(_ctx);
+ usbi_io_exit(_ctx);
- if (destroying_default_context) {
- usbi_default_context = NULL;
- usbi_mutex_static_unlock(&default_context_lock);
+ for_each_device(_ctx, dev) {
+ usbi_warn(_ctx, "device %d.%d still referenced",
+ dev->bus_number, dev->device_address);
+ DEVICE_CTX(dev) = NULL;
}
+
+ if (!list_empty(&_ctx->open_devs))
+ usbi_warn(_ctx, "application left some devices open");
+
+ usbi_mutex_destroy(&_ctx->open_devs_lock);
+ usbi_mutex_destroy(&_ctx->usb_devs_lock);
+
+ free(_ctx);
}
/** \ingroup libusb_misc
@@ -2574,7 +2585,8 @@ static void log_v(struct libusb_context *ctx, enum libusb_log_level level,
#else
enum libusb_log_level ctx_level;
- ctx = usbi_get_context(ctx);
+ ctx = ctx ? ctx : usbi_default_context;
+ ctx = ctx ? ctx : usbi_fallback_context;
if (ctx)
ctx_level = ctx->debug;
else
diff --git a/libusb/descriptor.c b/libusb/descriptor.c
index ecd9441..253ef1c 100644
--- a/libusb/descriptor.c
+++ b/libusb/descriptor.c
@@ -140,7 +140,7 @@ static int parse_endpoint(struct libusb_context *ctx,
header->bDescriptorType == LIBUSB_DT_DEVICE)
break;
- usbi_dbg("skipping descriptor 0x%x", header->bDescriptorType);
+ usbi_dbg(ctx, "skipping descriptor 0x%x", header->bDescriptorType);
buffer += header->bLength;
size -= header->bLength;
parsed += header->bLength;
@@ -414,7 +414,7 @@ static int parse_configuration(struct libusb_context *ctx,
header->bDescriptorType == LIBUSB_DT_DEVICE)
break;
- usbi_dbg("skipping descriptor 0x%x", header->bDescriptorType);
+ usbi_dbg(ctx, "skipping descriptor 0x%x", header->bDescriptorType);
buffer += header->bLength;
size -= header->bLength;
}
@@ -531,7 +531,7 @@ static int get_config_descriptor(struct libusb_device *dev, uint8_t config_idx,
int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev,
struct libusb_device_descriptor *desc)
{
- usbi_dbg(" ");
+ usbi_dbg(DEVICE_CTX(dev), " ");
static_assert(sizeof(dev->device_descriptor) == LIBUSB_DT_DEVICE_SIZE,
"struct libusb_device_descriptor is not expected size");
*desc = dev->device_descriptor;
@@ -601,7 +601,7 @@ int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev,
uint8_t *buf;
int r;
- usbi_dbg("index %u", config_index);
+ usbi_dbg(DEVICE_CTX(dev), "index %u", config_index);
if (config_index >= dev->device_descriptor.bNumConfigurations)
return LIBUSB_ERROR_NOT_FOUND;
@@ -656,7 +656,7 @@ int API_EXPORTED libusb_get_config_descriptor_by_value(libusb_device *dev,
return raw_desc_to_config(DEVICE_CTX(dev), buf, r, config);
}
- usbi_dbg("value %u", bConfigurationValue);
+ usbi_dbg(DEVICE_CTX(dev), "value %u", bConfigurationValue);
for (idx = 0; idx < dev->device_descriptor.bNumConfigurations; idx++) {
union usbi_config_desc_buf _config;
@@ -850,23 +850,24 @@ int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle,
uint16_t bos_len;
uint8_t *bos_data;
int r;
+ struct libusb_context *ctx = HANDLE_CTX(dev_handle);
/* Read the BOS. This generates 2 requests on the bus,
* one for the header, and one for the full BOS */
r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, _bos.buf, sizeof(_bos.buf));
if (r < 0) {
if (r != LIBUSB_ERROR_PIPE)
- usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r);
+ usbi_err(ctx, "failed to read BOS (%d)", r);
return r;
}
if (r < LIBUSB_DT_BOS_SIZE) {
- usbi_err(HANDLE_CTX(dev_handle), "short BOS read %d/%d",
+ usbi_err(ctx, "short BOS read %d/%d",
r, LIBUSB_DT_BOS_SIZE);
return LIBUSB_ERROR_IO;
}
bos_len = libusb_le16_to_cpu(_bos.desc.wTotalLength);
- usbi_dbg("found BOS descriptor: size %u bytes, %u capabilities",
+ usbi_dbg(ctx, "found BOS descriptor: size %u bytes, %u capabilities",
bos_len, _bos.desc.bNumDeviceCaps);
bos_data = calloc(1, bos_len);
if (!bos_data)
@@ -875,11 +876,10 @@ int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle,
r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_data, bos_len);
if (r >= 0) {
if (r != (int)bos_len)
- usbi_warn(HANDLE_CTX(dev_handle), "short BOS read %d/%u",
- r, bos_len);
+ usbi_warn(ctx, "short BOS read %d/%u", r, bos_len);
r = parse_bos(HANDLE_CTX(dev_handle), bos, bos_data, r);
} else {
- usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r);
+ usbi_err(ctx, "failed to read BOS (%d)", r);
}
free(bos_data);
@@ -1109,7 +1109,7 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha
else if (str.desc.bDescriptorType != LIBUSB_DT_STRING)
return LIBUSB_ERROR_IO;
else if (str.desc.bLength & 1)
- usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor", str.desc.bLength);
+ usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for language ID string descriptor", str.desc.bLength);
langid = libusb_le16_to_cpu(str.desc.wData[0]);
r = libusb_get_string_descriptor(dev_handle, desc_index, langid, str.buf, sizeof(str.buf));
@@ -1120,7 +1120,7 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha
else if (str.desc.bDescriptorType != LIBUSB_DT_STRING)
return LIBUSB_ERROR_IO;
else if ((str.desc.bLength & 1) || str.desc.bLength != r)
- usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor", str.desc.bLength);
+ usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor (read %d)", str.desc.bLength, r);
di = 0;
for (si = 2; si < str.desc.bLength; si += 2) {
diff --git a/libusb/hotplug.c b/libusb/hotplug.c
index e3e5e76..6b743c7 100644
--- a/libusb/hotplug.c
+++ b/libusb/hotplug.c
@@ -1,7 +1,7 @@
/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
/*
* Hotplug functions for libusb
- * Copyright © 2012-2013 Nathan Hjelm <hjelmn@mac.com>
+ * Copyright © 2012-2021 Nathan Hjelm <hjelmn@mac.com>
* Copyright © 2012-2013 Peter Stuge <peter@stuge.se>
*
* This library is free software; you can redistribute it and/or
@@ -20,7 +20,6 @@
*/
#include "libusbi.h"
-#include "hotplug.h"
/**
* @defgroup libusb_hotplug Device hotplug event notification
@@ -63,7 +62,7 @@
* 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()
+ * libusb_device_handles for the device it is up to you to call libusb_close()
* on each device 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.
@@ -144,15 +143,79 @@ int main (void) {
*/
#define VALID_HOTPLUG_EVENTS \
- (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | \
- LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
+ (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | \
+ LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
#define VALID_HOTPLUG_FLAGS \
- (LIBUSB_HOTPLUG_ENUMERATE)
+ (LIBUSB_HOTPLUG_ENUMERATE)
-static int usbi_hotplug_match_cb(struct libusb_context *ctx,
- struct libusb_device *dev, libusb_hotplug_event event,
- struct libusb_hotplug_callback *hotplug_cb)
+void usbi_hotplug_init(struct libusb_context *ctx)
+{
+ /* check for hotplug support */
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
+ return;
+
+ usbi_mutex_init(&ctx->hotplug_cbs_lock);
+ list_init(&ctx->hotplug_cbs);
+ ctx->next_hotplug_cb_handle = 1;
+ usbi_atomic_store(&ctx->hotplug_ready, 1);
+}
+
+void usbi_hotplug_exit(struct libusb_context *ctx)
+{
+ struct usbi_hotplug_callback *hotplug_cb, *next_cb;
+ struct usbi_hotplug_message *msg;
+ struct libusb_device *dev, *next_dev;
+
+ /* check for hotplug support */
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
+ return;
+
+ if (!usbi_atomic_load(&ctx->hotplug_ready))
+ return;
+
+ /* free all registered hotplug callbacks */
+ for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) {
+ list_del(&hotplug_cb->list);
+ free(hotplug_cb);
+ }
+
+ /* free all pending hotplug messages */
+ while (!list_empty(&ctx->hotplug_msgs)) {
+ msg = list_first_entry(&ctx->hotplug_msgs, struct usbi_hotplug_message, list);
+
+ /* if the device left, the message holds a reference
+ * and we must drop it */
+ if (msg->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
+ libusb_unref_device(msg->device);
+
+ list_del(&msg->list);
+ free(msg);
+ }
+
+ /* free all discovered devices. due to parent references loop until no devices are freed. */
+ for_each_device_safe(ctx, dev, next_dev) {
+ /* remove the device from the usb_devs list only if there are no
+ * references held, otherwise leave it on the list so that a
+ * warning message will be shown */
+ if (usbi_atomic_load(&dev->refcnt) == 1) {
+ list_del(&dev->list);
+ }
+ if (dev->parent_dev && usbi_atomic_load(&dev->parent_dev->refcnt) == 1) {
+ /* the parent was before this device in the list and will be released.
+ remove it from the list. this is safe as parent_dev can not be
+ equal to next_dev. */
+ assert (dev->parent_dev != next_dev);
+ list_del(&dev->parent_dev->list);
+ }
+ libusb_unref_device(dev);
+ }
+
+ usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
+}
+
+static int usbi_hotplug_match_cb(struct libusb_device *dev,
+ libusb_hotplug_event event, struct usbi_hotplug_callback *hotplug_cb)
{
if (!(hotplug_cb->flags & event)) {
return 0;
@@ -173,71 +236,100 @@ static int usbi_hotplug_match_cb(struct libusb_context *ctx,
return 0;
}
- return hotplug_cb->cb(ctx, dev, event, hotplug_cb->user_data);
-}
-
-void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
- libusb_hotplug_event event)
-{
- struct libusb_hotplug_callback *hotplug_cb, *next;
- int ret;
-
- usbi_mutex_lock(&ctx->hotplug_cbs_lock);
-
- for_each_hotplug_cb_safe(ctx, hotplug_cb, next) {
- if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) {
- /* process deregistration in usbi_hotplug_deregister() */
- continue;
- }
-
- 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);
+ return hotplug_cb->cb(DEVICE_CTX(dev), dev, event, hotplug_cb->user_data);
}
void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
libusb_hotplug_event event)
{
- struct libusb_hotplug_message *message = calloc(1, sizeof(*message));
+ struct usbi_hotplug_message *msg;
unsigned int event_flags;
- if (!message) {
+ /* Only generate a notification if hotplug is ready. This prevents hotplug
+ * notifications from being generated during initial enumeration or if the
+ * backend does not support hotplug. */
+ if (!usbi_atomic_load(&ctx->hotplug_ready))
+ return;
+
+ msg = calloc(1, sizeof(*msg));
+ if (!msg) {
usbi_err(ctx, "error allocating hotplug message");
return;
}
- message->event = event;
- message->device = dev;
+ msg->event = event;
+ msg->device = dev;
/* Take the event data lock and add this message to the list.
* Only signal an event if there are no prior pending events. */
usbi_mutex_lock(&ctx->event_data_lock);
event_flags = ctx->event_flags;
ctx->event_flags |= USBI_EVENT_HOTPLUG_MSG_PENDING;
- list_add_tail(&message->list, &ctx->hotplug_msgs);
+ list_add_tail(&msg->list, &ctx->hotplug_msgs);
if (!event_flags)
usbi_signal_event(&ctx->event);
usbi_mutex_unlock(&ctx->event_data_lock);
}
+void usbi_hotplug_process(struct libusb_context *ctx, struct list_head *hotplug_msgs)
+{
+ struct usbi_hotplug_callback *hotplug_cb, *next_cb;
+ struct usbi_hotplug_message *msg;
+ int r;
+
+ usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+
+ /* dispatch all pending hotplug messages */
+ while (!list_empty(hotplug_msgs)) {
+ msg = list_first_entry(hotplug_msgs, struct usbi_hotplug_message, list);
+
+ for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) {
+ /* skip callbacks that have unregistered */
+ if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)
+ continue;
+
+ usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+ r = usbi_hotplug_match_cb(msg->device, msg->event, hotplug_cb);
+ usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+
+ if (r) {
+ list_del(&hotplug_cb->list);
+ free(hotplug_cb);
+ }
+ }
+
+ /* if the device left, the message holds a reference
+ * and we must drop it */
+ if (msg->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
+ libusb_unref_device(msg->device);
+
+ list_del(&msg->list);
+ free(msg);
+ }
+
+ /* free any callbacks that have unregistered */
+ for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) {
+ if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) {
+ usbi_dbg(ctx, "freeing hotplug cb %p with handle %d",
+ hotplug_cb, hotplug_cb->handle);
+ list_del(&hotplug_cb->list);
+ free(hotplug_cb);
+ }
+ }
+
+ usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+}
+
int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
int events, int flags,
int vendor_id, int product_id, int dev_class,
libusb_hotplug_callback_fn cb_fn, void *user_data,
libusb_hotplug_callback_handle *callback_handle)
{
- struct libusb_hotplug_callback *new_callback;
+ struct usbi_hotplug_callback *hotplug_cb;
/* check for sane values */
- if ((!events || (~VALID_HOTPLUG_EVENTS & events)) ||
+ if (!events || (~VALID_HOTPLUG_EVENTS & events) ||
(~VALID_HOTPLUG_FLAGS & flags) ||
(LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) ||
(LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) ||
@@ -247,47 +339,45 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
}
/* check for hotplug support */
- if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
return LIBUSB_ERROR_NOT_SUPPORTED;
- }
ctx = usbi_get_context(ctx);
- new_callback = calloc(1, sizeof(*new_callback));
- if (!new_callback) {
+ hotplug_cb = calloc(1, sizeof(*hotplug_cb));
+ if (!hotplug_cb)
return LIBUSB_ERROR_NO_MEM;
- }
- new_callback->flags = (uint8_t)events;
+ hotplug_cb->flags = (uint8_t)events;
if (LIBUSB_HOTPLUG_MATCH_ANY != vendor_id) {
- new_callback->flags |= USBI_HOTPLUG_VENDOR_ID_VALID;
- new_callback->vendor_id = (uint16_t)vendor_id;
+ hotplug_cb->flags |= USBI_HOTPLUG_VENDOR_ID_VALID;
+ hotplug_cb->vendor_id = (uint16_t)vendor_id;
}
if (LIBUSB_HOTPLUG_MATCH_ANY != product_id) {
- new_callback->flags |= USBI_HOTPLUG_PRODUCT_ID_VALID;
- new_callback->product_id = (uint16_t)product_id;
+ hotplug_cb->flags |= USBI_HOTPLUG_PRODUCT_ID_VALID;
+ hotplug_cb->product_id = (uint16_t)product_id;
}
if (LIBUSB_HOTPLUG_MATCH_ANY != dev_class) {
- new_callback->flags |= USBI_HOTPLUG_DEV_CLASS_VALID;
- new_callback->dev_class = (uint8_t)dev_class;
+ hotplug_cb->flags |= USBI_HOTPLUG_DEV_CLASS_VALID;
+ hotplug_cb->dev_class = (uint8_t)dev_class;
}
- new_callback->cb = cb_fn;
- new_callback->user_data = user_data;
+ hotplug_cb->cb = cb_fn;
+ hotplug_cb->user_data = user_data;
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
/* protect the handle by the context hotplug lock */
- new_callback->handle = ctx->next_hotplug_cb_handle++;
+ hotplug_cb->handle = ctx->next_hotplug_cb_handle++;
/* handle the unlikely case of overflow */
if (ctx->next_hotplug_cb_handle < 0)
ctx->next_hotplug_cb_handle = 1;
- list_add(&new_callback->list, &ctx->hotplug_cbs);
+ list_add(&hotplug_cb->list, &ctx->hotplug_cbs);
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
- usbi_dbg("new hotplug cb %p with handle %d", new_callback, new_callback->handle);
+ usbi_dbg(ctx, "new hotplug cb %p with handle %d", hotplug_cb, hotplug_cb->handle);
if ((flags & LIBUSB_HOTPLUG_ENUMERATE) && (events & LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)) {
ssize_t i, len;
@@ -295,23 +385,21 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
len = libusb_get_device_list(ctx, &devs);
if (len < 0) {
- libusb_hotplug_deregister_callback(ctx,
- new_callback->handle);
+ libusb_hotplug_deregister_callback(ctx, hotplug_cb->handle);
return (int)len;
}
for (i = 0; i < len; i++) {
- usbi_hotplug_match_cb(ctx, devs[i],
+ usbi_hotplug_match_cb(devs[i],
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
- new_callback);
+ hotplug_cb);
}
libusb_free_device_list(devs, 1);
}
-
if (callback_handle)
- *callback_handle = new_callback->handle;
+ *callback_handle = hotplug_cb->handle;
return LIBUSB_SUCCESS;
}
@@ -319,24 +407,24 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
void API_EXPORTED libusb_hotplug_deregister_callback(libusb_context *ctx,
libusb_hotplug_callback_handle callback_handle)
{
- struct libusb_hotplug_callback *hotplug_cb;
+ struct usbi_hotplug_callback *hotplug_cb;
int deregistered = 0;
/* check for hotplug support */
- if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
return;
- }
- usbi_dbg("deregister hotplug cb %d", callback_handle);
+ usbi_dbg(ctx, "deregister hotplug cb %d", callback_handle);
ctx = usbi_get_context(ctx);
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
for_each_hotplug_cb(ctx, hotplug_cb) {
if (callback_handle == hotplug_cb->handle) {
- /* Mark this callback for deregistration */
+ /* mark this callback for deregistration */
hotplug_cb->flags |= USBI_HOTPLUG_NEEDS_FREE;
deregistered = 1;
+ break;
}
}
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
@@ -357,15 +445,14 @@ DEFAULT_VISIBILITY
void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx,
libusb_hotplug_callback_handle callback_handle)
{
- struct libusb_hotplug_callback *hotplug_cb;
+ struct usbi_hotplug_callback *hotplug_cb;
void *user_data = NULL;
/* check for hotplug support */
- if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+ if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
return NULL;
- }
- usbi_dbg("get hotplug user data %d", callback_handle);
+ usbi_dbg(ctx, "get hotplug cb %d user data", callback_handle);
ctx = usbi_get_context(ctx);
@@ -373,25 +460,10 @@ void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx,
for_each_hotplug_cb(ctx, hotplug_cb) {
if (callback_handle == hotplug_cb->handle) {
user_data = hotplug_cb->user_data;
+ break;
}
}
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
return user_data;
}
-
-void usbi_hotplug_deregister(struct libusb_context *ctx, int forced)
-{
- struct libusb_hotplug_callback *hotplug_cb, *next;
-
- usbi_mutex_lock(&ctx->hotplug_cbs_lock);
- for_each_hotplug_cb_safe(ctx, hotplug_cb, next) {
- if (forced || (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)) {
- usbi_dbg("freeing hotplug cb %p with handle %d", hotplug_cb,
- hotplug_cb->handle);
- list_del(&hotplug_cb->list);
- free(hotplug_cb);
- }
- }
- usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
-}
diff --git a/libusb/hotplug.h b/libusb/hotplug.h
deleted file mode 100644
index 161f7e5..0000000
--- a/libusb/hotplug.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
-/*
- * Hotplug support for libusb
- * 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
- */
-
-#ifndef USBI_HOTPLUG_H
-#define USBI_HOTPLUG_H
-
-#include "libusbi.h"
-
-enum usbi_hotplug_flags {
- /* This callback is interested in device arrivals */
- USBI_HOTPLUG_DEVICE_ARRIVED = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
-
- /* This callback is interested in device removals */
- USBI_HOTPLUG_DEVICE_LEFT = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
-
- /* IMPORTANT: The values for the below entries must start *after*
- * the highest value of the above entries!!!
- */
-
- /* The vendor_id field is valid for matching */
- USBI_HOTPLUG_VENDOR_ID_VALID = (1U << 3),
-
- /* The product_id field is valid for matching */
- USBI_HOTPLUG_PRODUCT_ID_VALID = (1U << 4),
-
- /* The dev_class field is valid for matching */
- USBI_HOTPLUG_DEV_CLASS_VALID = (1U << 5),
-
- /* This callback has been unregistered and needs to be freed */
- USBI_HOTPLUG_NEEDS_FREE = (1U << 6),
-};
-
-/** \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 {
- /** Flags that control how this callback behaves */
- uint8_t flags;
-
- /** Vendor ID to match (if flags says this is valid) */
- uint16_t vendor_id;
-
- /** Product ID to match (if flags says this is valid) */
- uint16_t product_id;
-
- /** Device class to match (if flags says this is valid) */
- uint8_t dev_class;
-
- /** 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;
-
- /** List this callback is registered in (ctx->hotplug_cbs) */
- struct list_head list;
-};
-
-struct libusb_hotplug_message {
- /** The hotplug event that occurred */
- libusb_hotplug_event event;
-
- /** The device for which this hotplug event occurred */
- struct libusb_device *device;
-
- /** List this message is contained in (ctx->hotplug_msgs) */
- struct list_head list;
-};
-
-#define for_each_hotplug_cb(ctx, c) \
- for_each_helper(c, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback)
-
-#define for_each_hotplug_cb_safe(ctx, c, n) \
- for_each_safe_helper(c, n, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback)
-
-void usbi_hotplug_deregister(struct libusb_context *ctx, int forced);
-void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
- libusb_hotplug_event event);
-void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
- libusb_hotplug_event event);
-
-#endif
diff --git a/libusb/io.c b/libusb/io.c
index 0e960dd..9e3146c 100644
--- a/libusb/io.c
+++ b/libusb/io.c
@@ -22,7 +22,6 @@
*/
#include "libusbi.h"
-#include "hotplug.h"
/**
* \page libusb_io Synchronous and asynchronous device I/O
@@ -73,7 +72,7 @@
* a single function call. When the function call returns, the transfer has
* completed and you can parse the results.
*
- * If you have used the libusb-0.1 before, this I/O style will seem familiar to
+ * If you have used libusb-0.1 before, this I/O style will seem familiar to
* you. libusb-0.1 only offered a synchronous interface.
*
* In our input device example, to read button presses you might write code
@@ -1181,12 +1180,12 @@ int usbi_io_init(struct libusb_context *ctx)
#ifdef HAVE_OS_TIMER
r = usbi_create_timer(&ctx->timer);
if (r == 0) {
- usbi_dbg("using timer for timeouts");
+ usbi_dbg(ctx, "using timer for timeouts");
r = usbi_add_event_source(ctx, USBI_TIMER_OS_HANDLE(&ctx->timer), USBI_TIMER_POLL_EVENTS);
if (r < 0)
goto err_destroy_timer;
} else {
- usbi_dbg("timer not available for timeouts");
+ usbi_dbg(ctx, "timer not available for timeouts");
}
#endif
@@ -1310,7 +1309,6 @@ struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(
itransfer->priv = ptr;
usbi_mutex_init(&itransfer->lock);
transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- usbi_dbg("transfer %p", transfer);
return transfer;
}
@@ -1340,12 +1338,14 @@ void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer)
if (!transfer)
return;
- usbi_dbg("transfer %p", transfer);
+ usbi_dbg(TRANSFER_CTX(transfer), "transfer %p", transfer);
if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER)
free(transfer->buffer);
itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
usbi_mutex_destroy(&itransfer->lock);
+ if (itransfer->dev)
+ libusb_unref_device(itransfer->dev);
priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size);
ptr = (unsigned char *)itransfer - priv_size;
@@ -1376,12 +1376,12 @@ static int arm_timer_for_next_timeout(struct libusb_context *ctx)
/* act on first transfer that has not already been handled */
if (!(itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) {
- usbi_dbg("next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
+ usbi_dbg(ctx, "next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
return usbi_arm_timer(&ctx->timer, cur_ts);
}
}
- usbi_dbg("no timeouts, disarming timer");
+ usbi_dbg(ctx, "no timeouts, disarming timer");
return usbi_disarm_timer(&ctx->timer);
}
#else
@@ -1438,7 +1438,7 @@ out:
if (first && usbi_using_timer(ctx) && TIMESPEC_IS_SET(timeout)) {
/* if this transfer has the lowest timeout of all active transfers,
* rearm the timer with this transfer's timeout */
- usbi_dbg("arm timer for timeout in %ums (first in line)",
+ usbi_dbg(ctx, "arm timer for timeout in %ums (first in line)",
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
r = usbi_arm_timer(&ctx->timer, timeout);
}
@@ -1491,10 +1491,16 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer)
{
struct usbi_transfer *itransfer =
LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
- struct libusb_context *ctx = TRANSFER_CTX(transfer);
+ struct libusb_context *ctx;
int r;
- usbi_dbg("transfer %p", transfer);
+ assert(transfer->dev_handle);
+ if (itransfer->dev)
+ libusb_unref_device(itransfer->dev);
+ itransfer->dev = libusb_ref_device(transfer->dev_handle->dev);
+
+ ctx = HANDLE_CTX(transfer->dev_handle);
+ usbi_dbg(ctx, "transfer %p", transfer);
/*
* Important note on locking, this function takes / releases locks
@@ -1546,15 +1552,13 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer)
}
/*
* We must release the flying transfers lock here, because with
- * some backends the submit_transfer method is synchroneous.
+ * some backends the submit_transfer method is synchronous.
*/
usbi_mutex_unlock(&ctx->flying_transfers_lock);
r = usbi_backend.submit_transfer(itransfer);
if (r == LIBUSB_SUCCESS) {
itransfer->state_flags |= USBI_TRANSFER_IN_FLIGHT;
- /* keep a reference to this device */
- libusb_ref_device(transfer->dev_handle->dev);
}
usbi_mutex_unlock(&itransfer->lock);
@@ -1572,6 +1576,26 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer)
* \ref libusb_transfer_status::LIBUSB_TRANSFER_CANCELLED
* "LIBUSB_TRANSFER_CANCELLED."
*
+ * This function behaves differently on Darwin-based systems (macOS and iOS):
+ *
+ * - Calling this function for one transfer will cause all transfers on the
+ * same endpoint to be cancelled. Your callback function will be invoked with
+ * a transfer status of
+ * \ref libusb_transfer_status::LIBUSB_TRANSFER_CANCELLED
+ * "LIBUSB_TRANSFER_CANCELLED" for each transfer that was cancelled.
+
+ * - Calling this function also sends a \c ClearFeature(ENDPOINT_HALT) request
+ * for the transfer's endpoint. If the device does not handle this request
+ * correctly, the data toggle bits for the endpoint can be left out of sync
+ * between host and device, which can have unpredictable results when the
+ * next data is sent on the endpoint, including data being silently lost.
+ * A call to \ref libusb_clear_halt will not resolve this situation, since
+ * that function uses the same request. Therefore, if your program runs on
+ * Darwin and uses a device that does not correctly implement
+ * \c ClearFeature(ENDPOINT_HALT) requests, it may only be safe to cancel
+ * transfers when followed by a device reset using
+ * \ref libusb_reset_device.
+ *
* \param transfer the transfer to cancel
* \returns 0 on success
* \returns LIBUSB_ERROR_NOT_FOUND if the transfer is not in progress,
@@ -1582,9 +1606,10 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer)
{
struct usbi_transfer *itransfer =
LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+ struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
int r;
- usbi_dbg("transfer %p", transfer );
+ usbi_dbg(ctx, "transfer %p", transfer );
usbi_mutex_lock(&itransfer->lock);
if (!(itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT)
|| (itransfer->state_flags & USBI_TRANSFER_CANCELLING)) {
@@ -1595,10 +1620,9 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer)
if (r < 0) {
if (r != LIBUSB_ERROR_NOT_FOUND &&
r != LIBUSB_ERROR_NO_DEVICE)
- usbi_err(TRANSFER_CTX(transfer),
- "cancel transfer failed error %d", r);
+ usbi_err(ctx, "cancel transfer failed error %d", r);
else
- usbi_dbg("cancel transfer failed error %d", r);
+ usbi_dbg(ctx, "cancel transfer failed error %d", r);
if (r == LIBUSB_ERROR_NO_DEVICE)
itransfer->state_flags |= USBI_TRANSFER_DEVICE_DISAPPEARED;
@@ -1661,13 +1685,13 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
{
struct libusb_transfer *transfer =
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- struct libusb_device_handle *dev_handle = transfer->dev_handle;
+ struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
uint8_t flags;
int r;
r = remove_from_flying_list(itransfer);
if (r < 0)
- usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout");
+ usbi_err(ctx, "failed to set timer for next timeout");
usbi_mutex_lock(&itransfer->lock);
itransfer->state_flags &= ~USBI_TRANSFER_IN_FLIGHT;
@@ -1679,7 +1703,7 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
if (transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL)
rqlen -= LIBUSB_CONTROL_SETUP_SIZE;
if (rqlen != itransfer->transferred) {
- usbi_dbg("interpreting short transfer as error");
+ usbi_dbg(ctx, "interpreting short transfer as error");
status = LIBUSB_TRANSFER_ERROR;
}
}
@@ -1687,14 +1711,13 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
flags = transfer->flags;
transfer->status = status;
transfer->actual_length = itransfer->transferred;
- usbi_dbg("transfer %p has callback %p", transfer, transfer->callback);
+ usbi_dbg(ctx, "transfer %p has callback %p", transfer, transfer->callback);
if (transfer->callback)
transfer->callback(transfer);
/* transfer might have been freed by the above call, do not use from
* this point. */
if (flags & LIBUSB_TRANSFER_FREE_TRANSFER)
libusb_free_transfer(transfer);
- libusb_unref_device(dev_handle->dev);
return r;
}
@@ -1715,7 +1738,7 @@ int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer)
/* if the URB was cancelled due to timeout, report timeout to the user */
if (timed_out) {
- usbi_dbg("detected timeout cancellation");
+ usbi_dbg(ctx, "detected timeout cancellation");
return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_TIMED_OUT);
}
@@ -1728,10 +1751,10 @@ int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer)
* function will be called the next time an event handler runs. */
void usbi_signal_transfer_completion(struct usbi_transfer *itransfer)
{
- libusb_device_handle *dev_handle = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->dev_handle;
+ struct libusb_device *dev = itransfer->dev;
- if (dev_handle) {
- struct libusb_context *ctx = HANDLE_CTX(dev_handle);
+ if (dev) {
+ struct libusb_context *ctx = DEVICE_CTX(dev);
unsigned int event_flags;
usbi_mutex_lock(&ctx->event_data_lock);
@@ -1776,7 +1799,7 @@ int API_EXPORTED libusb_try_lock_events(libusb_context *ctx)
ru = ctx->device_close;
usbi_mutex_unlock(&ctx->event_data_lock);
if (ru) {
- usbi_dbg("someone else is closing a device");
+ usbi_dbg(ctx, "someone else is closing a device");
return 1;
}
@@ -1868,7 +1891,7 @@ int API_EXPORTED libusb_event_handling_ok(libusb_context *ctx)
r = ctx->device_close;
usbi_mutex_unlock(&ctx->event_data_lock);
if (r) {
- usbi_dbg("someone else is closing a device");
+ usbi_dbg(ctx, "someone else is closing a device");
return 0;
}
@@ -1897,7 +1920,7 @@ int API_EXPORTED libusb_event_handler_active(libusb_context *ctx)
r = ctx->device_close;
usbi_mutex_unlock(&ctx->event_data_lock);
if (r) {
- usbi_dbg("someone else is closing a device");
+ usbi_dbg(ctx, "someone else is closing a device");
return 1;
}
@@ -1918,7 +1941,7 @@ void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx)
{
unsigned int event_flags;
- usbi_dbg(" ");
+ usbi_dbg(ctx, " ");
ctx = usbi_get_context(ctx);
usbi_mutex_lock(&ctx->event_data_lock);
@@ -2073,9 +2096,10 @@ static void handle_timeouts(struct libusb_context *ctx)
static int handle_event_trigger(struct libusb_context *ctx)
{
struct list_head hotplug_msgs;
+ int hotplug_event = 0;
int r = 0;
- usbi_dbg("event triggered");
+ usbi_dbg(ctx, "event triggered");
list_init(&hotplug_msgs);
@@ -2084,21 +2108,28 @@ static int handle_event_trigger(struct libusb_context *ctx)
/* check if someone modified the event sources */
if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED)
- usbi_dbg("someone updated the event sources");
+ usbi_dbg(ctx, "someone updated the event sources");
if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) {
- usbi_dbg("someone purposefully interrupted");
+ usbi_dbg(ctx, "someone purposefully interrupted");
ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT;
}
+ if (ctx->event_flags & USBI_EVENT_HOTPLUG_CB_DEREGISTERED) {
+ usbi_dbg(ctx, "someone unregistered a hotplug cb");
+ ctx->event_flags &= ~USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
+ hotplug_event = 1;
+ }
+
/* check if someone is closing a device */
if (ctx->event_flags & USBI_EVENT_DEVICE_CLOSE)
- usbi_dbg("someone is closing a device");
+ usbi_dbg(ctx, "someone is closing a device");
/* check for any pending hotplug messages */
if (ctx->event_flags & USBI_EVENT_HOTPLUG_MSG_PENDING) {
- usbi_dbg("hotplug message received");
+ usbi_dbg(ctx, "hotplug message received");
ctx->event_flags &= ~USBI_EVENT_HOTPLUG_MSG_PENDING;
+ hotplug_event = 1;
assert(!list_empty(&ctx->hotplug_msgs));
list_cut(&hotplug_msgs, &ctx->hotplug_msgs);
}
@@ -2136,20 +2167,9 @@ static int handle_event_trigger(struct libusb_context *ctx)
usbi_mutex_unlock(&ctx->event_data_lock);
- /* process the hotplug messages, if any */
- while (!list_empty(&hotplug_msgs)) {
- struct libusb_hotplug_message *message =
- list_first_entry(&hotplug_msgs, struct libusb_hotplug_message, list);
-
- usbi_hotplug_match(ctx, message->device, message->event);
-
- /* the device left, dereference the device */
- if (message->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
- libusb_unref_device(message->device);
-
- list_del(&message->list);
- free(message);
- }
+ /* process the hotplug events, if any */
+ if (hotplug_event)
+ usbi_hotplug_process(ctx, &hotplug_msgs);
return r;
}
@@ -2190,7 +2210,7 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv)
* save the additional overhead */
usbi_mutex_lock(&ctx->event_data_lock);
if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) {
- usbi_dbg("event sources modified, reallocating event data");
+ usbi_dbg(ctx, "event sources modified, reallocating event data");
/* free anything removed since we last ran */
cleanup_removed_event_sources(ctx);
@@ -2337,7 +2357,7 @@ retry:
if (libusb_try_lock_events(ctx) == 0) {
if (completed == NULL || !*completed) {
/* we obtained the event lock: do our own event handling */
- usbi_dbg("doing our own event handling");
+ usbi_dbg(ctx, "doing our own event handling");
r = handle_events(ctx, &poll_timeout);
}
libusb_unlock_events(ctx);
@@ -2355,11 +2375,11 @@ retry:
/* we hit a race: whoever was event handling earlier finished in the
* time it took us to reach this point. try the cycle again. */
libusb_unlock_event_waiters(ctx);
- usbi_dbg("event handler was active but went away, retrying");
+ usbi_dbg(ctx, "event handler was active but went away, retrying");
goto retry;
}
- usbi_dbg("another thread is doing event handling");
+ usbi_dbg(ctx, "another thread is doing event handling");
r = libusb_wait_for_event(ctx, &poll_timeout);
already_done:
@@ -2554,7 +2574,7 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
usbi_mutex_lock(&ctx->flying_transfers_lock);
if (list_empty(&ctx->flying_transfers)) {
usbi_mutex_unlock(&ctx->flying_transfers_lock);
- usbi_dbg("no URBs, no timeout!");
+ usbi_dbg(ctx, "no URBs, no timeout!");
return 0;
}
@@ -2573,19 +2593,19 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
usbi_mutex_unlock(&ctx->flying_transfers_lock);
if (!TIMESPEC_IS_SET(&next_timeout)) {
- usbi_dbg("no URB with timeout or all handled by OS; no timeout!");
+ usbi_dbg(ctx, "no URB with timeout or all handled by OS; no timeout!");
return 0;
}
usbi_get_monotonic_time(&systime);
if (!TIMESPEC_CMP(&systime, &next_timeout, <)) {
- usbi_dbg("first timeout already expired");
+ usbi_dbg(ctx, "first timeout already expired");
timerclear(tv);
} else {
TIMESPEC_SUB(&next_timeout, &systime, &next_timeout);
TIMESPEC_TO_TIMEVAL(tv, &next_timeout);
- usbi_dbg("next timeout in %ld.%06lds", (long)tv->tv_sec, (long)tv->tv_usec);
+ usbi_dbg(ctx, "next timeout in %ld.%06lds", (long)tv->tv_sec, (long)tv->tv_usec);
}
return 1;
@@ -2656,7 +2676,7 @@ int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle
if (!ievent_source)
return LIBUSB_ERROR_NO_MEM;
- usbi_dbg("add " USBI_OS_HANDLE_FORMAT_STRING " events %d", os_handle, poll_events);
+ usbi_dbg(ctx, "add " USBI_OS_HANDLE_FORMAT_STRING " events %d", os_handle, poll_events);
ievent_source->data.os_handle = os_handle;
ievent_source->data.poll_events = poll_events;
usbi_mutex_lock(&ctx->event_data_lock);
@@ -2678,7 +2698,7 @@ void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_ha
struct usbi_event_source *ievent_source;
int found = 0;
- usbi_dbg("remove " USBI_OS_HANDLE_FORMAT_STRING, os_handle);
+ usbi_dbg(ctx, "remove " USBI_OS_HANDLE_FORMAT_STRING, os_handle);
usbi_mutex_lock(&ctx->event_data_lock);
for_each_event_source(ctx, ievent_source) {
if (ievent_source->data.os_handle == os_handle) {
@@ -2688,7 +2708,7 @@ void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_ha
}
if (!found) {
- usbi_dbg("couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle);
+ usbi_dbg(ctx, "couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle);
usbi_mutex_unlock(&ctx->event_data_lock);
return;
}
@@ -2787,7 +2807,7 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle)
struct usbi_transfer *cur;
struct usbi_transfer *to_cancel;
- usbi_dbg("device %d.%d",
+ usbi_dbg(ctx, "device %d.%d",
dev_handle->dev->bus_number, dev_handle->dev->device_address);
/* terminate all pending transfers with the LIBUSB_TRANSFER_NO_DEVICE
@@ -2822,7 +2842,7 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle)
if (!to_cancel)
break;
- usbi_dbg("cancelling transfer %p from disconnect",
+ usbi_dbg(ctx, "cancelling transfer %p from disconnect",
USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel));
usbi_mutex_lock(&to_cancel->lock);
diff --git a/libusb/libusb-1.0.def b/libusb/libusb-1.0.def
index 700a8bc..5c7352e 100644
--- a/libusb/libusb-1.0.def
+++ b/libusb/libusb-1.0.def
@@ -150,12 +150,12 @@ EXPORTS
libusb_set_configuration@8 = libusb_set_configuration
libusb_set_debug
libusb_set_debug@8 = libusb_set_debug
- libusb_set_log_cb
- libusb_set_log_cb@12 = libusb_set_log_cb
libusb_set_interface_alt_setting
libusb_set_interface_alt_setting@12 = libusb_set_interface_alt_setting
+ libusb_set_log_cb
+ libusb_set_log_cb@12 = libusb_set_log_cb
libusb_set_option
- _libusb_set_option = libusb_set_option
+ libusb_set_option@8 = libusb_set_option
libusb_set_pollfd_notifiers
libusb_set_pollfd_notifiers@16 = libusb_set_pollfd_notifiers
libusb_setlocale
diff --git a/libusb/libusb.h b/libusb/libusb.h
index 1308571..2592ea7 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -26,13 +26,19 @@
#define LIBUSB_H
#if defined(_MSC_VER)
+#pragma warning(push)
+/* Disable: warning C4200: nonstandard extension used : zero-sized array in struct/union */
+#pragma warning(disable:4200)
/* on MS environments, the inline keyword is available in C++ only */
#if !defined(__cplusplus)
#define inline __inline
#endif
/* ssize_t is also not available */
+#ifndef _SSIZE_T_DEFINED
+#define _SSIZE_T_DEFINED
#include <basetsd.h>
typedef SSIZE_T ssize_t;
+#endif /* _SSIZE_T_DEFINED */
#endif /* _MSC_VER */
#include <limits.h>
@@ -136,7 +142,7 @@ typedef SSIZE_T ssize_t;
* Internally, LIBUSB_API_VERSION is defined as follows:
* (libusb major << 24) | (libusb minor << 16) | (16 bit incremental)
*/
-#define LIBUSB_API_VERSION 0x01000108
+#define LIBUSB_API_VERSION 0x01000109
/* The following is kept for compatibility, but will be deprecated in the future */
#define LIBUSBX_API_VERSION LIBUSB_API_VERSION
@@ -904,7 +910,7 @@ struct libusb_container_id_descriptor {
/** \ingroup libusb_asyncio
* Setup packet for control transfers. */
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) || defined(__WATCOMC__)
#pragma pack(push, 1)
#endif
struct libusb_control_setup {
@@ -932,7 +938,7 @@ struct libusb_control_setup {
/** Number of bytes to transfer */
uint16_t wLength;
} LIBUSB_PACKED;
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) || defined(__WATCOMC__)
#pragma pack(pop)
#endif
@@ -979,8 +985,9 @@ struct libusb_version {
* Sessions are created by libusb_init() and destroyed through libusb_exit().
* If your application is guaranteed to only ever include a single libusb
* user (i.e. you), you do not have to worry about contexts: pass NULL in
- * every function call where a context is required. The default context
- * will be used.
+ * every function call where a context is required, and the default context
+ * will be used. Note that libusb_set_option(NULL, ...) is special, and adds
+ * an option to a list of default options for new contexts.
*
* For more information, see \ref libusb_contexts.
*/
@@ -989,7 +996,7 @@ typedef struct libusb_context libusb_context;
/** \ingroup libusb_dev
* Structure representing a USB device detected on the system. This is an
* opaque type for which you are only ever provided with a pointer, usually
- * originating from libusb_get_device_list().
+ * originating from libusb_get_device_list() or libusb_hotplug_register_callback().
*
* 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().
@@ -1325,6 +1332,9 @@ enum libusb_log_level {
/** \ingroup libusb_lib
* Log callback mode.
+ *
+ * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107
+ *
* \see libusb_set_log_cb()
*/
enum libusb_log_cb_mode {
@@ -1341,6 +1351,9 @@ enum libusb_log_cb_mode {
* is a global log message
* \param level the log level, see \ref libusb_log_level for a description
* \param str the log message
+ *
+ * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107
+ *
* \see libusb_set_log_cb()
*/
typedef void (LIBUSB_CALL *libusb_log_cb)(libusb_context *ctx,
@@ -2092,20 +2105,36 @@ enum libusb_option {
*/
LIBUSB_OPTION_USE_USBDK = 1,
- /** Set libusb has weak authority. With this option, libusb will skip
- * scan devices in libusb_init.
+ /** Do not scan for devices
+ *
+ * With this option set, libusb will skip scanning devices in
+ * libusb_init(). Must be set before calling libusb_init().
*
- * This option should be set before calling libusb_init(), otherwise
- * libusb_init will failed. Normally libusb_wrap_sys_device need set
- * this option.
+ * Hotplug functionality will also be deactivated.
*
- * Only valid on Linux-based operating system, such as Android.
+ * The option is useful in combination with libusb_wrap_sys_device(),
+ * which can access a device directly without prior device scanning.
+ *
+ * This is typically needed on Android, where access to USB devices
+ * is limited.
+ *
+ * For LIBUSB_API_VERSION 0x01000108 it was called LIBUSB_OPTION_WEAK_AUTHORITY
+ *
+ * Only valid on Linux.
*/
- LIBUSB_OPTION_WEAK_AUTHORITY = 2
+ LIBUSB_OPTION_NO_DEVICE_DISCOVERY = 2,
+
+#define LIBUSB_OPTION_WEAK_AUTHORITY LIBUSB_OPTION_NO_DEVICE_DISCOVERY
+
+ LIBUSB_OPTION_MAX = 3
};
int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...);
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
#if defined(__cplusplus)
}
#endif
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index 491114b..b1fc88c 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -83,6 +83,34 @@
#define PTR_ALIGN(v) \
(((v) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1))
+/* Atomic operations
+ *
+ * Useful for reference counting or when accessing a value without a lock
+ *
+ * The following atomic operations are defined:
+ * usbi_atomic_load() - Atomically read a variable's value
+ * usbi_atomic_store() - Atomically write a new value value to a variable
+ * usbi_atomic_inc() - Atomically increment a variable's value and return the new value
+ * usbi_atomic_dec() - Atomically decrement a variable's value and return the new value
+ *
+ * All of these operations are ordered with each other, thus the effects of
+ * any one operation is guaranteed to be seen by any other operation.
+ */
+#ifdef _MSC_VER
+typedef volatile LONG usbi_atomic_t;
+#define usbi_atomic_load(a) (*(a))
+#define usbi_atomic_store(a, v) (*(a)) = (v)
+#define usbi_atomic_inc(a) InterlockedIncrement((a))
+#define usbi_atomic_dec(a) InterlockedDecrement((a))
+#else
+#include <stdatomic.h>
+typedef atomic_long usbi_atomic_t;
+#define usbi_atomic_load(a) atomic_load((a))
+#define usbi_atomic_store(a, v) atomic_store((a), (v))
+#define usbi_atomic_inc(a) (atomic_fetch_add((a), 1) + 1)
+#define usbi_atomic_dec(a) (atomic_fetch_add((a), -1) - 1)
+#endif
+
/* Internal abstractions for event handling and thread synchronization */
#if defined(PLATFORM_POSIX)
#include "os/events_posix.h"
@@ -289,22 +317,23 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
#define usbi_err(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_ERROR, __VA_ARGS__)
#define usbi_warn(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__)
#define usbi_info(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__)
-#define usbi_dbg(...) _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
+#define usbi_dbg(ctx ,...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
#else /* ENABLE_LOGGING */
#define usbi_err(ctx, ...) UNUSED(ctx)
#define usbi_warn(ctx, ...) UNUSED(ctx)
#define usbi_info(ctx, ...) UNUSED(ctx)
-#define usbi_dbg(...) do {} while (0)
+#define usbi_dbg(ctx, ...) do {} while (0)
#endif /* ENABLE_LOGGING */
#define DEVICE_CTX(dev) ((dev)->ctx)
-#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev))
-#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle))
+#define HANDLE_CTX(handle) ((handle) ? DEVICE_CTX((handle)->dev) : NULL)
#define ITRANSFER_CTX(itransfer) \
- (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)))
+ ((itransfer)->dev ? DEVICE_CTX((itransfer)->dev) : NULL)
+#define TRANSFER_CTX(transfer) \
+ (ITRANSFER_CTX(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer)))
#define IS_EPIN(ep) (0 != ((ep) & LIBUSB_ENDPOINT_IN))
#define IS_EPOUT(ep) (!IS_EPIN(ep))
@@ -340,6 +369,9 @@ struct libusb_context {
libusb_hotplug_callback_handle next_hotplug_cb_handle;
usbi_mutex_t hotplug_cbs_lock;
+ /* A flag to indicate that the context is ready for hotplug notifications */
+ usbi_atomic_t hotplug_ready;
+
/* 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
@@ -404,13 +436,26 @@ struct libusb_context {
};
extern struct libusb_context *usbi_default_context;
+extern struct libusb_context *usbi_fallback_context;
extern struct list_head active_contexts_list;
extern usbi_mutex_static_t active_contexts_lock;
static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx)
{
- return ctx ? ctx : usbi_default_context;
+ static int warned = 0;
+
+ if (!ctx) {
+ ctx = usbi_default_context;
+ }
+ if (!ctx) {
+ ctx = usbi_fallback_context;
+ if (ctx && warned == 0) {
+ usbi_err(ctx, "API misuse! Using non-default context as implicit default.");
+ warned = 1;
+ }
+ }
+ return ctx;
}
enum usbi_event_flags {
@@ -450,10 +495,7 @@ static inline void usbi_end_event_handling(struct libusb_context *ctx)
}
struct libusb_device {
- /* lock protects refcnt, everything else is finalized at initialization
- * time */
- usbi_mutex_t lock;
- int refcnt;
+ usbi_atomic_t refcnt;
struct libusb_context *ctx;
struct libusb_device *parent_dev;
@@ -467,7 +509,7 @@ struct libusb_device {
unsigned long session_data;
struct libusb_device_descriptor device_descriptor;
- int attached;
+ usbi_atomic_t attached;
};
struct libusb_device_handle {
@@ -534,6 +576,10 @@ struct usbi_transfer {
uint32_t state_flags; /* Protected by usbi_transfer->lock */
uint32_t timeout_flags; /* Protected by the flying_stransfers_lock */
+ /* The device reference is held until destruction for logging
+ * even after dev_handle is set to NULL. */
+ struct libusb_device *dev;
+
/* this lock is held during libusb_submit_transfer() and
* libusb_cancel_transfer() (allowing the OS backend to prevent duplicate
* cancellation, submission-during-cancellation, etc). the OS backend
@@ -664,8 +710,75 @@ union usbi_bos_desc_buf {
uint16_t align; /* Force 2-byte alignment */
};
+enum usbi_hotplug_flags {
+ /* This callback is interested in device arrivals */
+ USBI_HOTPLUG_DEVICE_ARRIVED = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
+
+ /* This callback is interested in device removals */
+ USBI_HOTPLUG_DEVICE_LEFT = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
+
+ /* IMPORTANT: The values for the below entries must start *after*
+ * the highest value of the above entries!!!
+ */
+
+ /* The vendor_id field is valid for matching */
+ USBI_HOTPLUG_VENDOR_ID_VALID = (1U << 3),
+
+ /* The product_id field is valid for matching */
+ USBI_HOTPLUG_PRODUCT_ID_VALID = (1U << 4),
+
+ /* The dev_class field is valid for matching */
+ USBI_HOTPLUG_DEV_CLASS_VALID = (1U << 5),
+
+ /* This callback has been unregistered and needs to be freed */
+ USBI_HOTPLUG_NEEDS_FREE = (1U << 6),
+};
+
+struct usbi_hotplug_callback {
+ /* Flags that control how this callback behaves */
+ uint8_t flags;
+
+ /* Vendor ID to match (if flags says this is valid) */
+ uint16_t vendor_id;
+
+ /* Product ID to match (if flags says this is valid) */
+ uint16_t product_id;
+
+ /* Device class to match (if flags says this is valid) */
+ uint8_t dev_class;
+
+ /* 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;
+
+ /* List this callback is registered in (ctx->hotplug_cbs) */
+ struct list_head list;
+};
+
+struct usbi_hotplug_message {
+ /* The hotplug event that occurred */
+ libusb_hotplug_event event;
+
+ /* The device for which this hotplug event occurred */
+ struct libusb_device *device;
+
+ /* List this message is contained in (ctx->hotplug_msgs) */
+ struct list_head list;
+};
+
/* shared data and functions */
+void usbi_hotplug_init(struct libusb_context *ctx);
+void usbi_hotplug_exit(struct libusb_context *ctx);
+void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
+ libusb_hotplug_event event);
+void usbi_hotplug_process(struct libusb_context *ctx, struct list_head *hotplug_msgs);
+
int usbi_io_init(struct libusb_context *ctx);
void usbi_io_exit(struct libusb_context *ctx);
@@ -696,6 +809,13 @@ int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle
short poll_events);
void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle);
+struct usbi_option {
+ int is_set;
+ union {
+ int ival;
+ } arg;
+};
+
/* OS event abstraction */
int usbi_create_event(usbi_event_t *event);
@@ -793,7 +913,8 @@ struct usbi_os_backend {
* data structures for later, etc.
*
* This function is called when a libusb user initializes the library
- * prior to use.
+ * prior to use. Mutual exclusion with other init and exit calls is
+ * guaranteed when this function is called.
*
* Return 0 on success, or a LIBUSB_ERROR code on failure.
*/
@@ -803,6 +924,8 @@ struct usbi_os_backend {
* that was set up by init.
*
* This function is called when the user deinitializes the library.
+ * Mutual exclusion with other init and exit calls is guaranteed when
+ * this function is called.
*/
void (*exit)(struct libusb_context *ctx);
@@ -1365,6 +1488,12 @@ extern const struct usbi_os_backend usbi_backend;
#define for_each_removed_event_source_safe(ctx, e, n) \
for_each_safe_helper(e, n, &(ctx)->removed_event_sources, struct usbi_event_source)
+#define for_each_hotplug_cb(ctx, c) \
+ for_each_helper(c, &(ctx)->hotplug_cbs, struct usbi_hotplug_callback)
+
+#define for_each_hotplug_cb_safe(ctx, c, n) \
+ for_each_safe_helper(c, n, &(ctx)->hotplug_cbs, struct usbi_hotplug_callback)
+
#ifdef __cplusplus
}
#endif
diff --git a/libusb/os/darwin_usb.c b/libusb/os/darwin_usb.c
index e415589..388dbca 100644
--- a/libusb/os/darwin_usb.c
+++ b/libusb/os/darwin_usb.c
@@ -1,8 +1,8 @@
/* -*- Mode: C; indent-tabs-mode:nil -*- */
/*
* darwin backend for libusb 1.0
- * Copyright © 2008-2020 Nathan Hjelm <hjelmn@cs.unm.edu>
- * Copyright © 2019-2020 Google LLC. All rights reserved.
+ * Copyright © 2008-2021 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2019-2021 Google LLC. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -41,6 +41,10 @@
* function. Its use is also conditionalized to only older deployment targets. */
#define OBJC_SILENCE_GC_DEPRECATIONS 1
+/* Default timeout to 10s for reenumerate. This is needed because USBDeviceReEnumerate
+ * does not return error status on macOS. */
+#define DARWIN_REENUMERATE_TIMEOUT_US (10 * USEC_PER_SEC)
+
#include <AvailabilityMacros.h>
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200
#include <objc/objc-auto.h>
@@ -48,9 +52,11 @@
#include "darwin_usb.h"
-static pthread_mutex_t libusb_darwin_init_mutex = PTHREAD_MUTEX_INITIALIZER;
static int init_count = 0;
+/* Both kIOMasterPortDefault or kIOMainPortDefault are synonyms for 0. */
+static const mach_port_t darwin_default_master_port = 0;
+
/* async event thread */
static pthread_mutex_t libusb_darwin_at_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER;
@@ -77,14 +83,17 @@ static pthread_t libusb_darwin_at;
static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len);
static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface);
static int darwin_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface);
+static int darwin_reenumerate_device(struct libusb_device_handle *dev_handle, bool capture);
+static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint);
static int darwin_reset_device(struct libusb_device_handle *dev_handle);
+static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface);
static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0);
static enum libusb_error darwin_scan_devices(struct libusb_context *ctx);
static enum libusb_error process_new_device (struct libusb_context *ctx, struct darwin_cached_device *cached_device,
UInt64 old_session_id);
-static enum libusb_error darwin_get_cached_device(io_service_t service, struct darwin_cached_device **cached_out,
+static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io_service_t service, struct darwin_cached_device **cached_out,
UInt64 *old_session_id);
#if defined(ENABLE_LOGGING)
@@ -102,6 +111,9 @@ static const char *darwin_error_str (IOReturn result) {
case kIOReturnExclusiveAccess:
return "another process has device opened for exclusive access";
case kIOUSBPipeStalled:
+#if defined(kUSBHostReturnPipeStalled)
+ case kUSBHostReturnPipeStalled:
+#endif
return "pipe is stalled";
case kIOReturnError:
return "could not establish a connection to the Darwin kernel";
@@ -141,16 +153,20 @@ static enum libusb_error darwin_to_libusb (IOReturn result) {
case kIOReturnExclusiveAccess:
return LIBUSB_ERROR_ACCESS;
case kIOUSBPipeStalled:
+#if defined(kUSBHostReturnPipeStalled)
+ case kUSBHostReturnPipeStalled:
+#endif
return LIBUSB_ERROR_PIPE;
case kIOReturnBadArgument:
return LIBUSB_ERROR_INVALID_PARAM;
case kIOUSBTransactionTimeout:
return LIBUSB_ERROR_TIMEOUT;
+ case kIOUSBUnknownPipeErr:
+ return LIBUSB_ERROR_NOT_FOUND;
case kIOReturnNotResponding:
case kIOReturnAborted:
case kIOReturnError:
case kIOUSBNoAsyncPortErr:
- case kIOUSBUnknownPipeErr:
default:
return LIBUSB_ERROR_OTHER;
}
@@ -167,6 +183,7 @@ static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev)
(*(cached_dev->device))->Release(cached_dev->device);
cached_dev->device = NULL;
}
+ IOObjectRelease (cached_dev->service);
free (cached_dev);
}
}
@@ -183,7 +200,9 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui
uint8_t i, iface;
- usbi_dbg ("converting ep address 0x%02x to pipeRef and interface", ep);
+ struct libusb_context *ctx = HANDLE_CTX(dev_handle);
+
+ usbi_dbg (ctx, "converting ep address 0x%02x to pipeRef and interface", ep);
for (iface = 0 ; iface < USB_MAXINTERFACES ; iface++) {
cInterface = &priv->interfaces[iface];
@@ -199,7 +218,7 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui
if (interface_out)
*interface_out = cInterface;
- usbi_dbg ("pipe %d on interface %d matches", *pipep, iface);
+ usbi_dbg (ctx, "pipe %d on interface %d matches", *pipep, iface);
return LIBUSB_SUCCESS;
}
}
@@ -240,7 +259,7 @@ static IOReturn usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32
CFRelease (locationCF);
}
- return IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, deviceIterator);
+ return IOServiceGetMatchingServices(darwin_default_master_port, matchingDict, deviceIterator);
}
/* Returns 1 on success, 0 on failure. */
@@ -281,7 +300,7 @@ static bool get_ioregistry_value_data (io_service_t service, CFStringRef propert
return success;
}
-static usb_device_t **darwin_device_from_service (io_service_t service)
+static usb_device_t **darwin_device_from_service (struct libusb_context *ctx, io_service_t service)
{
io_cf_plugin_ref_t *plugInInterface = NULL;
usb_device_t **device;
@@ -300,14 +319,14 @@ static usb_device_t **darwin_device_from_service (io_service_t service)
break;
}
- usbi_dbg ("set up plugin for service retry: %s", darwin_error_str (kresult));
+ usbi_dbg (ctx, "set up plugin for service retry: %s", darwin_error_str (kresult));
/* sleep for a little while before trying again */
nanosleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 1000}, NULL);
}
if (kIOReturnSuccess != kresult || !plugInInterface) {
- usbi_dbg ("could not set up plugin for service: %s", darwin_error_str (kresult));
+ usbi_dbg (ctx, "could not set up plugin for service: %s", darwin_error_str (kresult));
return NULL;
}
@@ -330,7 +349,7 @@ static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
usbi_mutex_lock(&active_contexts_lock);
while ((service = IOIteratorNext(add_devices))) {
- ret = darwin_get_cached_device (service, &cached_device, &old_session_id);
+ ret = darwin_get_cached_device (NULL, service, &cached_device, &old_session_id);
if (ret < 0 || !cached_device->can_enumerate) {
continue;
}
@@ -341,7 +360,7 @@ static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
}
if (cached_device->in_reenumerate) {
- usbi_dbg ("cached device in reset state. reset complete...");
+ usbi_dbg (NULL, "cached device in reset state. reset complete...");
cached_device->in_reenumerate = false;
}
@@ -358,7 +377,7 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
struct darwin_cached_device *old_device;
io_service_t device;
- UInt64 session;
+ UInt64 session, locationID;
int ret;
usbi_mutex_lock(&active_contexts_lock);
@@ -368,6 +387,7 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
/* get the location from the i/o registry */
ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session);
+ (void) get_ioregistry_value_number (device, CFSTR("locationID"), kCFNumberSInt32Type, &locationID);
IOObjectRelease (device);
if (!ret)
continue;
@@ -380,7 +400,8 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
if (old_device->in_reenumerate) {
/* device is re-enumerating. do not dereference the device at this time. libusb_reset_device()
* will deref if needed. */
- usbi_dbg ("detected device detached due to re-enumeration");
+ usbi_dbg (NULL, "detected device detached due to re-enumeration. sessionID: 0x%" PRIx64 ", locationID: 0x%" PRIx64,
+ session, locationID);
/* the device object is no longer usable so go ahead and release it */
if (old_device->device) {
@@ -403,7 +424,7 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
}
for_each_context(ctx) {
- usbi_dbg ("notifying context %p of device disconnect", ctx);
+ usbi_dbg (ctx, "notifying context %p of device disconnect", ctx);
dev = usbi_get_device_by_session_id(ctx, (unsigned long) session);
if (dev) {
@@ -426,7 +447,7 @@ static void darwin_hotplug_poll (void)
/* since a kernel thread may notify the IOIterators used for
* hotplug notification we can't just clear the iterators.
* instead just wait until all IOService providers are quiet */
- (void) IOKitWaitQuiet (kIOMasterPortDefault, &timeout);
+ (void) IOKitWaitQuiet (darwin_default_master_port, &timeout);
}
static void darwin_clear_iterator (io_iterator_t iter) {
@@ -473,7 +494,8 @@ static void *darwin_event_thread_main (void *arg0) {
io_iterator_t libusb_rem_device_iterator;
io_iterator_t libusb_add_device_iterator;
- usbi_dbg ("creating hotplug event source");
+ /* ctx must only be used for logging during thread startup */
+ usbi_dbg (ctx, "creating hotplug event source");
runloop = CFRunLoopGetCurrent ();
CFRetain (runloop);
@@ -486,7 +508,7 @@ static void *darwin_event_thread_main (void *arg0) {
CFRunLoopAddSource(runloop, libusb_shutdown_cfsource, kCFRunLoopDefaultMode);
/* add the notification port to the run loop */
- libusb_notification_port = IONotificationPortCreate (kIOMasterPortDefault);
+ libusb_notification_port = IONotificationPortCreate (darwin_default_master_port);
libusb_notification_cfsource = IONotificationPortGetRunLoopSource (libusb_notification_port);
CFRunLoopAddSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode);
@@ -494,7 +516,7 @@ static void *darwin_event_thread_main (void *arg0) {
kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification,
IOServiceMatching(darwin_device_class),
darwin_devices_detached,
- ctx, &libusb_rem_device_iterator);
+ NULL, &libusb_rem_device_iterator);
if (kresult != kIOReturnSuccess) {
usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
@@ -507,7 +529,7 @@ static void *darwin_event_thread_main (void *arg0) {
kresult = IOServiceAddMatchingNotification(libusb_notification_port, kIOFirstMatchNotification,
IOServiceMatching(darwin_device_class),
darwin_devices_attached,
- ctx, &libusb_add_device_iterator);
+ NULL, &libusb_add_device_iterator);
if (kresult != kIOReturnSuccess) {
usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
@@ -520,7 +542,7 @@ static void *darwin_event_thread_main (void *arg0) {
darwin_clear_iterator (libusb_rem_device_iterator);
darwin_clear_iterator (libusb_add_device_iterator);
- usbi_dbg ("darwin event thread ready to receive events");
+ usbi_dbg (ctx, "darwin event thread ready to receive events");
/* signal the main thread that the hotplug runloop has been created. */
pthread_mutex_lock (&libusb_darwin_at_mutex);
@@ -532,7 +554,7 @@ static void *darwin_event_thread_main (void *arg0) {
/* run the runloop */
CFRunLoopRun();
- usbi_dbg ("darwin event thread exiting");
+ usbi_dbg (NULL, "darwin event thread exiting");
/* signal the main thread that the hotplug runloop has finished. */
pthread_mutex_lock (&libusb_darwin_at_mutex);
@@ -567,23 +589,20 @@ static void darwin_cleanup_devices(void) {
list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) {
darwin_deref_cached_device(dev);
}
-
- darwin_cached_devices.prev = darwin_cached_devices.next = NULL;
}
static int darwin_init(struct libusb_context *ctx) {
bool first_init;
int rc;
- pthread_mutex_lock (&libusb_darwin_init_mutex);
-
first_init = (1 == ++init_count);
do {
if (first_init) {
- assert (NULL == darwin_cached_devices.next);
- list_init (&darwin_cached_devices);
-
+ if (NULL == darwin_cached_devices.next) {
+ list_init (&darwin_cached_devices);
+ }
+ assert(list_empty(&darwin_cached_devices));
#if !defined(HAVE_CLOCK_GETTIME)
/* create the clocks that will be used if clock_gettime() is not available */
host_name_port_t host_self;
@@ -632,16 +651,12 @@ static int darwin_init(struct libusb_context *ctx) {
--init_count;
}
- pthread_mutex_unlock (&libusb_darwin_init_mutex);
-
return rc;
}
static void darwin_exit (struct libusb_context *ctx) {
UNUSED(ctx);
- pthread_mutex_lock (&libusb_darwin_init_mutex);
-
if (0 == --init_count) {
/* stop the event runloop and wait for the thread to terminate. */
pthread_mutex_lock (&libusb_darwin_at_mutex);
@@ -659,8 +674,6 @@ static void darwin_exit (struct libusb_context *ctx) {
mach_port_deallocate(mach_task_self(), clock_monotonic);
#endif
}
-
- pthread_mutex_unlock (&libusb_darwin_init_mutex);
}
static int get_configuration_index (struct libusb_device *dev, UInt8 config_value) {
@@ -744,7 +757,7 @@ static enum libusb_error darwin_check_configuration (struct libusb_context *ctx,
not usable anyway */
if (0x05ac == libusb_le16_to_cpu (dev->dev_descriptor.idVendor) &&
0x8005 == libusb_le16_to_cpu (dev->dev_descriptor.idProduct)) {
- usbi_dbg ("ignoring configuration on root hub simulation");
+ usbi_dbg (ctx, "ignoring configuration on root hub simulation");
dev->active_config = 0;
return LIBUSB_SUCCESS;
}
@@ -787,7 +800,7 @@ static enum libusb_error darwin_check_configuration (struct libusb_context *ctx,
/* not configured */
dev->active_config = 0;
- usbi_dbg ("active config: %u, first config: %u", dev->active_config, dev->first_config);
+ usbi_dbg (ctx, "active config: %u, first config: %u", dev->active_config, dev->first_config);
return LIBUSB_SUCCESS;
}
@@ -812,7 +825,7 @@ static IOReturn darwin_request_descriptor (usb_device_t **device, UInt8 desc, UI
return (*device)->DeviceRequestTO (device, &req);
}
-static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_device *dev) {
+static enum libusb_error darwin_cache_device_descriptor (struct libusb_context *ctx, struct darwin_cached_device *dev) {
usb_device_t **device = dev->device;
int retries = 1;
long delay = 30000; // microseconds
@@ -850,7 +863,7 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de
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(ctx, "descriptor appears to be invalid. resetting configuration before trying again...");
/* set the first configuration */
(*device)->SetConfiguration(device, 1);
@@ -881,7 +894,7 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de
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));
+ usbi_dbg(ctx, "could not retrieve device descriptor. failed to unsuspend: %s",darwin_error_str(ret2));
} else
unsuspended = 1;
@@ -890,7 +903,7 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de
}
if (kIOReturnSuccess != ret) {
- usbi_dbg("kernel responded with code: 0x%08x. sleeping for %ld ms before trying again", ret, delay/1000);
+ usbi_dbg(ctx, "kernel responded with code: 0x%08x. sleeping for %ld ms before trying again", ret, delay/1000);
/* sleep for a little while before trying again */
nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000}, NULL);
}
@@ -906,10 +919,10 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de
if (ret != kIOReturnSuccess) {
/* a debug message was already printed out for this error */
if (LIBUSB_CLASS_HUB == bDeviceClass)
- usbi_dbg ("could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
+ usbi_dbg (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
idVendor, idProduct, darwin_error_str (ret), ret);
else
- usbi_warn (NULL, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
+ usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
idVendor, idProduct, darwin_error_str (ret), ret);
return darwin_to_libusb (ret);
}
@@ -922,20 +935,20 @@ static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_de
return LIBUSB_ERROR_NO_DEVICE;
}
- usbi_dbg ("cached device descriptor:");
- usbi_dbg (" bDescriptorType: 0x%02x", dev->dev_descriptor.bDescriptorType);
- usbi_dbg (" bcdUSB: 0x%04x", libusb_le16_to_cpu (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", libusb_le16_to_cpu (dev->dev_descriptor.idVendor));
- usbi_dbg (" idProduct: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idProduct));
- usbi_dbg (" bcdDevice: 0x%04x", libusb_le16_to_cpu (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);
+ usbi_dbg (ctx, "cached device descriptor:");
+ usbi_dbg (ctx, " bDescriptorType: 0x%02x", dev->dev_descriptor.bDescriptorType);
+ usbi_dbg (ctx, " bcdUSB: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdUSB));
+ usbi_dbg (ctx, " bDeviceClass: 0x%02x", dev->dev_descriptor.bDeviceClass);
+ usbi_dbg (ctx, " bDeviceSubClass: 0x%02x", dev->dev_descriptor.bDeviceSubClass);
+ usbi_dbg (ctx, " bDeviceProtocol: 0x%02x", dev->dev_descriptor.bDeviceProtocol);
+ usbi_dbg (ctx, " bMaxPacketSize0: 0x%02x", dev->dev_descriptor.bMaxPacketSize0);
+ usbi_dbg (ctx, " idVendor: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idVendor));
+ usbi_dbg (ctx, " idProduct: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idProduct));
+ usbi_dbg (ctx, " bcdDevice: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdDevice));
+ usbi_dbg (ctx, " iManufacturer: 0x%02x", dev->dev_descriptor.iManufacturer);
+ usbi_dbg (ctx, " iProduct: 0x%02x", dev->dev_descriptor.iProduct);
+ usbi_dbg (ctx, " iSerialNumber: 0x%02x", dev->dev_descriptor.iSerialNumber);
+ usbi_dbg (ctx, " bNumConfigurations: 0x%02x", dev->dev_descriptor.bNumConfigurations);
dev->can_enumerate = 1;
@@ -979,7 +992,7 @@ static bool get_device_parent_sessionID(io_service_t service, UInt64 *parent_ses
return false;
}
-static enum libusb_error darwin_get_cached_device(io_service_t service, struct darwin_cached_device **cached_out,
+static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io_service_t service, struct darwin_cached_device **cached_out,
UInt64 *old_session_id) {
struct darwin_cached_device *new_device;
UInt64 sessionID = 0, parent_sessionID = 0;
@@ -996,28 +1009,28 @@ static enum libusb_error darwin_get_cached_device(io_service_t service, struct d
(void) get_ioregistry_value_number (service, CFSTR("sessionID"), kCFNumberSInt64Type, &sessionID);
(void) get_ioregistry_value_number (service, CFSTR("locationID"), kCFNumberSInt32Type, &locationID);
if (!get_device_port (service, &port)) {
- usbi_dbg("could not get connected port number");
+ usbi_dbg(ctx, "could not get connected port number");
}
- usbi_dbg("finding cached device for sessionID 0x%" PRIx64, sessionID);
+ usbi_dbg(ctx, "finding cached device for sessionID 0x%" PRIx64, sessionID);
if (get_device_parent_sessionID(service, &parent_sessionID)) {
- usbi_dbg("parent sessionID: 0x%" PRIx64, parent_sessionID);
+ usbi_dbg(ctx, "parent sessionID: 0x%" PRIx64, parent_sessionID);
}
usbi_mutex_lock(&darwin_cached_devices_lock);
do {
list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) {
- usbi_dbg("matching sessionID/locationID 0x%" PRIx64 "/0x%x against cached device with sessionID/locationID 0x%" PRIx64 "/0x%x",
+ usbi_dbg(ctx, "matching sessionID/locationID 0x%" PRIx64 "/0x%x against cached device with sessionID/locationID 0x%" PRIx64 "/0x%x",
sessionID, locationID, new_device->session, new_device->location);
if (new_device->location == locationID && new_device->in_reenumerate) {
- usbi_dbg ("found cached device with matching location that is being re-enumerated");
+ usbi_dbg (ctx, "found cached device with matching location that is being re-enumerated");
*old_session_id = new_device->session;
break;
}
if (new_device->session == sessionID) {
- usbi_dbg("using cached device for device");
+ usbi_dbg(ctx, "using cached device for device");
*cached_out = new_device;
break;
}
@@ -1026,9 +1039,9 @@ static enum libusb_error darwin_get_cached_device(io_service_t service, struct d
if (*cached_out)
break;
- usbi_dbg("caching new device with sessionID 0x%" PRIx64, sessionID);
+ usbi_dbg(ctx, "caching new device with sessionID 0x%" PRIx64, sessionID);
- device = darwin_device_from_service (service);
+ device = darwin_device_from_service (ctx, service);
if (!device) {
ret = LIBUSB_ERROR_NO_DEVICE;
break;
@@ -1052,6 +1065,9 @@ static enum libusb_error darwin_get_cached_device(io_service_t service, struct d
(*device)->GetLocationID (device, &new_device->location);
new_device->port = port;
new_device->parent_session = parent_sessionID;
+ } else {
+ /* release the ref to old device's service */
+ IOObjectRelease (new_device->service);
}
/* keep track of devices regardless of if we successfully enumerate them to
@@ -1060,9 +1076,13 @@ static enum libusb_error darwin_get_cached_device(io_service_t service, struct d
new_device->session = sessionID;
new_device->device = device;
+ new_device->service = service;
+
+ /* retain the service */
+ IOObjectRetain (service);
/* cache the device descriptor */
- ret = darwin_cache_device_descriptor(new_device);
+ ret = darwin_cache_device_descriptor(ctx, new_device);
if (ret)
break;
@@ -1094,14 +1114,14 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct
break;
if (0 != old_session_id) {
- usbi_dbg ("re-using existing device from context %p for with session 0x%" PRIx64 " new session 0x%" PRIx64,
+ usbi_dbg (ctx, "re-using existing device from context %p for with session 0x%" PRIx64 " new session 0x%" PRIx64,
ctx, old_session_id, cached_device->session);
/* save the libusb device before the session id is updated */
dev = usbi_get_device_by_session_id (ctx, (unsigned long) old_session_id);
}
if (!dev) {
- usbi_dbg ("allocating new device in context %p for with session 0x%" PRIx64,
+ usbi_dbg (ctx, "allocating new device in context %p for with session 0x%" PRIx64,
ctx, cached_device->session);
dev = usbi_alloc_device(ctx, (unsigned long) cached_device->session);
@@ -1114,6 +1134,8 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct
priv->dev = cached_device;
darwin_ref_cached_device (priv->dev);
dev->port_number = cached_device->port;
+ /* the location ID encodes the path to the device. the top byte of the location ID contains the bus number
+ (numbered from 0). the remaining bytes can be used to construct the device tree for that bus. */
dev->bus_number = cached_device->location >> 24;
assert(cached_device->address <= UINT8_MAX);
dev->device_address = (uint8_t)cached_device->address;
@@ -1127,10 +1149,13 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct
usbi_localize_device_descriptor(&dev->device_descriptor);
dev->session_data = cached_device->session;
+ if (NULL != dev->parent_dev) {
+ libusb_unref_device(dev->parent_dev);
+ dev->parent_dev = NULL;
+ }
+
if (cached_device->parent_session > 0) {
dev->parent_dev = usbi_get_device_by_session_id (ctx, (unsigned long) cached_device->parent_session);
- } else {
- dev->parent_dev = NULL;
}
(*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed);
@@ -1139,7 +1164,7 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct
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 MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
case kUSBDeviceSpeedSuper: dev->speed = LIBUSB_SPEED_SUPER; break;
#endif
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
@@ -1153,7 +1178,7 @@ static enum libusb_error process_new_device (struct libusb_context *ctx, struct
if (ret < 0)
break;
- usbi_dbg ("found device with address %d port = %d parent = %p at %p", dev->device_address,
+ usbi_dbg (ctx, "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);
@@ -1180,7 +1205,7 @@ static enum libusb_error darwin_scan_devices(struct libusb_context *ctx) {
return darwin_to_libusb (kresult);
while ((service = IOIteratorNext (deviceIterator))) {
- ret = darwin_get_cached_device (service, &cached_device, &old_session_id);
+ ret = darwin_get_cached_device (ctx, service, &cached_device, &old_session_id);
if (ret < 0 || !cached_device->can_enumerate) {
continue;
}
@@ -1232,14 +1257,14 @@ static int darwin_open (struct libusb_device_handle *dev_handle) {
CFRetain (libusb_darwin_acfl);
- /* add the cfSource to the aync run loop */
+ /* add the cfSource to the async run loop */
CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes);
}
/* device opened successfully */
dpriv->open_count++;
- usbi_dbg ("device open for access");
+ usbi_dbg (HANDLE_CTX(dev_handle), "device open for access");
return 0;
}
@@ -1257,6 +1282,10 @@ static void darwin_close (struct libusb_device_handle *dev_handle) {
}
dpriv->open_count--;
+ if (NULL == dpriv->device) {
+ usbi_warn (HANDLE_CTX (dev_handle), "darwin_close device missing IOService");
+ return;
+ }
/* make sure all interfaces are released */
for (i = 0 ; i < USB_MAXINTERFACES ; i++)
@@ -1362,28 +1391,39 @@ static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle,
/* current interface */
struct darwin_interface *cInterface = &priv->interfaces[iface];
+#if InterfaceVersion >= 550
+ IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3};
+#else
+ UInt8 dont_care1, dont_care3;
+ UInt16 dont_care2;
+#endif
IOReturn kresult;
UInt8 numep, direction, number;
- UInt8 dont_care1, dont_care3;
- UInt16 dont_care2;
int rc;
+ struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
- usbi_dbg ("building table of endpoints.");
+ usbi_dbg (ctx, "building table of endpoints.");
/* retrieve the total number of endpoints on this interface */
kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep);
if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "can't get number of endpoints for interface: %s", darwin_error_str(kresult));
+ usbi_err (ctx, "can't get number of endpoints for interface: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
/* iterate through pipe references */
for (UInt8 i = 1 ; i <= numep ; i++) {
+#if InterfaceVersion >= 550
+ kresult = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, i, &pipeProperties);
+ number = pipeProperties.bEndpointNumber;
+ direction = pipeProperties.bDirection;
+#else
kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1,
&dont_care2, &dont_care3);
-
+#endif
if (kresult != kIOReturnSuccess) {
/* probably a buggy device. try to get the endpoint address from the descriptors */
struct libusb_config_descriptor *config;
@@ -1401,6 +1441,10 @@ static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle,
return rc;
}
+ if (iface >= config->bNumInterfaces) {
+ usbi_err (HANDLE_CTX (dev_handle), "interface %d out of range for device", iface);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
endpoint_desc = config->interface[iface].altsetting[alt_setting].endpoint + i - 1;
cInterface->endpoint_addrs[i - 1] = endpoint_desc->bEndpointAddress;
@@ -1408,7 +1452,7 @@ static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle,
cInterface->endpoint_addrs[i - 1] = (UInt8)(((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK));
}
- usbi_dbg ("interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift,
+ usbi_dbg (ctx, "interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift,
cInterface->endpoint_addrs[i - 1] & LIBUSB_ENDPOINT_ADDRESS_MASK);
}
@@ -1429,30 +1473,32 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8
/* current interface */
struct darwin_interface *cInterface = &priv->interfaces[iface];
+ struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
if (kresult != kIOReturnSuccess)
return darwin_to_libusb (kresult);
/* make sure we have an interface */
if (!usbInterface && dpriv->first_config != 0) {
- usbi_info (HANDLE_CTX (dev_handle), "no interface found; setting configuration: %d", dpriv->first_config);
+ usbi_info (ctx, "no interface found; setting configuration: %d", dpriv->first_config);
/* set the configuration */
ret = darwin_set_configuration (dev_handle, (int) dpriv->first_config);
if (ret != LIBUSB_SUCCESS) {
- usbi_err (HANDLE_CTX (dev_handle), "could not set configuration");
+ usbi_err (ctx, "could not set configuration");
return ret;
}
kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
+ usbi_err (ctx, "darwin_get_interface: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
}
if (!usbInterface) {
- usbi_err (HANDLE_CTX (dev_handle), "interface not found");
+ usbi_info (ctx, "interface not found");
return LIBUSB_ERROR_NOT_FOUND;
}
@@ -1464,12 +1510,12 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8
(void)IOObjectRelease (usbInterface);
if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult));
+ usbi_err (ctx, "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
if (!plugInInterface) {
- usbi_err (HANDLE_CTX (dev_handle), "plugin interface not found");
+ usbi_err (ctx, "plugin interface not found");
return LIBUSB_ERROR_NOT_FOUND;
}
@@ -1481,14 +1527,14 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8
/* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */
(*plugInInterface)->Release (plugInInterface);
if (kresult != kIOReturnSuccess || !cInterface->interface) {
- usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult));
+ usbi_err (ctx, "QueryInterface: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
/* claim the interface */
kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface);
if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceOpen: %s", darwin_error_str(kresult));
+ usbi_info (ctx, "USBInterfaceOpen: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
@@ -1497,7 +1543,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8
if (ret) {
/* this should not happen */
darwin_release_interface (dev_handle, iface);
- usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
+ usbi_err (ctx, "could not build endpoint table");
return ret;
}
@@ -1506,7 +1552,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8
/* create async event source */
kresult = (*(cInterface->interface))->CreateInterfaceAsyncEventSource (cInterface->interface, &cInterface->cfSource);
if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "could not create async event source");
+ usbi_err (ctx, "could not create async event source");
/* can't continue without an async event source */
(void)darwin_release_interface (dev_handle, iface);
@@ -1517,7 +1563,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8
/* add the cfSource to the async thread's run loop */
CFRunLoopAddSource(libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
- usbi_dbg ("interface opened");
+ usbi_dbg (ctx, "interface opened");
return LIBUSB_SUCCESS;
}
@@ -1540,6 +1586,7 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, uin
if (cInterface->cfSource) {
CFRunLoopRemoveSource (libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
CFRelease (cInterface->cfSource);
+ cInterface->cfSource = NULL;
}
kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface);
@@ -1555,6 +1602,30 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, uin
return darwin_to_libusb (kresult);
}
+static int check_alt_setting_and_clear_halt(struct libusb_device_handle *dev_handle, uint8_t altsetting, struct darwin_interface *cInterface) {
+ enum libusb_error ret;
+ IOReturn kresult;
+ uint8_t current_alt_setting;
+
+ kresult = (*(cInterface->interface))->GetAlternateSetting (cInterface->interface, &current_alt_setting);
+ if (kresult == kIOReturnSuccess && altsetting != current_alt_setting) {
+ return LIBUSB_ERROR_PIPE;
+ }
+
+ for (int i = 0 ; i < cInterface->num_endpoints ; i++) {
+ ret = darwin_clear_halt(dev_handle, cInterface->endpoint_addrs[i]);
+ if (LIBUSB_SUCCESS != ret) {
+ usbi_warn(HANDLE_CTX (dev_handle), "error clearing pipe halt for endpoint %d", i);
+ if (LIBUSB_ERROR_NOT_FOUND == ret) {
+ /* may need to re-open the interface */
+ return ret;
+ }
+ }
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting) {
struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
IOReturn kresult;
@@ -1567,19 +1638,41 @@ static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_hand
return LIBUSB_ERROR_NO_DEVICE;
kresult = (*(cInterface->interface))->SetAlternateInterface (cInterface->interface, altsetting);
- if (kresult != kIOReturnSuccess)
- darwin_reset_device (dev_handle);
+ if (kresult == kIOReturnSuccess) {
+ /* update the list of endpoints */
+ ret = get_endpoints (dev_handle, iface);
+ if (ret) {
+ /* this should not happen */
+ darwin_release_interface (dev_handle, iface);
+ usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
+ }
+ return ret;
+ }
- /* update list of endpoints */
- ret = get_endpoints (dev_handle, iface);
- if (ret) {
- /* this should not happen */
- darwin_release_interface (dev_handle, iface);
- usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
+ usbi_warn (HANDLE_CTX (dev_handle), "SetAlternateInterface: %s", darwin_error_str(kresult));
+
+ ret = darwin_to_libusb(kresult);
+ if (ret != LIBUSB_ERROR_PIPE) {
return ret;
}
- return darwin_to_libusb (kresult);
+ /* If a device only supports a default setting for the specified interface, then a STALL
+ (kIOUSBPipeStalled) may be returned. Ref: USB 2.0 specs 9.4.10.
+ Mimic the behaviour in e.g. the Linux kernel: in such case, reset all endpoints
+ of the interface (as would have been done per 9.1.1.5) and return success. */
+
+ ret = check_alt_setting_and_clear_halt(dev_handle, altsetting, cInterface);
+ if (LIBUSB_ERROR_NOT_FOUND == ret) {
+ /* For some reason we need to reclaim the interface after the pipe error with some versions of macOS */
+ ret = darwin_claim_interface (dev_handle, iface);
+ if (LIBUSB_SUCCESS != ret) {
+ darwin_release_interface (dev_handle, iface);
+ usbi_err (HANDLE_CTX (dev_handle), "could not reclaim interface: %s", darwin_error_str(kresult));
+ }
+ ret = check_alt_setting_and_clear_halt(dev_handle, altsetting, cInterface);
+ }
+
+ return ret;
}
static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) {
@@ -1610,6 +1703,8 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t
int open_count = dpriv->open_count;
int ret;
+ struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
/* clear claimed interfaces temporarily */
dev_handle->claimed_interfaces = 0;
@@ -1629,16 +1724,16 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t
}
if (dpriv->active_config != active_config) {
- usbi_dbg ("darwin/restore_state: restoring configuration %d...", active_config);
+ usbi_dbg (ctx, "darwin/restore_state: restoring configuration %d...", active_config);
ret = darwin_set_configuration (dev_handle, active_config);
if (LIBUSB_SUCCESS != ret) {
- usbi_dbg ("darwin/restore_state: could not restore configuration");
+ usbi_dbg (ctx, "darwin/restore_state: could not restore configuration");
return LIBUSB_ERROR_NOT_FOUND;
}
}
- usbi_dbg ("darwin/restore_state: reclaiming interfaces");
+ usbi_dbg (ctx, "darwin/restore_state: reclaiming interfaces");
if (claimed_interfaces) {
for (uint8_t iface = 0 ; iface < USB_MAXINTERFACES ; ++iface) {
@@ -1646,11 +1741,11 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t
continue;
}
- usbi_dbg ("darwin/restore_state: re-claiming interface %u", iface);
+ usbi_dbg (ctx, "darwin/restore_state: re-claiming interface %u", iface);
ret = darwin_claim_interface (dev_handle, iface);
if (LIBUSB_SUCCESS != ret) {
- usbi_dbg ("darwin/restore_state: could not claim interface %u", iface);
+ usbi_dbg (ctx, "darwin/restore_state: could not claim interface %u", iface);
return LIBUSB_ERROR_NOT_FOUND;
}
@@ -1658,21 +1753,24 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t
}
}
- usbi_dbg ("darwin/restore_state: device state restored");
+ usbi_dbg (ctx, "darwin/restore_state: device state restored");
return LIBUSB_SUCCESS;
}
-static int darwin_reset_device(struct libusb_device_handle *dev_handle) {
+static int darwin_reenumerate_device (struct libusb_device_handle *dev_handle, bool capture) {
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
unsigned long claimed_interfaces = dev_handle->claimed_interfaces;
int8_t active_config = dpriv->active_config;
+ UInt32 options = 0;
IOUSBDeviceDescriptor descriptor;
IOUSBConfigurationDescriptorPtr cached_configuration;
IOUSBConfigurationDescriptor *cached_configurations;
IOReturn kresult;
UInt8 i;
+ struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
if (dpriv->in_reenumerate) {
/* ack, two (or more) threads are trying to reset the device! abort! */
return LIBUSB_ERROR_NOT_FOUND;
@@ -1689,62 +1787,152 @@ static int darwin_reset_device(struct libusb_device_handle *dev_handle) {
memcpy (cached_configurations + i, cached_configuration, sizeof (cached_configurations[i]));
}
+ /* if we need to release capture */
+ if (HAS_CAPTURE_DEVICE()) {
+ if (capture) {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
+ options |= kUSBReEnumerateCaptureDeviceMask;
+#endif
+ }
+ } else {
+ capture = false;
+ }
+
/* from macOS 10.11 ResetDevice no longer does anything so just use USBDeviceReEnumerate */
- kresult = (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0);
+ kresult = (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, options);
if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "USBDeviceReEnumerate: %s", darwin_error_str (kresult));
+ usbi_err (ctx, "USBDeviceReEnumerate: %s", darwin_error_str (kresult));
dpriv->in_reenumerate = false;
return darwin_to_libusb (kresult);
}
- usbi_dbg ("darwin/reset_device: waiting for re-enumeration to complete...");
+ /* capture mode does not re-enumerate but it does require re-open */
+ if (capture) {
+ usbi_dbg (ctx, "darwin/reenumerate_device: restoring state...");
+ dpriv->in_reenumerate = false;
+ return darwin_restore_state (dev_handle, active_config, claimed_interfaces);
+ }
+
+ usbi_dbg (ctx, "darwin/reenumerate_device: waiting for re-enumeration to complete...");
+
+ struct timespec start;
+ usbi_get_monotonic_time(&start);
while (dpriv->in_reenumerate) {
struct timespec delay = {.tv_sec = 0, .tv_nsec = 1000};
nanosleep (&delay, NULL);
+
+ struct timespec now;
+ usbi_get_monotonic_time(&now);
+ unsigned long elapsed_us = (now.tv_sec - start.tv_sec) * USEC_PER_SEC +
+ (now.tv_nsec - start.tv_nsec) / 1000;
+
+ if (elapsed_us >= DARWIN_REENUMERATE_TIMEOUT_US) {
+ usbi_err (ctx, "darwin/reenumerate_device: timeout waiting for reenumerate");
+ dpriv->in_reenumerate = false;
+ return LIBUSB_ERROR_TIMEOUT;
+ }
}
/* compare descriptors */
- usbi_dbg ("darwin/reset_device: checking whether descriptors changed");
+ usbi_dbg (ctx, "darwin/reenumerate_device: checking whether descriptors changed");
if (memcmp (&descriptor, &dpriv->dev_descriptor, sizeof (descriptor))) {
/* device descriptor changed. need to return not found. */
- usbi_dbg ("darwin/reset_device: device descriptor changed");
+ usbi_dbg (ctx, "darwin/reenumerate_device: device descriptor changed");
return LIBUSB_ERROR_NOT_FOUND;
}
for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) {
(void) (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
if (memcmp (cached_configuration, cached_configurations + i, sizeof (cached_configurations[i]))) {
- usbi_dbg ("darwin/reset_device: configuration descriptor %d changed", i);
+ usbi_dbg (ctx, "darwin/reenumerate_device: configuration descriptor %d changed", i);
return LIBUSB_ERROR_NOT_FOUND;
}
}
- usbi_dbg ("darwin/reset_device: device reset complete. restoring state...");
+ usbi_dbg (ctx, "darwin/reenumerate_device: device reset complete. restoring state...");
return darwin_restore_state (dev_handle, active_config, claimed_interfaces);
}
-static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t interface) {
+static int darwin_reset_device (struct libusb_device_handle *dev_handle) {
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
- io_service_t usbInterface;
- CFTypeRef driver;
IOReturn kresult;
+ enum libusb_error ret;
- kresult = darwin_get_interface (dpriv->device, interface, &usbInterface);
- if (kresult != kIOReturnSuccess) {
- usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
-
- return darwin_to_libusb (kresult);
+#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1
+ if (dpriv->capture_count > 0) {
+ /* we have to use ResetDevice as USBDeviceReEnumerate() loses the authorization for capture */
+ kresult = (*(dpriv->device))->ResetDevice (dpriv->device);
+ ret = darwin_to_libusb (kresult);
+ } else {
+ ret = darwin_reenumerate_device (dev_handle, false);
+ }
+#else
+ /* ResetDevice() is missing on non-macOS platforms */
+ ret = darwin_reenumerate_device (dev_handle, false);
+ if ((ret == LIBUSB_SUCCESS || ret == LIBUSB_ERROR_NOT_FOUND) && dpriv->capture_count > 0) {
+ int capture_count;
+ int8_t active_config = dpriv->active_config;
+ unsigned long claimed_interfaces = dev_handle->claimed_interfaces;
+
+ /* save old capture_count */
+ capture_count = dpriv->capture_count;
+ /* reset capture count */
+ dpriv->capture_count = 0;
+ /* attempt to detach kernel driver again as it is now re-attached */
+ ret = darwin_detach_kernel_driver (dev_handle, 0);
+ if (ret != LIBUSB_SUCCESS) {
+ return ret;
+ }
+ /* restore capture_count */
+ dpriv->capture_count = capture_count;
+ /* restore configuration */
+ ret = darwin_restore_state (dev_handle, active_config, claimed_interfaces);
}
+#endif
+ return ret;
+}
+
+static io_service_t usb_find_interface_matching_location (const io_name_t class_name, UInt8 interface_number, UInt32 location) {
+ CFMutableDictionaryRef matchingDict = IOServiceMatching (class_name);
+ CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable (kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location);
+ CFTypeRef interfaceCF = CFNumberCreate (NULL, kCFNumberSInt8Type, &interface_number);
- driver = IORegistryEntryCreateCFProperty (usbInterface, kIOBundleIdentifierKey, kCFAllocatorDefault, 0);
- IOObjectRelease (usbInterface);
+ CFDictionarySetValue (matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict);
+ CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF);
+ CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBHostMatchingPropertyInterfaceNumber), interfaceCF);
- if (driver) {
- CFRelease (driver);
+ CFRelease (interfaceCF);
+ CFRelease (locationCF);
+ CFRelease (propertyMatchDict);
+
+ return IOServiceGetMatchingService (darwin_default_master_port, matchingDict);
+}
+
+static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t interface) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ io_service_t usb_interface, child = IO_OBJECT_NULL;
+
+ /* locate the IO registry entry for this interface */
+ usb_interface = usb_find_interface_matching_location (kIOUSBHostInterfaceClassName, interface, dpriv->location);
+ if (0 == usb_interface) {
+ /* check for the legacy class entry */
+ usb_interface = usb_find_interface_matching_location (kIOUSBInterfaceClassName, interface, dpriv->location);
+ if (0 == usb_interface) {
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+ }
+ /* if the IO object has a child entry in the IO Registry it has a kernel driver attached */
+ (void) IORegistryEntryGetChildEntry (usb_interface, kIOServicePlane, &child);
+ IOObjectRelease (usb_interface);
+ if (IO_OBJECT_NULL != child) {
+ IOObjectRelease (child);
return 1;
}
@@ -1873,11 +2061,17 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
IOReturn kresult;
- uint8_t direction, number, interval, pipeRef, transferType;
- uint16_t maxPacketSize;
+ uint8_t pipeRef, interval;
UInt64 frame;
AbsoluteTime atTime;
int i;
+#if InterfaceVersion >= 550
+ IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3};
+#else
+ /* None of the values below are used in libusb for iso transfers */
+ uint8_t direction, number, transferType;
+ uint16_t maxPacketSize;
+#endif
struct darwin_interface *cInterface;
@@ -1909,8 +2103,20 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
}
/* determine the properties of this endpoint and the speed of the device */
- (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
+#if InterfaceVersion >= 550
+ kresult = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, pipeRef, &pipeProperties);
+ interval = pipeProperties.bInterval;
+#else
+ kresult = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
&transferType, &maxPacketSize, &interval);
+#endif
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (TRANSFER_CTX (transfer), "failed to get pipe properties: %d", kresult);
+ free(tpriv->isoc_framelist);
+ tpriv->isoc_framelist = NULL;
+
+ return darwin_to_libusb (kresult);
+ }
/* Last but not least we need the bus frame number */
kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime);
@@ -1922,9 +2128,6 @@ 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;
@@ -2051,8 +2254,10 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) {
uint8_t pipeRef, iface;
IOReturn kresult;
+ struct libusb_context *ctx = ITRANSFER_CTX (itransfer);
+
if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, &iface, &cInterface) != 0) {
- usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
+ usbi_err (ctx, "endpoint not found on any open interface");
return LIBUSB_ERROR_NOT_FOUND;
}
@@ -2060,7 +2265,7 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) {
if (!dpriv->device)
return LIBUSB_ERROR_NO_DEVICE;
- usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions on interface %d pipe %d", iface, pipeRef);
+ usbi_warn (ctx, "aborting all transactions on interface %d pipe %d", iface, pipeRef);
/* abort transactions */
#if InterfaceVersion >= 550
@@ -2070,7 +2275,7 @@ static int darwin_abort_transfers (struct usbi_transfer *itransfer) {
#endif
(*(cInterface->interface))->AbortPipe (cInterface->interface, pipeRef);
- usbi_dbg ("calling clear pipe stall to clear the data toggle bit");
+ usbi_dbg (ctx, "calling clear pipe stall to clear the data toggle bit");
/* newer versions of darwin support clearing additional bits on the device's endpoint */
kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef);
@@ -2099,7 +2304,7 @@ static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0)
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
- usbi_dbg ("an async io operation has completed");
+ usbi_dbg (TRANSFER_CTX(transfer), "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) {
@@ -2122,6 +2327,8 @@ static enum libusb_transfer_status darwin_transfer_status (struct usbi_transfer
if (itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT)
result = kIOUSBTransactionTimeout;
+ struct libusb_context *ctx = ITRANSFER_CTX (itransfer);
+
switch (result) {
case kIOReturnUnderrun:
case kIOReturnSuccess:
@@ -2129,17 +2336,17 @@ static enum libusb_transfer_status darwin_transfer_status (struct usbi_transfer
case kIOReturnAborted:
return LIBUSB_TRANSFER_CANCELLED;
case kIOUSBPipeStalled:
- usbi_dbg ("transfer error: pipe is stalled");
+ usbi_dbg (ctx, "transfer error: pipe is stalled");
return LIBUSB_TRANSFER_STALL;
case kIOReturnOverrun:
- usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: data overrun");
+ usbi_warn (ctx, "transfer error: data overrun");
return LIBUSB_TRANSFER_OVERFLOW;
case kIOUSBTransactionTimeout:
- usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: timed out");
+ usbi_warn (ctx, "transfer error: timed out");
itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT;
return LIBUSB_TRANSFER_TIMED_OUT;
default:
- usbi_warn (ITRANSFER_CTX (itransfer), "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result);
+ usbi_warn (ctx, "transfer error: %s (value = 0x%08x)", darwin_error_str (result), result);
return LIBUSB_TRANSFER_ERROR;
}
}
@@ -2148,22 +2355,23 @@ static int darwin_handle_transfer_completion (struct usbi_transfer *itransfer) {
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
const unsigned char max_transfer_type = LIBUSB_TRANSFER_TYPE_BULK_STREAM;
- const char *transfer_types[max_transfer_type + 1] = {"control", "isoc", "bulk", "interrupt", "bulk-stream"};
+ const char *transfer_types[] = {"control", "isoc", "bulk", "interrupt", "bulk-stream", NULL};
bool is_isoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type;
+ struct libusb_context *ctx = ITRANSFER_CTX (itransfer);
if (transfer->type > max_transfer_type) {
- usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+ usbi_err (ctx, "unknown endpoint type %d", transfer->type);
return LIBUSB_ERROR_INVALID_PARAM;
}
if (NULL == tpriv) {
- usbi_err (TRANSFER_CTX(transfer), "malformed request is missing transfer priv");
+ usbi_err (ctx, "malformed request is missing transfer priv");
return LIBUSB_ERROR_INVALID_PARAM;
}
- usbi_dbg ("handling transfer completion type %s with kernel status %d", transfer_types[transfer->type], tpriv->result);
+ usbi_dbg (ctx, "handling transfer completion type %s with kernel status %d", transfer_types[transfer->type], tpriv->result);
- if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result) {
+ if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result || kIOUSBTransactionTimeout == tpriv->result) {
if (is_isoc && tpriv->isoc_framelist) {
/* copy isochronous results back */
@@ -2262,9 +2470,159 @@ static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigne
}
#endif
+#if InterfaceVersion >= 700
+
+/* macOS APIs for getting entitlement values */
+
+#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1
+#include <Security/Security.h>
+#else
+typedef struct __SecTask *SecTaskRef;
+extern SecTaskRef SecTaskCreateFromSelf(CFAllocatorRef allocator);
+extern CFTypeRef SecTaskCopyValueForEntitlement(SecTaskRef task, CFStringRef entitlement, CFErrorRef *error);
+#endif
+
+static bool darwin_has_capture_entitlements (void) {
+ SecTaskRef task;
+ CFTypeRef value;
+ bool entitled;
+
+ task = SecTaskCreateFromSelf (kCFAllocatorDefault);
+ if (task == NULL) {
+ return false;
+ }
+ value = SecTaskCopyValueForEntitlement(task, CFSTR("com.apple.vm.device-access"), NULL);
+ CFRelease (task);
+ entitled = value && (CFGetTypeID (value) == CFBooleanGetTypeID ()) && CFBooleanGetValue (value);
+ if (value) {
+ CFRelease (value);
+ }
+ return entitled;
+}
+
+static int darwin_reload_device (struct libusb_device_handle *dev_handle) {
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ enum libusb_error err;
+
+ usbi_mutex_lock(&darwin_cached_devices_lock);
+ (*(dpriv->device))->Release(dpriv->device);
+ dpriv->device = darwin_device_from_service (HANDLE_CTX (dev_handle), dpriv->service);
+ if (!dpriv->device) {
+ err = LIBUSB_ERROR_NO_DEVICE;
+ } else {
+ err = LIBUSB_SUCCESS;
+ }
+ usbi_mutex_unlock(&darwin_cached_devices_lock);
+
+ return err;
+}
+
+/* On macOS, we capture an entire device at once, not individual interfaces. */
+
+static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) {
+ UNUSED(interface);
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+ IOReturn kresult;
+ enum libusb_error err;
+ struct libusb_context *ctx = HANDLE_CTX (dev_handle);
+
+ if (HAS_CAPTURE_DEVICE()) {
+ } else {
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+
+ if (dpriv->capture_count == 0) {
+ usbi_dbg (ctx, "attempting to detach kernel driver from device");
+
+ if (darwin_has_capture_entitlements ()) {
+ /* request authorization */
+ kresult = IOServiceAuthorize (dpriv->service, kIOServiceInteractionAllowed);
+ if (kresult != kIOReturnSuccess) {
+ usbi_warn (ctx, "IOServiceAuthorize: %s", darwin_error_str(kresult));
+ return darwin_to_libusb (kresult);
+ }
+
+ /* we need start() to be called again for authorization status to refresh */
+ err = darwin_reload_device (dev_handle);
+ if (err != LIBUSB_SUCCESS) {
+ return err;
+ }
+ } else {
+ usbi_info (ctx, "no capture entitlements. may not be able to detach the kernel driver for this device");
+ if (0 != geteuid()) {
+ usbi_warn (ctx, "USB device capture requires either an entitlement (com.apple.vm.device-access) or root privilege");
+ return LIBUSB_ERROR_ACCESS;
+ }
+ }
+
+ /* reset device to release existing drivers */
+ err = darwin_reenumerate_device (dev_handle, true);
+ if (err != LIBUSB_SUCCESS) {
+ return err;
+ }
+ }
+ dpriv->capture_count++;
+ return LIBUSB_SUCCESS;
+}
+
+
+static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) {
+ UNUSED(interface);
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+
+ if (HAS_CAPTURE_DEVICE()) {
+ } else {
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+
+ dpriv->capture_count--;
+ if (dpriv->capture_count > 0) {
+ return LIBUSB_SUCCESS;
+ }
+
+ usbi_dbg (HANDLE_CTX (dev_handle), "reenumerating device for kernel driver attach");
+
+ /* reset device to attach kernel drivers */
+ return darwin_reenumerate_device (dev_handle, false);
+}
+
+static int darwin_capture_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface) {
+ enum libusb_error ret;
+ if (dev_handle->auto_detach_kernel_driver && darwin_kernel_driver_active(dev_handle, iface)) {
+ ret = darwin_detach_kernel_driver (dev_handle, iface);
+ if (ret != LIBUSB_SUCCESS) {
+ usbi_info (HANDLE_CTX (dev_handle), "failed to auto-detach the kernel driver for this device, ret=%d", ret);
+ }
+ }
+
+ return darwin_claim_interface (dev_handle, iface);
+}
+
+static int darwin_capture_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface) {
+ enum libusb_error ret;
+ struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+
+ ret = darwin_release_interface (dev_handle, iface);
+ if (ret != LIBUSB_SUCCESS) {
+ return ret;
+ }
+
+ if (dev_handle->auto_detach_kernel_driver && dpriv->capture_count > 0) {
+ ret = darwin_attach_kernel_driver (dev_handle, iface);
+ if (LIBUSB_SUCCESS != ret) {
+ usbi_info (HANDLE_CTX (dev_handle), "on attempt to reattach the kernel driver got ret=%d", ret);
+ }
+ /* ignore the error as the interface was successfully released */
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+#endif
+
const struct usbi_os_backend usbi_backend = {
.name = "Darwin",
- .caps = 0,
+ .caps = USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER,
.init = darwin_init,
.exit = darwin_exit,
.get_active_config_descriptor = darwin_get_active_config_descriptor,
@@ -2275,8 +2633,6 @@ const struct usbi_os_backend usbi_backend = {
.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,
@@ -2289,6 +2645,16 @@ const struct usbi_os_backend usbi_backend = {
.kernel_driver_active = darwin_kernel_driver_active,
+#if InterfaceVersion >= 700
+ .detach_kernel_driver = darwin_detach_kernel_driver,
+ .attach_kernel_driver = darwin_attach_kernel_driver,
+ .claim_interface = darwin_capture_claim_interface,
+ .release_interface = darwin_capture_release_interface,
+#else
+ .claim_interface = darwin_claim_interface,
+ .release_interface = darwin_release_interface,
+#endif
+
.destroy_device = darwin_destroy_device,
.submit_transfer = darwin_submit_transfer,
diff --git a/libusb/os/darwin_usb.h b/libusb/os/darwin_usb.h
index b799bfd..7b72fff 100644
--- a/libusb/os/darwin_usb.h
+++ b/libusb/os/darwin_usb.h
@@ -30,59 +30,63 @@
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/IOCFPlugIn.h>
+#if defined(HAVE_IOKIT_USB_IOUSBHOSTFAMILYDEFINITIONS_H)
+#include <IOKit/usb/IOUSBHostFamilyDefinitions.h>
+#endif
+
/* IOUSBInterfaceInferface */
/* New in OS 10.12.0. */
-#if defined (kIOUSBInterfaceInterfaceID800) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
+#if defined (kIOUSBInterfaceInterfaceID800)
#define usb_interface_t IOUSBInterfaceInterface800
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID800
#define InterfaceVersion 800
/* New in OS 10.10.0. */
-#elif defined (kIOUSBInterfaceInterfaceID700) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101000)
+#elif defined (kIOUSBInterfaceInterfaceID700)
#define usb_interface_t IOUSBInterfaceInterface700
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID700
#define InterfaceVersion 700
/* New in OS 10.9.0. */
-#elif defined (kIOUSBInterfaceInterfaceID650) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
+#elif defined (kIOUSBInterfaceInterfaceID650)
#define usb_interface_t IOUSBInterfaceInterface650
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID650
#define InterfaceVersion 650
/* New in OS 10.8.2 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBInterfaceInterfaceID550) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
+#elif defined (kIOUSBInterfaceInterfaceID550)
#define usb_interface_t IOUSBInterfaceInterface550
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550
#define InterfaceVersion 550
/* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBInterfaceInterfaceID500) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
+#elif defined (kIOUSBInterfaceInterfaceID500)
#define usb_interface_t IOUSBInterfaceInterface500
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500
#define InterfaceVersion 500
/* New in OS 10.5.0. */
-#elif defined (kIOUSBInterfaceInterfaceID300) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
+#elif defined (kIOUSBInterfaceInterfaceID300)
#define usb_interface_t IOUSBInterfaceInterface300
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300
#define InterfaceVersion 300
/* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBInterfaceInterfaceID245) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
+#elif defined (kIOUSBInterfaceInterfaceID245)
#define usb_interface_t IOUSBInterfaceInterface245
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245
#define InterfaceVersion 245
/* New in OS 10.4.0. */
-#elif defined (kIOUSBInterfaceInterfaceID220) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1040)
+#elif defined (kIOUSBInterfaceInterfaceID220)
#define usb_interface_t IOUSBInterfaceInterface220
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220
@@ -97,42 +101,42 @@
/* IOUSBDeviceInterface */
/* New in OS 10.9.0. */
-#if defined (kIOUSBDeviceInterfaceID650) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
+#if defined (kIOUSBDeviceInterfaceID650)
#define usb_device_t IOUSBDeviceInterface650
#define DeviceInterfaceID kIOUSBDeviceInterfaceID650
#define DeviceVersion 650
/* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID500) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
+#elif defined (kIOUSBDeviceInterfaceID500)
#define usb_device_t IOUSBDeviceInterface500
#define DeviceInterfaceID kIOUSBDeviceInterfaceID500
#define DeviceVersion 500
/* New in OS 10.5.4 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID320) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
+#elif defined (kIOUSBDeviceInterfaceID320)
#define usb_device_t IOUSBDeviceInterface320
#define DeviceInterfaceID kIOUSBDeviceInterfaceID320
#define DeviceVersion 320
/* New in OS 10.5.0. */
-#elif defined (kIOUSBDeviceInterfaceID300) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
+#elif defined (kIOUSBDeviceInterfaceID300)
#define usb_device_t IOUSBDeviceInterface300
#define DeviceInterfaceID kIOUSBDeviceInterfaceID300
#define DeviceVersion 300
/* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID245) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
+#elif defined (kIOUSBDeviceInterfaceID245)
#define usb_device_t IOUSBDeviceInterface245
#define DeviceInterfaceID kIOUSBDeviceInterfaceID245
#define DeviceVersion 245
/* New in OS 10.2.3 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID197) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1030)
+#elif defined (kIOUSBDeviceInterfaceID197)
#define usb_device_t IOUSBDeviceInterface197
#define DeviceInterfaceID kIOUSBDeviceInterfaceID197
@@ -144,10 +148,28 @@
#endif
+#if !defined(kIOUSBHostInterfaceClassName)
+#define kIOUSBHostInterfaceClassName "IOUSBHostInterface"
+#endif
+
+#if !defined(kUSBHostMatchingPropertyInterfaceNumber)
+#define kUSBHostMatchingPropertyInterfaceNumber "bInterfaceNumber"
+#endif
+
#if !defined(IO_OBJECT_NULL)
#define IO_OBJECT_NULL ((io_object_t) 0)
#endif
+/* Testing availability */
+#ifndef __has_builtin
+ #define __has_builtin(x) 0 // Compatibility with non-clang compilers.
+#endif
+#if __has_builtin(__builtin_available)
+ #define HAS_CAPTURE_DEVICE() __builtin_available(macOS 10.10, *)
+#else
+ #define HAS_CAPTURE_DEVICE() 0
+#endif
+
typedef IOCFPlugInInterface *io_cf_plugin_ref_t;
typedef IONotificationPortRef io_notification_port_t;
@@ -161,11 +183,13 @@ struct darwin_cached_device {
USBDeviceAddress address;
char sys_path[21];
usb_device_t **device;
+ io_service_t service;
int open_count;
UInt8 first_config, active_config, port;
int can_enumerate;
int refcount;
bool in_reenumerate;
+ int capture_count;
};
struct darwin_device_priv {
diff --git a/libusb/os/events_posix.c b/libusb/os/events_posix.c
index b74189b..715a2d5 100644
--- a/libusb/os/events_posix.c
+++ b/libusb/os/events_posix.c
@@ -222,9 +222,9 @@ int usbi_wait_for_events(struct libusb_context *ctx,
usbi_nfds_t nfds = (usbi_nfds_t)ctx->event_data_cnt;
int internal_fds, num_ready;
- usbi_dbg("poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms);
+ usbi_dbg(ctx, "poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms);
num_ready = poll(fds, nfds, timeout_ms);
- usbi_dbg("poll() returned %d", num_ready);
+ usbi_dbg(ctx, "poll() returned %d", num_ready);
if (num_ready == 0) {
if (usbi_using_timer(ctx))
goto done;
@@ -279,7 +279,7 @@ int usbi_wait_for_events(struct libusb_context *ctx,
continue;
/* pollfd was removed between the creation of the fds array and
* here. remove triggered revent as it is no longer relevant. */
- usbi_dbg("fd %d was removed, ignoring raised events", fds[n].fd);
+ usbi_dbg(ctx, "fd %d was removed, ignoring raised events", fds[n].fd);
fds[n].revents = 0;
num_ready--;
break;
diff --git a/libusb/os/events_windows.c b/libusb/os/events_windows.c
index 81d8b87..f22bebc 100644
--- a/libusb/os/events_windows.c
+++ b/libusb/os/events_windows.c
@@ -171,9 +171,9 @@ int usbi_wait_for_events(struct libusb_context *ctx,
DWORD num_handles = (DWORD)ctx->event_data_cnt;
DWORD result;
- usbi_dbg("WaitForMultipleObjects() for %lu HANDLEs with timeout in %dms", ULONG_CAST(num_handles), timeout_ms);
+ usbi_dbg(ctx, "WaitForMultipleObjects() for %lu HANDLEs with timeout in %dms", ULONG_CAST(num_handles), timeout_ms);
result = WaitForMultipleObjects(num_handles, handles, FALSE, (DWORD)timeout_ms);
- usbi_dbg("WaitForMultipleObjects() returned %lu", ULONG_CAST(result));
+ usbi_dbg(ctx, "WaitForMultipleObjects() returned %lu", ULONG_CAST(result));
if (result == WAIT_TIMEOUT) {
if (usbi_using_timer(ctx))
goto done;
diff --git a/libusb/os/haiku_pollfs.cpp b/libusb/os/haiku_pollfs.cpp
index cb4fda8..b85edf7 100644
--- a/libusb/os/haiku_pollfs.cpp
+++ b/libusb/os/haiku_pollfs.cpp
@@ -100,14 +100,14 @@ WatchedEntry::WatchedEntry(BMessenger *messenger, entry_ref *ref)
for_each_context(ctx) {
struct libusb_device *dev = usbi_get_device_by_session_id(ctx, session_id);
if (dev) {
- usbi_dbg("using previously allocated device with location %lu", session_id);
+ usbi_dbg(NULL, "using previously allocated device with location %lu", session_id);
libusb_unref_device(dev);
continue;
}
- usbi_dbg("allocating new device with location %lu", session_id);
+ usbi_dbg(NULL, "allocating new device with location %lu", session_id);
dev = usbi_alloc_device(ctx, session_id);
if (!dev) {
- usbi_dbg("device allocation failed");
+ usbi_dbg(NULL, "device allocation failed");
continue;
}
*((USBDevice **)usbi_get_device_priv(dev)) = fDevice;
@@ -134,7 +134,7 @@ WatchedEntry::WatchedEntry(BMessenger *messenger, entry_ref *ref)
usbi_localize_device_descriptor(&dev->device_descriptor);
if (usbi_sanitize_device(dev) < 0) {
- usbi_dbg("device sanitization failed");
+ usbi_dbg(NULL, "device sanitization failed");
libusb_unref_device(dev);
continue;
}
@@ -178,7 +178,7 @@ WatchedEntry::~WatchedEntry()
usbi_disconnect_device(dev);
libusb_unref_device(dev);
} else {
- usbi_dbg("device with location %lu not found", session_id);
+ usbi_dbg(ctx, "device with location %lu not found", session_id);
}
}
usbi_mutex_static_unlock(&active_contexts_lock);
diff --git a/libusb/os/haiku_usb_backend.cpp b/libusb/os/haiku_usb_backend.cpp
index 8bbf3e0..2fcefdd 100644
--- a/libusb/os/haiku_usb_backend.cpp
+++ b/libusb/os/haiku_usb_backend.cpp
@@ -296,16 +296,16 @@ USBDeviceHandle::SetAltSetting(uint8 inumber, uint8 alt)
return _errno_to_libusb(command.alternate.status);
}
if (command.alternate.alternate_info == (uint32)alt) {
- usbi_dbg("Setting alternate interface successful");
+ usbi_dbg(NULL, "Setting alternate interface successful");
return LIBUSB_SUCCESS;
}
command.alternate.alternate_info = alt;
if (ioctl(fRawFD, B_USB_RAW_COMMAND_SET_ALT_INTERFACE, &command, sizeof(command)) ||
- command.alternate.status != B_USB_RAW_STATUS_SUCCESS) { //IF IOCTL FAILS DEVICE DISONNECTED PROBABLY
+ command.alternate.status != B_USB_RAW_STATUS_SUCCESS) { //IF IOCTL FAILS DEVICE DISCONNECTED PROBABLY
usbi_err(NULL, "Error setting alternate interface");
return _errno_to_libusb(command.alternate.status);
}
- usbi_dbg("Setting alternate interface successful");
+ usbi_dbg(NULL, "Setting alternate interface successful");
return LIBUSB_SUCCESS;
}
diff --git a/libusb/os/linux_netlink.c b/libusb/os/linux_netlink.c
index 77c83c5..899084f 100644
--- a/libusb/os/linux_netlink.c
+++ b/libusb/os/linux_netlink.c
@@ -34,8 +34,8 @@
#ifdef HAVE_ASM_TYPES_H
#include <asm/types.h>
#endif
-#include <linux/netlink.h>
#include <sys/socket.h>
+#include <linux/netlink.h>
#define NL_GROUP_KERNEL 1
@@ -99,7 +99,7 @@ int linux_netlink_start_event_monitor(void)
linux_netlink_socket = socket(PF_NETLINK, socktype, NETLINK_KOBJECT_UEVENT);
if (linux_netlink_socket == -1 && errno == EINVAL) {
- usbi_dbg("failed to create netlink socket of type %d, attempting SOCK_RAW", socktype);
+ usbi_dbg(NULL, "failed to create netlink socket of type %d, attempting SOCK_RAW", socktype);
socktype = SOCK_RAW;
linux_netlink_socket = socket(PF_NETLINK, socktype, NETLINK_KOBJECT_UEVENT);
}
@@ -204,7 +204,7 @@ static int linux_netlink_parse(const char *buffer, size_t len, int *detached,
} else if (strcmp(tmp, "remove") == 0) {
*detached = 1;
} else if (strcmp(tmp, "add") != 0) {
- usbi_dbg("unknown device action %s", tmp);
+ usbi_dbg(NULL, "unknown device action %s", tmp);
return -1;
}
@@ -311,20 +311,20 @@ static int linux_netlink_read_message(void)
}
if (sa_nl.nl_groups != NL_GROUP_KERNEL || sa_nl.nl_pid != 0) {
- usbi_dbg("ignoring netlink message from unknown group/PID (%u/%u)",
+ usbi_dbg(NULL, "ignoring netlink message from unknown group/PID (%u/%u)",
(unsigned int)sa_nl.nl_groups, (unsigned int)sa_nl.nl_pid);
return -1;
}
cmsg = CMSG_FIRSTHDR(&msg);
if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) {
- usbi_dbg("ignoring netlink message with no sender credentials");
+ usbi_dbg(NULL, "ignoring netlink message with no sender credentials");
return -1;
}
cred = (struct ucred *)CMSG_DATA(cmsg);
if (cred->uid != 0) {
- usbi_dbg("ignoring netlink message with non-zero sender UID %u", (unsigned int)cred->uid);
+ usbi_dbg(NULL, "ignoring netlink message with non-zero sender UID %u", (unsigned int)cred->uid);
return -1;
}
@@ -332,7 +332,7 @@ static int linux_netlink_read_message(void)
if (r)
return r;
- usbi_dbg("netlink hotplug found device busnum: %hhu, devaddr: %hhu, sys_name: %s, removed: %s",
+ usbi_dbg(NULL, "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 */
@@ -362,7 +362,7 @@ static void *linux_netlink_event_thread_main(void *arg)
usbi_warn(NULL, "failed to set hotplug event thread name, error=%d", r);
#endif
- usbi_dbg("netlink event thread entering");
+ usbi_dbg(NULL, "netlink event thread entering");
while (1) {
r = poll(fds, 2, -1);
@@ -384,7 +384,7 @@ static void *linux_netlink_event_thread_main(void *arg)
}
}
- usbi_dbg("netlink event thread exiting");
+ usbi_dbg(NULL, "netlink event thread exiting");
return NULL;
}
diff --git a/libusb/os/linux_udev.c b/libusb/os/linux_udev.c
index beb2f05..9ec9eb1 100644
--- a/libusb/os/linux_udev.c
+++ b/libusb/os/linux_udev.c
@@ -177,7 +177,7 @@ static void *linux_udev_event_thread_main(void *arg)
usbi_warn(NULL, "failed to set hotplug event thread name, error=%d", r);
#endif
- usbi_dbg("udev event thread entering");
+ usbi_dbg(NULL, "udev event thread entering");
while (1) {
r = poll(fds, 2, -1);
@@ -201,7 +201,7 @@ static void *linux_udev_event_thread_main(void *arg)
}
}
- usbi_dbg("udev event thread exiting");
+ usbi_dbg(NULL, "udev event thread exiting");
return NULL;
}
@@ -246,7 +246,7 @@ static void udev_hotplug_event(struct udev_device *udev_dev)
break;
}
- usbi_dbg("udev hotplug event. action: %s.", udev_action);
+ usbi_dbg(NULL, "udev hotplug event. action: %s.", udev_action);
if (strncmp(udev_action, "add", 3) == 0) {
linux_hotplug_enumerate(busnum, devaddr, sys_name);
@@ -313,7 +313,7 @@ void linux_udev_hotplug_poll(void)
do {
udev_dev = udev_monitor_receive_device(udev_monitor);
if (udev_dev) {
- usbi_dbg("Handling hotplug event from hotplug_poll");
+ usbi_dbg(NULL, "Handling hotplug event from hotplug_poll");
udev_hotplug_event(udev_dev);
}
} while (udev_dev);
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index fb2ed53..285d9ca 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -95,13 +95,9 @@ static int sysfs_available = -1;
/* how many times have we initted (and not exited) ? */
static int init_count = 0;
-#ifdef __ANDROID__
/* have no authority to operate usb device directly */
-static int weak_authority = 0;
-#endif
+static int no_enumeration = 0;
-/* Serialize hotplug start/stop */
-static usbi_mutex_static_t linux_hotplug_startstop_lock = USBI_MUTEX_INITIALIZER;
/* Serialize scan-devices, event-thread, and poll */
usbi_mutex_static_t linux_hotplug_lock = USBI_MUTEX_INITIALIZER;
@@ -128,7 +124,7 @@ struct linux_device_priv {
void *descriptors;
size_t descriptors_len;
struct config_descriptor *config_descriptors;
- uint8_t active_config; /* cache val for !sysfs_available */
+ int active_config; /* cache val for !sysfs_available */
};
struct linux_device_handle_priv {
@@ -169,6 +165,21 @@ struct linux_transfer_priv {
int iso_packet_offset;
};
+static int dev_has_config0(struct libusb_device *dev)
+{
+ struct linux_device_priv *priv = usbi_get_device_priv(dev);
+ struct config_descriptor *config;
+ uint8_t idx;
+
+ for (idx = 0; idx < dev->device_descriptor.bNumConfigurations; idx++) {
+ config = &priv->config_descriptors[idx];
+ if (config->desc->bConfigurationValue == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
static int get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent)
{
struct libusb_context *ctx = DEVICE_CTX(dev);
@@ -223,11 +234,11 @@ static int is_usbdev_entry(const char *name, uint8_t *bus_p, uint8_t *dev_p)
if (sscanf(name, "usbdev%d.%d", &busnum, &devnum) != 2)
return 0;
if (busnum < 0 || busnum > UINT8_MAX || devnum < 0 || devnum > UINT8_MAX) {
- usbi_dbg("invalid usbdev format '%s'", name);
+ usbi_dbg(NULL, "invalid usbdev format '%s'", name);
return 0;
}
- usbi_dbg("found: %s", name);
+ usbi_dbg(NULL, "found: %s", name);
if (bus_p)
*bus_p = (uint8_t)busnum;
if (dev_p)
@@ -312,7 +323,7 @@ static int get_kernel_version(struct libusb_context *ctx,
if (atoms < 3)
ver->sublevel = -1;
- usbi_dbg("reported kernel version is %s", uts.release);
+ usbi_dbg(ctx, "reported kernel version is %s", uts.release);
return 0;
}
@@ -360,7 +371,7 @@ static int op_init(struct libusb_context *ctx)
return LIBUSB_ERROR_OTHER;
}
- usbi_dbg("found usbfs at %s", usbfs_path);
+ usbi_dbg(ctx, "found usbfs at %s", usbfs_path);
if (!max_iso_packet_len) {
if (kernel_version_ge(&kversion, 5, 2, 0))
@@ -371,14 +382,14 @@ static int op_init(struct libusb_context *ctx)
max_iso_packet_len = 8192;
}
- usbi_dbg("max iso packet length is (likely) %u bytes", max_iso_packet_len);
+ usbi_dbg(ctx, "max iso packet length is (likely) %u bytes", max_iso_packet_len);
if (sysfs_available == -1) {
struct statfs statfsbuf;
r = statfs(SYSFS_MOUNT_PATH, &statfsbuf);
if (r == 0 && statfsbuf.f_type == SYSFS_MAGIC) {
- usbi_dbg("sysfs is available");
+ usbi_dbg(ctx, "sysfs is available");
sysfs_available = 1;
} else {
usbi_warn(ctx, "sysfs not mounted");
@@ -386,13 +397,10 @@ static int op_init(struct libusb_context *ctx)
}
}
-#ifdef __ANDROID__
- if (weak_authority) {
+ if (no_enumeration) {
return LIBUSB_SUCCESS;
}
-#endif
- usbi_mutex_static_lock(&linux_hotplug_startstop_lock);
r = LIBUSB_SUCCESS;
if (init_count == 0) {
/* start up hotplug event handler */
@@ -407,7 +415,6 @@ static int op_init(struct libusb_context *ctx)
} else {
usbi_err(ctx, "error starting hotplug event monitor");
}
- usbi_mutex_static_unlock(&linux_hotplug_startstop_lock);
return r;
}
@@ -415,13 +422,16 @@ static int op_init(struct libusb_context *ctx)
static void op_exit(struct libusb_context *ctx)
{
UNUSED(ctx);
- usbi_mutex_static_lock(&linux_hotplug_startstop_lock);
+
+ if (no_enumeration) {
+ return;
+ }
+
assert(init_count != 0);
if (!--init_count) {
/* tear down event handler */
linux_stop_event_monitor();
}
- usbi_mutex_static_unlock(&linux_hotplug_startstop_lock);
}
static int op_set_option(struct libusb_context *ctx, enum libusb_option option, va_list ap)
@@ -429,15 +439,11 @@ static int op_set_option(struct libusb_context *ctx, enum libusb_option option,
UNUSED(ctx);
UNUSED(ap);
-#ifdef __ANDROID__
- if (option == LIBUSB_OPTION_WEAK_AUTHORITY) {
- usbi_dbg("set libusb has weak authority");
- weak_authority = 1;
+ if (option == LIBUSB_OPTION_NO_DEVICE_DISCOVERY) {
+ usbi_dbg(ctx, "no enumeration will be performed");
+ no_enumeration = 1;
return LIBUSB_SUCCESS;
}
-#else
- UNUSED(option);
-#endif
return LIBUSB_ERROR_NOT_SUPPORTED;
}
@@ -498,7 +504,7 @@ static int read_sysfs_attr(struct libusb_context *ctx,
if (fd < 0)
return fd;
- r = read(fd, buf, sizeof(buf));
+ r = read(fd, buf, sizeof(buf) - 1);
if (r < 0) {
r = errno;
close(fd);
@@ -516,16 +522,18 @@ static int read_sysfs_attr(struct libusb_context *ctx,
return 0;
}
- /* The kernel does *not* NULL-terminate the string, but every attribute
+ /* The kernel does *not* NUL-terminate the string, but every attribute
* should be terminated with a newline character. */
if (!isdigit(buf[0])) {
usbi_err(ctx, "attribute %s doesn't have numeric value?", attr);
return LIBUSB_ERROR_IO;
} else if (buf[r - 1] != '\n') {
- usbi_err(ctx, "attribute %s doesn't end with newline?", attr);
- return LIBUSB_ERROR_IO;
+ usbi_warn(ctx, "attribute %s doesn't end with newline?", attr);
+ } else {
+ /* Remove the terminating newline character */
+ r--;
}
- buf[r - 1] = '\0';
+ buf[r] = '\0';
errno = 0;
value = strtol(buf, &endptr, 10);
@@ -565,22 +573,12 @@ static int sysfs_scan_device(struct libusb_context *ctx, const char *devname)
}
/* read the bConfigurationValue for a device */
-static int sysfs_get_active_config(struct libusb_device *dev, uint8_t *config)
+static int sysfs_get_active_config(struct libusb_device *dev, int *config)
{
struct linux_device_priv *priv = usbi_get_device_priv(dev);
- int ret, tmp;
-
- ret = read_sysfs_attr(DEVICE_CTX(dev), priv->sysfs_dir, "bConfigurationValue",
- UINT8_MAX, &tmp);
- if (ret < 0)
- return ret;
-
- if (tmp == -1)
- tmp = 0; /* unconfigured */
- *config = (uint8_t)tmp;
-
- return 0;
+ return read_sysfs_attr(DEVICE_CTX(dev), priv->sysfs_dir, "bConfigurationValue",
+ UINT8_MAX, config);
}
int linux_get_device_address(struct libusb_context *ctx, int detached,
@@ -590,7 +588,7 @@ int linux_get_device_address(struct libusb_context *ctx, int detached,
int sysfs_val;
int r;
- usbi_dbg("getting address for device: %s detached: %d", sys_name, detached);
+ usbi_dbg(ctx, "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_available || detached || !sys_name) {
@@ -619,7 +617,7 @@ int linux_get_device_address(struct libusb_context *ctx, int detached,
return LIBUSB_SUCCESS;
}
- usbi_dbg("scan %s", sys_name);
+ usbi_dbg(ctx, "scan %s", sys_name);
r = read_sysfs_attr(ctx, sys_name, "busnum", UINT8_MAX, &sysfs_val);
if (r < 0)
@@ -631,7 +629,7 @@ int linux_get_device_address(struct libusb_context *ctx, int detached,
return r;
*devaddr = (uint8_t)sysfs_val;
- usbi_dbg("bus=%u dev=%u", *busnum, *devaddr);
+ usbi_dbg(ctx, "bus=%u dev=%u", *busnum, *devaddr);
return LIBUSB_SUCCESS;
}
@@ -641,7 +639,12 @@ static int seek_to_next_config(struct libusb_context *ctx,
uint8_t *buffer, size_t len)
{
struct usbi_descriptor_header *header;
- int offset = 0;
+ int offset;
+
+ /* Start seeking past the config descriptor */
+ offset = LIBUSB_DT_CONFIG_SIZE;
+ buffer += LIBUSB_DT_CONFIG_SIZE;
+ len -= LIBUSB_DT_CONFIG_SIZE;
while (len > 0) {
if (len < 2) {
@@ -718,7 +721,7 @@ static int parse_config_descriptors(struct libusb_device *dev)
}
if (priv->sysfs_dir) {
- /*
+ /*
* In sysfs wTotalLength is ignored, instead the kernel returns a
* config descriptor with verified bLength fields, with descriptors
* with an invalid bLength removed.
@@ -727,8 +730,7 @@ static int parse_config_descriptors(struct libusb_device *dev)
int offset;
if (num_configs > 1 && idx < num_configs - 1) {
- offset = seek_to_next_config(ctx, buffer + LIBUSB_DT_CONFIG_SIZE,
- remaining - LIBUSB_DT_CONFIG_SIZE);
+ offset = seek_to_next_config(ctx, buffer, remaining);
if (offset < 0)
return offset;
sysfs_config_len = (uint16_t)offset;
@@ -752,6 +754,9 @@ static int parse_config_descriptors(struct libusb_device *dev)
}
}
+ if (config_desc->bConfigurationValue == 0)
+ usbi_warn(ctx, "device has configuration 0");
+
priv->config_descriptors[idx].desc = config_desc;
priv->config_descriptors[idx].actual_len = config_len;
@@ -785,7 +790,7 @@ static int op_get_active_config_descriptor(struct libusb_device *dev,
{
struct linux_device_priv *priv = usbi_get_device_priv(dev);
void *config_desc;
- uint8_t active_config;
+ int active_config;
int r;
if (priv->sysfs_dir) {
@@ -797,12 +802,12 @@ static int op_get_active_config_descriptor(struct libusb_device *dev,
active_config = priv->active_config;
}
- if (active_config == 0) {
+ if (active_config == -1) {
usbi_err(DEVICE_CTX(dev), "device unconfigured");
return LIBUSB_ERROR_NOT_FOUND;
}
- r = op_get_config_descriptor_by_value(dev, active_config, &config_desc);
+ r = op_get_config_descriptor_by_value(dev, (uint8_t)active_config, &config_desc);
if (r < 0)
return r;
@@ -850,17 +855,26 @@ static int usbfs_get_active_config(struct libusb_device *dev, int fd)
/* we hit this error path frequently with buggy devices :( */
usbi_warn(DEVICE_CTX(dev), "get configuration failed, errno=%d", errno);
+
+ /* assume the current configuration is the first one if we have
+ * the configuration descriptors, otherwise treat the device
+ * as unconfigured. */
+ if (priv->config_descriptors)
+ priv->active_config = (int)priv->config_descriptors[0].desc->bConfigurationValue;
+ else
+ priv->active_config = -1;
} 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_warn(DEVICE_CTX(dev), "active cfg 0? assuming unconfigured device");
+ if (dev_has_config0(dev)) {
+ /* some buggy devices have a configuration 0, but we're
+ * reaching into the corner of a corner case here. */
+ priv->active_config = 0;
+ } else {
+ priv->active_config = -1;
+ }
+ } else {
+ priv->active_config = (int)active_config;
}
- priv->active_config = active_config;
-
return LIBUSB_SUCCESS;
}
@@ -991,9 +1005,9 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum,
usbi_warn(ctx, "Missing rw usbfs access; cannot determine "
"active configuration descriptor");
if (priv->config_descriptors)
- priv->active_config = priv->config_descriptors[0].desc->bConfigurationValue;
+ priv->active_config = (int)priv->config_descriptors[0].desc->bConfigurationValue;
else
- priv->active_config = 0; /* No config dt */
+ priv->active_config = -1; /* No config dt */
return LIBUSB_SUCCESS;
}
@@ -1058,14 +1072,14 @@ retry:
usbi_mutex_unlock(&ctx->usb_devs_lock);
if (!dev->parent_dev && add_parent) {
- usbi_dbg("parent_dev %s not enumerated yet, enumerating now",
+ usbi_dbg(ctx, "parent_dev %s not enumerated yet, enumerating now",
parent_sysfs_dir);
sysfs_scan_device(ctx, parent_sysfs_dir);
add_parent = 0;
goto retry;
}
- usbi_dbg("dev %p (%s) has parent %p (%s) port %u", dev, sysfs_dir,
+ usbi_dbg(ctx, "dev %p (%s) has parent %p (%s) port %u", dev, sysfs_dir,
dev->parent_dev, parent_sysfs_dir, dev->port_number);
free(parent_sysfs_dir);
@@ -1084,17 +1098,17 @@ int linux_enumerate_device(struct libusb_context *ctx,
* will be reused. instead we should add a simple sysfs attribute with
* a session ID. */
session_id = busnum << 8 | devaddr;
- usbi_dbg("busnum %u devaddr %u session_id %lu", busnum, devaddr, session_id);
+ usbi_dbg(ctx, "busnum %u devaddr %u session_id %lu", busnum, devaddr, session_id);
dev = usbi_get_device_by_session_id(ctx, session_id);
if (dev) {
/* device already exists in the context */
- usbi_dbg("session_id %lu already exists", session_id);
+ usbi_dbg(ctx, "session_id %lu already exists", session_id);
libusb_unref_device(dev);
return LIBUSB_SUCCESS;
}
- usbi_dbg("allocating new device for %u/%u (session %lu)",
+ usbi_dbg(ctx, "allocating new device for %u/%u (session %lu)",
busnum, devaddr, session_id);
dev = usbi_alloc_device(ctx, session_id);
if (!dev)
@@ -1143,7 +1157,7 @@ void linux_device_disconnected(uint8_t busnum, uint8_t devaddr)
usbi_disconnect_device(dev);
libusb_unref_device(dev);
} else {
- usbi_dbg("device not found for session %lx", session_id);
+ usbi_dbg(ctx, "device not found for session %lx", session_id);
}
}
usbi_mutex_static_unlock(&active_contexts_lock);
@@ -1175,7 +1189,7 @@ static int usbfs_scan_busdir(struct libusb_context *ctx, uint8_t busnum)
int r = LIBUSB_ERROR_IO;
sprintf(dirpath, USB_DEVTMPFS_PATH "/%03u", busnum);
- usbi_dbg("%s", dirpath);
+ usbi_dbg(ctx, "%s", dirpath);
dir = opendir(dirpath);
if (!dir) {
usbi_err(ctx, "opendir '%s' failed, errno=%d", dirpath, errno);
@@ -1191,12 +1205,12 @@ static int usbfs_scan_busdir(struct libusb_context *ctx, uint8_t busnum)
continue;
if (!parse_u8(entry->d_name, &devaddr)) {
- usbi_dbg("unknown dir entry %s", entry->d_name);
+ usbi_dbg(ctx, "unknown dir entry %s", entry->d_name);
continue;
}
if (linux_enumerate_device(ctx, busnum, devaddr, NULL)) {
- usbi_dbg("failed to enumerate dir entry %s", entry->d_name);
+ usbi_dbg(ctx, "failed to enumerate dir entry %s", entry->d_name);
continue;
}
@@ -1234,12 +1248,12 @@ static int usbfs_get_device_list(struct libusb_context *ctx)
r = linux_enumerate_device(ctx, busnum, devaddr, NULL);
if (r < 0) {
- usbi_dbg("failed to enumerate dir entry %s", entry->d_name);
+ usbi_dbg(ctx, "failed to enumerate dir entry %s", entry->d_name);
continue;
}
} else {
if (!parse_u8(entry->d_name, &busnum)) {
- usbi_dbg("unknown dir entry %s", entry->d_name);
+ usbi_dbg(ctx, "unknown dir entry %s", entry->d_name);
continue;
}
@@ -1274,7 +1288,7 @@ static int sysfs_get_device_list(struct libusb_context *ctx)
num_devices++;
if (sysfs_scan_device(ctx, entry->d_name)) {
- usbi_dbg("failed to enumerate dir entry %s", entry->d_name);
+ usbi_dbg(ctx, "failed to enumerate dir entry %s", entry->d_name);
continue;
}
@@ -1314,7 +1328,7 @@ static int initialize_handle(struct libusb_device_handle *handle, int fd)
r = ioctl(fd, IOCTL_USBFS_GET_CAPABILITIES, &hpriv->caps);
if (r < 0) {
if (errno == ENOTTY)
- usbi_dbg("getcap not available");
+ usbi_dbg(HANDLE_CTX(handle), "getcap not available");
else
usbi_err(HANDLE_CTX(handle), "getcap failed, errno=%d", errno);
hpriv->caps = USBFS_CAP_BULK_CONTINUATION;
@@ -1348,7 +1362,7 @@ static int op_wrap_sys_device(struct libusb_context *ctx,
/* Session id is unused as we do not add the device to the list of
* connected devices. */
- usbi_dbg("allocating new device for fd %d", fd);
+ usbi_dbg(ctx, "allocating new device for fd %d", fd);
dev = usbi_alloc_device(ctx, 0);
if (!dev)
return LIBUSB_ERROR_NO_MEM;
@@ -1361,7 +1375,7 @@ static int op_wrap_sys_device(struct libusb_context *ctx,
goto out;
/* Consider the device as connected, but do not add it to the managed
* device list. */
- dev->attached = 1;
+ usbi_atomic_store(&dev->attached, 1);
handle->dev = dev;
r = initialize_handle(handle, fd);
@@ -1383,8 +1397,8 @@ static int op_open(struct libusb_device_handle *handle)
/* device will still be marked as attached if hotplug monitor thread
* hasn't processed remove event yet */
usbi_mutex_static_lock(&linux_hotplug_lock);
- if (handle->dev->attached) {
- usbi_dbg("open failed with no device, but device still attached");
+ if (usbi_atomic_load(&handle->dev->attached)) {
+ usbi_dbg(HANDLE_CTX(handle), "open failed with no device, but device still attached");
linux_device_disconnected(handle->dev->bus_number,
handle->dev->device_address);
}
@@ -1415,22 +1429,27 @@ static int op_get_configuration(struct libusb_device_handle *handle,
uint8_t *config)
{
struct linux_device_priv *priv = usbi_get_device_priv(handle->dev);
+ int active_config = -1; /* to please compiler */
int r;
if (priv->sysfs_dir) {
- r = sysfs_get_active_config(handle->dev, config);
+ r = sysfs_get_active_config(handle->dev, &active_config);
} else {
struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle);
r = usbfs_get_active_config(handle->dev, hpriv->fd);
if (r == LIBUSB_SUCCESS)
- *config = priv->active_config;
+ active_config = priv->active_config;
}
if (r < 0)
return r;
- if (*config == 0)
- usbi_err(HANDLE_CTX(handle), "device unconfigured");
+ if (active_config == -1) {
+ usbi_warn(HANDLE_CTX(handle), "device unconfigured");
+ active_config = 0;
+ }
+
+ *config = (uint8_t)active_config;
return 0;
}
@@ -1454,11 +1473,13 @@ static int op_set_configuration(struct libusb_device_handle *handle, int config)
return LIBUSB_ERROR_OTHER;
}
- if (config == -1)
- config = 0;
+ /* if necessary, update our cached active config descriptor */
+ if (!priv->sysfs_dir) {
+ if (config == 0 && !dev_has_config0(handle->dev))
+ config = -1;
- /* update our cached active config descriptor */
- priv->active_config = (uint8_t)config;
+ priv->active_config = config;
+ }
return LIBUSB_SUCCESS;
}
@@ -1847,11 +1868,11 @@ static int discard_urbs(struct usbi_transfer *itransfer, int first, int last_plu
continue;
if (errno == EINVAL) {
- usbi_dbg("URB not found --> assuming ready to be reaped");
+ usbi_dbg(TRANSFER_CTX(transfer), "URB not found --> assuming ready to be reaped");
if (i == (last_plus_one - 1))
ret = LIBUSB_ERROR_NOT_FOUND;
} else if (errno == ENODEV) {
- usbi_dbg("Device not found for URB --> assuming ready to be reaped");
+ usbi_dbg(TRANSFER_CTX(transfer), "Device not found for URB --> assuming ready to be reaped");
ret = LIBUSB_ERROR_NO_DEVICE;
} else {
usbi_warn(TRANSFER_CTX(transfer), "unrecognised discard errno %d", errno);
@@ -1942,7 +1963,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer)
last_urb_partial = 1;
num_urbs++;
}
- usbi_dbg("need %d urbs for new transfer with length %d", num_urbs, transfer->length);
+ usbi_dbg(TRANSFER_CTX(transfer), "need %d urbs for new transfer with length %d", num_urbs, transfer->length);
urbs = calloc(num_urbs, sizeof(*urbs));
if (!urbs)
return LIBUSB_ERROR_NO_MEM;
@@ -2007,7 +2028,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer)
/* if the first URB submission fails, we can simply free up and
* return failure immediately. */
if (i == 0) {
- usbi_dbg("first URB failed, easy peasy");
+ usbi_dbg(TRANSFER_CTX(transfer), "first URB failed, easy peasy");
free(urbs);
tpriv->urbs = NULL;
return r;
@@ -2041,7 +2062,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer)
discard_urbs(itransfer, 0, i);
- usbi_dbg("reporting successful submission but waiting for %d "
+ usbi_dbg(TRANSFER_CTX(transfer), "reporting successful submission but waiting for %d "
"discards before reporting error", i);
return 0;
}
@@ -2092,7 +2113,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
/* usbfs limits the number of iso packets per URB */
num_urbs = (num_packets + (MAX_ISO_PACKETS_PER_URB - 1)) / MAX_ISO_PACKETS_PER_URB;
- usbi_dbg("need %d urbs for new transfer with length %d", num_urbs, transfer->length);
+ usbi_dbg(TRANSFER_CTX(transfer), "need %d urbs for new transfer with length %d", num_urbs, transfer->length);
urbs = calloc(num_urbs, sizeof(*urbs));
if (!urbs)
@@ -2163,7 +2184,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
/* if the first URB submission fails, we can simply free up and
* return failure immediately. */
if (i == 0) {
- usbi_dbg("first URB failed, easy peasy");
+ usbi_dbg(TRANSFER_CTX(transfer), "first URB failed, easy peasy");
free_iso_urbs(tpriv);
return r;
}
@@ -2188,7 +2209,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
tpriv->num_retired = num_urbs - i;
discard_urbs(itransfer, 0, i);
- usbi_dbg("reporting successful submission but waiting for %d "
+ usbi_dbg(TRANSFER_CTX(transfer), "reporting successful submission but waiting for %d "
"discards before reporting error", i);
return 0;
}
@@ -2318,14 +2339,14 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
int urb_idx = urb - tpriv->urbs;
usbi_mutex_lock(&itransfer->lock);
- usbi_dbg("handling completion status %d of bulk urb %d/%d", urb->status,
+ usbi_dbg(TRANSFER_CTX(transfer), "handling completion status %d of bulk urb %d/%d", urb->status,
urb_idx + 1, tpriv->num_urbs);
tpriv->num_retired++;
if (tpriv->reap_action != NORMAL) {
/* cancelled, submit_fail, or completed early */
- usbi_dbg("abnormal reap: urb status %d", urb->status);
+ usbi_dbg(TRANSFER_CTX(transfer), "abnormal reap: urb status %d", urb->status);
/* even though we're in the process of cancelling, it's possible that
* we may receive some data in these URBs that we don't want to lose.
@@ -2346,9 +2367,9 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
if (urb->actual_length > 0) {
unsigned char *target = transfer->buffer + itransfer->transferred;
- usbi_dbg("received %d bytes of surplus data", urb->actual_length);
+ usbi_dbg(TRANSFER_CTX(transfer), "received %d bytes of surplus data", urb->actual_length);
if (urb->buffer != target) {
- usbi_dbg("moving surplus data from offset %zu to offset %zu",
+ usbi_dbg(TRANSFER_CTX(transfer), "moving surplus data from offset %zu to offset %zu",
(unsigned char *)urb->buffer - transfer->buffer,
target - transfer->buffer);
memmove(target, urb->buffer, urb->actual_length);
@@ -2357,7 +2378,7 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
}
if (tpriv->num_retired == tpriv->num_urbs) {
- usbi_dbg("abnormal reap: last URB handled, reporting");
+ usbi_dbg(TRANSFER_CTX(transfer), "abnormal reap: last URB handled, reporting");
if (tpriv->reap_action != COMPLETED_EARLY &&
tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED)
tpriv->reap_status = LIBUSB_TRANSFER_ERROR;
@@ -2381,17 +2402,17 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
break;
case -ENODEV:
case -ESHUTDOWN:
- usbi_dbg("device removed");
+ usbi_dbg(TRANSFER_CTX(transfer), "device removed");
tpriv->reap_status = LIBUSB_TRANSFER_NO_DEVICE;
goto cancel_remaining;
case -EPIPE:
- usbi_dbg("detected endpoint stall");
+ usbi_dbg(TRANSFER_CTX(transfer), "detected endpoint stall");
if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED)
tpriv->reap_status = LIBUSB_TRANSFER_STALL;
goto cancel_remaining;
case -EOVERFLOW:
/* overflow can only ever occur in the last urb */
- usbi_dbg("overflow, actual_length=%d", urb->actual_length);
+ usbi_dbg(TRANSFER_CTX(transfer), "overflow, actual_length=%d", urb->actual_length);
if (tpriv->reap_status == LIBUSB_TRANSFER_COMPLETED)
tpriv->reap_status = LIBUSB_TRANSFER_OVERFLOW;
goto completed;
@@ -2400,7 +2421,7 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
case -EILSEQ:
case -ECOMM:
case -ENOSR:
- usbi_dbg("low-level bus error %d", urb->status);
+ usbi_dbg(TRANSFER_CTX(transfer), "low-level bus error %d", urb->status);
tpriv->reap_action = ERROR;
goto cancel_remaining;
default:
@@ -2412,10 +2433,10 @@ static int handle_bulk_completion(struct usbi_transfer *itransfer,
/* if we've reaped all urbs or we got less data than requested then we're
* done */
if (tpriv->num_retired == tpriv->num_urbs) {
- usbi_dbg("all URBs in transfer reaped --> complete!");
+ usbi_dbg(TRANSFER_CTX(transfer), "all URBs in transfer reaped --> complete!");
goto completed;
} else if (urb->actual_length < urb->buffer_length) {
- usbi_dbg("short transfer %d/%d --> complete!",
+ usbi_dbg(TRANSFER_CTX(transfer), "short transfer %d/%d --> complete!",
urb->actual_length, urb->buffer_length);
if (tpriv->reap_action == NORMAL)
tpriv->reap_action = COMPLETED_EARLY;
@@ -2471,7 +2492,7 @@ static int handle_iso_completion(struct usbi_transfer *itransfer,
return LIBUSB_ERROR_NOT_FOUND;
}
- usbi_dbg("handling completion status %d of iso urb %d/%d", urb->status,
+ usbi_dbg(TRANSFER_CTX(transfer), "handling completion status %d of iso urb %d/%d", urb->status,
urb_idx, num_urbs);
/* copy isochronous results back in */
@@ -2490,15 +2511,15 @@ static int handle_iso_completion(struct usbi_transfer *itransfer,
break;
case -ENODEV:
case -ESHUTDOWN:
- usbi_dbg("packet %d - device removed", i);
+ usbi_dbg(TRANSFER_CTX(transfer), "packet %d - device removed", i);
lib_desc->status = LIBUSB_TRANSFER_NO_DEVICE;
break;
case -EPIPE:
- usbi_dbg("packet %d - detected endpoint stall", i);
+ usbi_dbg(TRANSFER_CTX(transfer), "packet %d - detected endpoint stall", i);
lib_desc->status = LIBUSB_TRANSFER_STALL;
break;
case -EOVERFLOW:
- usbi_dbg("packet %d - overflow error", i);
+ usbi_dbg(TRANSFER_CTX(transfer), "packet %d - overflow error", i);
lib_desc->status = LIBUSB_TRANSFER_OVERFLOW;
break;
case -ETIME:
@@ -2507,7 +2528,7 @@ static int handle_iso_completion(struct usbi_transfer *itransfer,
case -ECOMM:
case -ENOSR:
case -EXDEV:
- usbi_dbg("packet %d - low-level USB error %d", i, urb_desc->status);
+ usbi_dbg(TRANSFER_CTX(transfer), "packet %d - low-level USB error %d", i, urb_desc->status);
lib_desc->status = LIBUSB_TRANSFER_ERROR;
break;
default:
@@ -2522,10 +2543,10 @@ static int handle_iso_completion(struct usbi_transfer *itransfer,
tpriv->num_retired++;
if (tpriv->reap_action != NORMAL) { /* cancelled or submit_fail */
- usbi_dbg("CANCEL: urb status %d", urb->status);
+ usbi_dbg(TRANSFER_CTX(transfer), "CANCEL: urb status %d", urb->status);
if (tpriv->num_retired == num_urbs) {
- usbi_dbg("CANCEL: last URB handled, reporting");
+ usbi_dbg(TRANSFER_CTX(transfer), "CANCEL: last URB handled, reporting");
free_iso_urbs(tpriv);
if (tpriv->reap_action == CANCELLED) {
usbi_mutex_unlock(&itransfer->lock);
@@ -2545,7 +2566,7 @@ static int handle_iso_completion(struct usbi_transfer *itransfer,
case -ECONNRESET:
break;
case -ESHUTDOWN:
- usbi_dbg("device removed");
+ usbi_dbg(TRANSFER_CTX(transfer), "device removed");
status = LIBUSB_TRANSFER_NO_DEVICE;
break;
default:
@@ -2556,7 +2577,7 @@ static int handle_iso_completion(struct usbi_transfer *itransfer,
/* if we've reaped all urbs then we're done */
if (tpriv->num_retired == num_urbs) {
- usbi_dbg("all URBs in transfer reaped --> complete!");
+ usbi_dbg(TRANSFER_CTX(transfer), "all URBs in transfer reaped --> complete!");
free_iso_urbs(tpriv);
usbi_mutex_unlock(&itransfer->lock);
return usbi_handle_transfer_completion(itransfer, status);
@@ -2574,7 +2595,7 @@ static int handle_control_completion(struct usbi_transfer *itransfer,
int status;
usbi_mutex_lock(&itransfer->lock);
- usbi_dbg("handling completion status %d", urb->status);
+ usbi_dbg(ITRANSFER_CTX(itransfer), "handling completion status %d", urb->status);
itransfer->transferred += urb->actual_length;
@@ -2597,15 +2618,15 @@ static int handle_control_completion(struct usbi_transfer *itransfer,
break;
case -ENODEV:
case -ESHUTDOWN:
- usbi_dbg("device removed");
+ usbi_dbg(ITRANSFER_CTX(itransfer), "device removed");
status = LIBUSB_TRANSFER_NO_DEVICE;
break;
case -EPIPE:
- usbi_dbg("unsupported control request");
+ usbi_dbg(ITRANSFER_CTX(itransfer), "unsupported control request");
status = LIBUSB_TRANSFER_STALL;
break;
case -EOVERFLOW:
- usbi_dbg("overflow, actual_length=%d", urb->actual_length);
+ usbi_dbg(ITRANSFER_CTX(itransfer), "overflow, actual_length=%d", urb->actual_length);
status = LIBUSB_TRANSFER_OVERFLOW;
break;
case -ETIME:
@@ -2613,7 +2634,7 @@ static int handle_control_completion(struct usbi_transfer *itransfer,
case -EILSEQ:
case -ECOMM:
case -ENOSR:
- usbi_dbg("low-level bus error %d", urb->status);
+ usbi_dbg(ITRANSFER_CTX(itransfer), "low-level bus error %d", urb->status);
status = LIBUSB_TRANSFER_ERROR;
break;
default:
@@ -2650,7 +2671,7 @@ static int reap_for_handle(struct libusb_device_handle *handle)
itransfer = urb->usercontext;
transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- usbi_dbg("urb type=%u status=%d transferred=%d", urb->type, urb->status, urb->actual_length);
+ usbi_dbg(HANDLE_CTX(handle), "urb type=%u status=%d transferred=%d", urb->type, urb->status, urb->actual_length);
switch (transfer->type) {
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
@@ -2707,7 +2728,7 @@ static int op_handle_events(struct libusb_context *ctx,
/* device will still be marked as attached if hotplug monitor thread
* hasn't processed remove event yet */
usbi_mutex_static_lock(&linux_hotplug_lock);
- if (handle->dev->attached)
+ if (usbi_atomic_load(&handle->dev->attached))
linux_device_disconnected(handle->dev->bus_number,
handle->dev->device_address);
usbi_mutex_static_unlock(&linux_hotplug_lock);
diff --git a/libusb/os/netbsd_usb.c b/libusb/os/netbsd_usb.c
index 7a36209..74833f6 100644
--- a/libusb/os/netbsd_usb.c
+++ b/libusb/os/netbsd_usb.c
@@ -122,7 +122,7 @@ netbsd_get_device_list(struct libusb_context * ctx,
char devnode[16];
int fd, err, i;
- usbi_dbg(" ");
+ usbi_dbg(ctx, " ");
/* Only ugen(4) is supported */
for (i = 0; i < USB_MAX_DEVICES; i++) {
@@ -205,7 +205,7 @@ netbsd_open(struct libusb_device_handle *handle)
for (i = 0; i < USB_MAX_ENDPOINTS; i++)
hpriv->endpoints[i] = -1;
- usbi_dbg("open %s: fd %d", dpriv->devnode, dpriv->fd);
+ usbi_dbg(HANDLE_CTX(handle), "open %s: fd %d", dpriv->devnode, dpriv->fd);
return (LIBUSB_SUCCESS);
}
@@ -215,7 +215,7 @@ netbsd_close(struct libusb_device_handle *handle)
{
struct device_priv *dpriv = usbi_get_device_priv(handle->dev);
- usbi_dbg("close: fd %d", dpriv->fd);
+ usbi_dbg(HANDLE_CTX(handle), "close: fd %d", dpriv->fd);
close(dpriv->fd);
dpriv->fd = -1;
@@ -229,7 +229,7 @@ netbsd_get_active_config_descriptor(struct libusb_device *dev,
len = MIN(len, (size_t)UGETW(dpriv->cdesc->wTotalLength));
- usbi_dbg("len %zu", len);
+ usbi_dbg(DEVICE_CTX(dev), "len %zu", len);
memcpy(buf, dpriv->cdesc, len);
@@ -244,7 +244,7 @@ netbsd_get_config_descriptor(struct libusb_device *dev, uint8_t idx,
struct usb_full_desc ufd;
int fd, err;
- usbi_dbg("index %u, len %zu", idx, len);
+ usbi_dbg(DEVICE_CTX(dev), "index %u, len %zu", idx, len);
/* A config descriptor may be requested before opening the device */
if (dpriv->fd >= 0) {
@@ -278,12 +278,12 @@ netbsd_get_configuration(struct libusb_device_handle *handle, uint8_t *config)
struct device_priv *dpriv = usbi_get_device_priv(handle->dev);
int tmp;
- usbi_dbg(" ");
+ usbi_dbg(HANDLE_CTX(handle), " ");
if (ioctl(dpriv->fd, USB_GET_CONFIG, &tmp) < 0)
return _errno_to_libusb(errno);
- usbi_dbg("configuration %d", tmp);
+ usbi_dbg(HANDLE_CTX(handle), "configuration %d", tmp);
*config = (uint8_t)tmp;
return (LIBUSB_SUCCESS);
@@ -294,7 +294,7 @@ netbsd_set_configuration(struct libusb_device_handle *handle, int config)
{
struct device_priv *dpriv = usbi_get_device_priv(handle->dev);
- usbi_dbg("configuration %d", config);
+ usbi_dbg(HANDLE_CTX(handle), "configuration %d", config);
if (ioctl(dpriv->fd, USB_SET_CONFIG, &config) < 0)
return _errno_to_libusb(errno);
@@ -338,7 +338,7 @@ netbsd_set_interface_altsetting(struct libusb_device_handle *handle, uint8_t ifa
struct device_priv *dpriv = usbi_get_device_priv(handle->dev);
struct usb_alt_interface intf;
- usbi_dbg("iface %u, setting %u", iface, altsetting);
+ usbi_dbg(HANDLE_CTX(handle), "iface %u, setting %u", iface, altsetting);
memset(&intf, 0, sizeof(intf));
@@ -357,7 +357,7 @@ netbsd_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint)
struct device_priv *dpriv = usbi_get_device_priv(handle->dev);
struct usb_ctl_request req;
- usbi_dbg(" ");
+ usbi_dbg(HANDLE_CTX(handle), " ");
req.ucr_request.bmRequestType = UT_WRITE_ENDPOINT;
req.ucr_request.bRequest = UR_CLEAR_FEATURE;
@@ -376,7 +376,7 @@ netbsd_destroy_device(struct libusb_device *dev)
{
struct device_priv *dpriv = usbi_get_device_priv(dev);
- usbi_dbg(" ");
+ usbi_dbg(DEVICE_CTX(dev), " ");
free(dpriv->cdesc);
}
@@ -387,7 +387,7 @@ netbsd_submit_transfer(struct usbi_transfer *itransfer)
struct libusb_transfer *transfer;
int err = 0;
- usbi_dbg(" ");
+ usbi_dbg(ITRANSFER_CTX(itransfer), " ");
transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
@@ -430,7 +430,7 @@ netbsd_cancel_transfer(struct usbi_transfer *itransfer)
{
UNUSED(itransfer);
- usbi_dbg(" ");
+ usbi_dbg(ITRANSFER_CTX(itransfer), " ");
return (LIBUSB_ERROR_NOT_SUPPORTED);
}
@@ -458,7 +458,7 @@ _errno_to_libusb(int err)
return (LIBUSB_ERROR_TIMEOUT);
}
- usbi_dbg("error: %s", strerror(err));
+ usbi_dbg(NULL, "error: %s", strerror(err));
return (LIBUSB_ERROR_OTHER);
}
@@ -472,14 +472,14 @@ _cache_active_config_descriptor(struct libusb_device *dev, int fd)
void *buf;
int len;
- usbi_dbg("fd %d", fd);
+ usbi_dbg(DEVICE_CTX(dev), "fd %d", fd);
ucd.ucd_config_index = USB_CURRENT_CONFIG_INDEX;
if ((ioctl(fd, USB_GET_CONFIG_DESC, &ucd)) < 0)
return _errno_to_libusb(errno);
- usbi_dbg("active bLength %d", ucd.ucd_desc.bLength);
+ usbi_dbg(DEVICE_CTX(dev), "active bLength %d", ucd.ucd_desc.bLength);
len = UGETW(ucd.ucd_desc.wTotalLength);
buf = malloc((size_t)len);
@@ -490,7 +490,7 @@ _cache_active_config_descriptor(struct libusb_device *dev, int fd)
ufd.ufd_size = len;
ufd.ufd_data = buf;
- usbi_dbg("index %d, len %d", ufd.ufd_config_index, len);
+ usbi_dbg(DEVICE_CTX(dev), "index %d, len %d", ufd.ufd_config_index, len);
if ((ioctl(fd, USB_GET_FULL_DESC, &ufd)) < 0) {
free(buf);
@@ -516,7 +516,7 @@ _sync_control_transfer(struct usbi_transfer *itransfer)
dpriv = usbi_get_device_priv(transfer->dev_handle->dev);
setup = (struct libusb_control_setup *)transfer->buffer;
- usbi_dbg("type %d request %d value %d index %d length %d timeout %d",
+ usbi_dbg(ITRANSFER_CTX(itransfer), "type %d request %d value %d index %d length %d timeout %d",
setup->bmRequestType, setup->bRequest,
libusb_le16_to_cpu(setup->wValue),
libusb_le16_to_cpu(setup->wIndex),
@@ -541,7 +541,7 @@ _sync_control_transfer(struct usbi_transfer *itransfer)
itransfer->transferred = req.ucr_actlen;
- usbi_dbg("transferred %d", itransfer->transferred);
+ usbi_dbg(ITRANSFER_CTX(itransfer), "transferred %d", itransfer->transferred);
return (0);
}
@@ -561,7 +561,7 @@ _access_endpoint(struct libusb_transfer *transfer)
endpt = UE_GET_ADDR(transfer->endpoint);
mode = IS_XFERIN(transfer) ? O_RDONLY : O_WRONLY;
- usbi_dbg("endpoint %d mode %d", endpt, mode);
+ usbi_dbg(TRANSFER_CTX(transfer), "endpoint %d mode %d", endpt, mode);
if (hpriv->endpoints[endpt] < 0) {
/* Pick the right node given the control one */
diff --git a/libusb/os/openbsd_usb.c b/libusb/os/openbsd_usb.c
index e05610e..9a5c604 100644
--- a/libusb/os/openbsd_usb.c
+++ b/libusb/os/openbsd_usb.c
@@ -129,7 +129,7 @@ obsd_get_device_list(struct libusb_context * ctx,
char *udevname;
int fd, addr, i, j;
- usbi_dbg(" ");
+ usbi_dbg(ctx, " ");
for (i = 0; i < 8; i++) {
snprintf(busnode, sizeof(busnode), USBDEV "%d", i);
@@ -238,7 +238,7 @@ obsd_open(struct libusb_device_handle *handle)
return _errno_to_libusb(errno);
dpriv->fd = fd;
- usbi_dbg("open %s: fd %d", devnode, dpriv->fd);
+ usbi_dbg(HANDLE_CTX(handle), "open %s: fd %d", devnode, dpriv->fd);
}
return (LIBUSB_SUCCESS);
@@ -250,7 +250,7 @@ obsd_close(struct libusb_device_handle *handle)
struct device_priv *dpriv = usbi_get_device_priv(handle->dev);
if (dpriv->devname) {
- usbi_dbg("close: fd %d", dpriv->fd);
+ usbi_dbg(HANDLE_CTX(handle), "close: fd %d", dpriv->fd);
close(dpriv->fd);
dpriv->fd = -1;
@@ -265,7 +265,7 @@ obsd_get_active_config_descriptor(struct libusb_device *dev,
len = MIN(len, (size_t)UGETW(dpriv->cdesc->wTotalLength));
- usbi_dbg("len %zu", len);
+ usbi_dbg(DEVICE_CTX(dev), "len %zu", len);
memcpy(buf, dpriv->cdesc, len);
@@ -288,7 +288,7 @@ obsd_get_config_descriptor(struct libusb_device *dev, uint8_t idx,
udf.udf_size = len;
udf.udf_data = buf;
- usbi_dbg("index %d, len %zu", udf.udf_config_index, len);
+ usbi_dbg(DEVICE_CTX(dev), "index %d, len %zu", udf.udf_config_index, len);
if (ioctl(fd, USB_DEVICE_GET_FDESC, &udf) < 0) {
err = errno;
@@ -307,7 +307,7 @@ obsd_get_configuration(struct libusb_device_handle *handle, uint8_t *config)
*config = dpriv->cdesc->bConfigurationValue;
- usbi_dbg("bConfigurationValue %u", *config);
+ usbi_dbg(HANDLE_CTX(handle), "bConfigurationValue %u", *config);
return (LIBUSB_SUCCESS);
}
@@ -320,7 +320,7 @@ obsd_set_configuration(struct libusb_device_handle *handle, int config)
if (dpriv->devname == NULL)
return (LIBUSB_ERROR_NOT_SUPPORTED);
- usbi_dbg("bConfigurationValue %d", config);
+ usbi_dbg(HANDLE_CTX(handle), "bConfigurationValue %d", config);
if (ioctl(dpriv->fd, USB_SET_CONFIG, &config) < 0)
return _errno_to_libusb(errno);
@@ -367,7 +367,7 @@ obsd_set_interface_altsetting(struct libusb_device_handle *handle, uint8_t iface
if (dpriv->devname == NULL)
return (LIBUSB_ERROR_NOT_SUPPORTED);
- usbi_dbg("iface %u, setting %u", iface, altsetting);
+ usbi_dbg(HANDLE_CTX(handle), "iface %u, setting %u", iface, altsetting);
memset(&intf, 0, sizeof(intf));
@@ -389,7 +389,7 @@ obsd_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint)
if ((fd = _bus_open(handle->dev->bus_number)) < 0)
return _errno_to_libusb(errno);
- usbi_dbg(" ");
+ usbi_dbg(HANDLE_CTX(handle), " ");
req.ucr_addr = handle->dev->device_address;
req.ucr_request.bmRequestType = UT_WRITE_ENDPOINT;
@@ -413,7 +413,7 @@ obsd_destroy_device(struct libusb_device *dev)
{
struct device_priv *dpriv = usbi_get_device_priv(dev);
- usbi_dbg(" ");
+ usbi_dbg(DEVICE_CTX(dev), " ");
free(dpriv->cdesc);
free(dpriv->devname);
@@ -425,7 +425,7 @@ obsd_submit_transfer(struct usbi_transfer *itransfer)
struct libusb_transfer *transfer;
int err = 0;
- usbi_dbg(" ");
+ usbi_dbg(ITRANSFER_CTX(itransfer), " ");
transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
@@ -468,7 +468,7 @@ obsd_cancel_transfer(struct usbi_transfer *itransfer)
{
UNUSED(itransfer);
- usbi_dbg(" ");
+ usbi_dbg(ITRANSFER_CTX(itransfer), " ");
return (LIBUSB_ERROR_NOT_SUPPORTED);
}
@@ -482,7 +482,7 @@ obsd_handle_transfer_completion(struct usbi_transfer *itransfer)
int
_errno_to_libusb(int err)
{
- usbi_dbg("error: %s (%d)", strerror(err), err);
+ usbi_dbg(NULL, "error: %s (%d)", strerror(err), err);
switch (err) {
case EIO:
@@ -512,7 +512,7 @@ _cache_active_config_descriptor(struct libusb_device *dev)
if ((fd = _bus_open(dev->bus_number)) < 0)
return _errno_to_libusb(errno);
- usbi_dbg("fd %d, addr %d", fd, dev->device_address);
+ usbi_dbg(DEVICE_CTX(dev), "fd %d, addr %d", fd, dev->device_address);
udc.udc_bus = dev->bus_number;
udc.udc_addr = dev->device_address;
@@ -523,7 +523,7 @@ _cache_active_config_descriptor(struct libusb_device *dev)
return _errno_to_libusb(errno);
}
- usbi_dbg("active bLength %d", udc.udc_desc.bLength);
+ usbi_dbg(DEVICE_CTX(dev), "active bLength %d", udc.udc_desc.bLength);
len = UGETW(udc.udc_desc.wTotalLength);
buf = malloc((size_t)len);
@@ -536,7 +536,7 @@ _cache_active_config_descriptor(struct libusb_device *dev)
udf.udf_size = len;
udf.udf_data = buf;
- usbi_dbg("index %d, len %d", udf.udf_config_index, len);
+ usbi_dbg(DEVICE_CTX(dev), "index %d, len %d", udf.udf_config_index, len);
if (ioctl(fd, USB_DEVICE_GET_FDESC, &udf) < 0) {
err = errno;
@@ -565,7 +565,7 @@ _sync_control_transfer(struct usbi_transfer *itransfer)
dpriv = usbi_get_device_priv(transfer->dev_handle->dev);
setup = (struct libusb_control_setup *)transfer->buffer;
- usbi_dbg("type %x request %x value %x index %d length %d timeout %d",
+ usbi_dbg(ITRANSFER_CTX(itransfer), "type 0x%x request 0x%x value 0x%x index %d length %d timeout %d",
setup->bmRequestType, setup->bRequest,
libusb_le16_to_cpu(setup->wValue),
libusb_le16_to_cpu(setup->wIndex),
@@ -610,7 +610,7 @@ _sync_control_transfer(struct usbi_transfer *itransfer)
itransfer->transferred = req.ucr_actlen;
- usbi_dbg("transferred %d", itransfer->transferred);
+ usbi_dbg(ITRANSFER_CTX(itransfer), "transferred %d", itransfer->transferred);
return (0);
}
@@ -630,7 +630,7 @@ _access_endpoint(struct libusb_transfer *transfer)
endpt = UE_GET_ADDR(transfer->endpoint);
mode = IS_XFERIN(transfer) ? O_RDONLY : O_WRONLY;
- usbi_dbg("endpoint %d mode %d", endpt, mode);
+ usbi_dbg(TRANSFER_CTX(transfer), "endpoint %d mode %d", endpt, mode);
if (hpriv->endpoints[endpt] < 0) {
/* Pick the right endpoint node */
diff --git a/libusb/os/sunos_usb.c b/libusb/os/sunos_usb.c
index 46866fb..28b167f 100644
--- a/libusb/os/sunos_usb.c
+++ b/libusb/os/sunos_usb.c
@@ -90,7 +90,7 @@ static int sunos_get_link(di_devlink_t devlink, void *arg)
char *content = (char *)di_devlink_content(devlink);
char *start = strstr(content, "/devices/");
start += strlen("/devices");
- usbi_dbg("%s", start);
+ usbi_dbg(NULL, "%s", start);
/* line content must have minor node */
if (start == NULL ||
@@ -101,7 +101,7 @@ static int sunos_get_link(di_devlink_t devlink, void *arg)
p = di_devlink_path(devlink);
q = strrchr(p, '/');
- usbi_dbg("%s", q);
+ usbi_dbg(NULL, "%s", q);
*(larg->linkpp) = strndup(p, strlen(p) - strlen(q));
@@ -118,7 +118,7 @@ static int sunos_physpath_to_devlink(
*link_path = NULL;
larg.linkpp = link_path;
if ((hdl = di_devlink_init(NULL, 0)) == NULL) {
- usbi_dbg("di_devlink_init failure");
+ usbi_dbg(NULL, "di_devlink_init failure");
return (-1);
}
@@ -131,7 +131,7 @@ static int sunos_physpath_to_devlink(
(void) di_devlink_fini(&hdl);
if (*link_path == NULL) {
- usbi_dbg("there is no devlink for this path");
+ usbi_dbg(NULL, "there is no devlink for this path");
return (-1);
}
@@ -167,13 +167,13 @@ sunos_usb_ioctl(struct libusb_device *dev, int cmd)
return (-1);
}
end++;
- usbi_dbg("unitaddr: %s", end);
+ usbi_dbg(DEVICE_CTX(dev), "unitaddr: %s", end);
nvlist_alloc(&nvlist, NV_UNIQUE_NAME_TYPE, KM_NOSLEEP);
nvlist_add_int32(nvlist, "port", dev->port_number);
//find the hub path
snprintf(path_arg, sizeof(path_arg), "/devices%s:hubd", hubpath);
- usbi_dbg("ioctl hub path: %s", path_arg);
+ usbi_dbg(DEVICE_CTX(dev), "ioctl hub path: %s", path_arg);
fd = open(path_arg, O_RDONLY);
if (fd < 0) {
@@ -193,15 +193,15 @@ sunos_usb_ioctl(struct libusb_device *dev, int cmd)
iocdata.c_nodename = (char *)"hub";
iocdata.c_unitaddr = end;
iocdata.cpyout_buf = &devctl_ap_state;
- usbi_dbg("%p, %" PRIuPTR, iocdata.nvl_user, iocdata.nvl_usersz);
+ usbi_dbg(DEVICE_CTX(dev), "%p, %" PRIuPTR, iocdata.nvl_user, iocdata.nvl_usersz);
errno = 0;
if (ioctl(fd, DEVCTL_AP_GETSTATE, &iocdata) == -1) {
usbi_err(DEVICE_CTX(dev), "ioctl failed: fd %d, cmd %x, errno %d (%s)",
fd, DEVCTL_AP_GETSTATE, errno, strerror(errno));
} else {
- usbi_dbg("dev rstate: %d", devctl_ap_state.ap_rstate);
- usbi_dbg("dev ostate: %d", devctl_ap_state.ap_ostate);
+ usbi_dbg(DEVICE_CTX(dev), "dev rstate: %d", devctl_ap_state.ap_rstate);
+ usbi_dbg(DEVICE_CTX(dev), "dev ostate: %d", devctl_ap_state.ap_ostate);
}
errno = 0;
@@ -227,7 +227,7 @@ sunos_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t inte
UNUSED(interface);
- usbi_dbg("%s", dpriv->ugenpath);
+ usbi_dbg(HANDLE_CTX(dev_handle), "%s", dpriv->ugenpath);
return (dpriv->ugenpath == NULL);
}
@@ -236,7 +236,7 @@ sunos_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t inte
* Private functions
*/
static int _errno_to_libusb(int);
-static int sunos_usb_get_status(int fd);
+static int sunos_usb_get_status(struct libusb_context *ctx, int fd);
static string_list_t *
sunos_new_string_list(void)
@@ -358,7 +358,7 @@ sunos_detach_kernel_driver(struct libusb_device_handle *dev_handle,
dpriv = usbi_get_device_priv(dev_handle->dev);
snprintf(path_arg, sizeof(path_arg), "\'\"%s\"\'", dpriv->phypath);
- usbi_dbg("%s", path_arg);
+ usbi_dbg(HANDLE_CTX(dev_handle), "%s", path_arg);
list = sunos_new_string_list();
if (list == NULL)
@@ -418,7 +418,7 @@ sunos_attach_kernel_driver(struct libusb_device_handle *dev_handle,
dpriv = usbi_get_device_priv(dev_handle->dev);
snprintf(path_arg, sizeof(path_arg), "\'\"%s\"\'", dpriv->phypath);
- usbi_dbg("%s", path_arg);
+ usbi_dbg(HANDLE_CTX(dev_handle), "%s", path_arg);
list = sunos_new_string_list();
if (list == NULL)
@@ -474,7 +474,7 @@ sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev)
proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
"usb-raw-cfg-descriptors", &rdata);
if (proplen <= 0) {
- usbi_dbg("can't find raw config descriptors");
+ usbi_dbg(DEVICE_CTX(dev), "can't find raw config descriptors");
return (LIBUSB_ERROR_IO);
}
@@ -501,7 +501,7 @@ sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev)
snprintf(match_str, sizeof(match_str), "^usb/%x.%x",
dev->device_descriptor.idVendor,
dev->device_descriptor.idProduct);
- usbi_dbg("match is %s", match_str);
+ usbi_dbg(DEVICE_CTX(dev), "match is %s", match_str);
sunos_physpath_to_devlink(dpriv->phypath, match_str, &dpriv->ugenpath);
di_devfs_path_free(phypath);
@@ -514,7 +514,7 @@ sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev)
/* address */
n = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "assigned-address", &addr);
if (n != 1 || *addr == 0) {
- usbi_dbg("can't get address");
+ usbi_dbg(DEVICE_CTX(dev), "can't get address");
} else {
dev->device_address = *addr;
}
@@ -530,7 +530,7 @@ sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev)
dev->speed = LIBUSB_SPEED_SUPER;
}
- usbi_dbg("vid=%x pid=%x, path=%s, bus_nmber=0x%x, port_number=%d, speed=%d",
+ usbi_dbg(DEVICE_CTX(dev), "vid=%x pid=%x, path=%s, bus_nmber=0x%x, port_number=%d, speed=%d",
dev->device_descriptor.idVendor, dev->device_descriptor.idProduct,
dpriv->phypath, dev->bus_number, dev->port_number, dev->speed);
@@ -571,7 +571,7 @@ sunos_add_devices(di_devlink_t link, void *arg)
dn = myself;
/* find the root hub */
while (di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "root-hub", &j) != 0) {
- usbi_dbg("find_root_hub:%s", di_devfs_path(dn));
+ usbi_dbg(NULL, "find_root_hub:%s", di_devfs_path(dn));
n = di_prop_lookup_ints(DDI_DEV_T_ANY, dn,
"assigned-address", &addr_prop);
session_id |= ((addr_prop[0] & 0xff) << i++ * 8);
@@ -586,13 +586,13 @@ sunos_add_devices(di_devlink_t link, void *arg)
session_id |= (bdf << i * 8);
bus_number = (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg);
- usbi_dbg("device bus address=%s:%x, name:%s",
+ usbi_dbg(NULL, "device bus address=%s:%x, name:%s",
di_bus_addr(myself), bus_number, di_node_name(dn));
- usbi_dbg("session id org:%" PRIx64, session_id);
+ usbi_dbg(NULL, "session id org:%" PRIx64, session_id);
/* dn is the usb device */
for (dn = di_child_node(myself); dn != DI_NODE_NIL; dn = di_sibling_node(dn)) {
- usbi_dbg("device path:%s", di_devfs_path(dn));
+ usbi_dbg(NULL, "device path:%s", di_devfs_path(dn));
/* skip hub devices, because its driver can not been unload */
if (di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "usb-port-count", &addr_prop) != -1)
continue;
@@ -600,18 +600,18 @@ sunos_add_devices(di_devlink_t link, void *arg)
n = di_prop_lookup_ints(DDI_DEV_T_ANY, dn,
"assigned-address", &addr_prop);
if ((n != 1) || (addr_prop[0] == 0)) {
- usbi_dbg("cannot get valid usb_addr");
+ usbi_dbg(NULL, "cannot get valid usb_addr");
continue;
}
sid = (session_id << 8) | (addr_prop[0] & 0xff) ;
- usbi_dbg("session id %" PRIX64, sid);
+ usbi_dbg(NULL, "session id %" PRIX64, sid);
dev = usbi_get_device_by_session_id(nargs->ctx, sid);
if (dev == NULL) {
dev = usbi_alloc_device(nargs->ctx, sid);
if (dev == NULL) {
- usbi_dbg("can't alloc device");
+ usbi_dbg(NULL, "can't alloc device");
continue;
}
devpriv = usbi_get_device_priv(dev);
@@ -619,21 +619,21 @@ sunos_add_devices(di_devlink_t link, void *arg)
if (sunos_fill_in_dev_info(dn, dev) != LIBUSB_SUCCESS) {
libusb_unref_device(dev);
- usbi_dbg("get information fail");
+ usbi_dbg(NULL, "get information fail");
continue;
}
if (usbi_sanitize_device(dev) < 0) {
libusb_unref_device(dev);
- usbi_dbg("sanatize failed: ");
+ usbi_dbg(NULL, "sanatize failed: ");
return (DI_WALK_TERMINATE);
}
} else {
devpriv = usbi_get_device_priv(dev);
- usbi_dbg("Dev %s exists", devpriv->ugenpath);
+ usbi_dbg(NULL, "Dev %s exists", devpriv->ugenpath);
}
if (discovered_devs_append(*(nargs->discdevs), dev) == NULL) {
- usbi_dbg("cannot append device");
+ usbi_dbg(NULL, "cannot append device");
}
/*
@@ -642,7 +642,7 @@ sunos_add_devices(di_devlink_t link, void *arg)
*/
libusb_unref_device(dev);
- usbi_dbg("Device %s %s id=0x%" PRIx64 ", devcount:%" PRIuPTR
+ usbi_dbg(NULL, "Device %s %s id=0x%" PRIx64 ", devcount:%" PRIuPTR
", bdf=%" PRIx64,
devpriv->ugenpath, di_devfs_path(dn), (uint64_t)sid,
(*nargs->discdevs)->len, bdf);
@@ -690,13 +690,13 @@ sunos_get_device_list(struct libusb_context * ctx,
args.discdevs = discdevs;
args.last_ugenpath = NULL;
if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
- usbi_dbg("di_int() failed: errno %d (%s)", errno, strerror(errno));
+ usbi_dbg(ctx, "di_int() failed: errno %d (%s)", errno, strerror(errno));
return (LIBUSB_ERROR_IO);
}
if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
di_fini(root_node);
- usbi_dbg("di_devlink_init() failed: errno %d (%s)", errno, strerror(errno));
+ usbi_dbg(ctx, "di_devlink_init() failed: errno %d (%s)", errno, strerror(errno));
return (LIBUSB_ERROR_IO);
}
@@ -705,7 +705,7 @@ sunos_get_device_list(struct libusb_context * ctx,
/* walk each node to find USB devices */
if (di_walk_node(root_node, DI_WALK_SIBFIRST, &args,
sunos_walk_minor_node_link) == -1) {
- usbi_dbg("di_walk_node() failed: errno %d (%s)", errno, strerror(errno));
+ usbi_dbg(ctx, "di_walk_node() failed: errno %d (%s)", errno, strerror(errno));
di_fini(root_node);
return (LIBUSB_ERROR_IO);
@@ -714,7 +714,7 @@ sunos_get_device_list(struct libusb_context * ctx,
di_fini(root_node);
di_devlink_fini(&devlink_hdl);
- usbi_dbg("%zu devices", (*discdevs)->len);
+ usbi_dbg(ctx, "%zu devices", (*discdevs)->len);
return ((*discdevs)->len);
}
@@ -729,7 +729,7 @@ sunos_usb_open_ep0(sunos_dev_handle_priv_t *hpriv, sunos_dev_priv_t *dpriv)
}
snprintf(filename, PATH_MAX, "%s/cntrl0", dpriv->ugenpath);
- usbi_dbg("opening %s", filename);
+ usbi_dbg(NULL, "opening %s", filename);
hpriv->eps[0].datafd = open(filename, O_RDWR);
if (hpriv->eps[0].datafd < 0) {
return(_errno_to_libusb(errno));
@@ -836,20 +836,20 @@ sunos_check_device_and_status_open(struct libusb_device_handle *hdl,
uint8_t ep_index;
sunos_dev_handle_priv_t *hpriv;
- usbi_dbg("open ep 0x%02x", ep_addr);
+ usbi_dbg(HANDLE_CTX(hdl), "open ep 0x%02x", ep_addr);
hpriv = usbi_get_device_handle_priv(hdl);
ep_index = sunos_usb_ep_index(ep_addr);
/* ep already opened */
if ((hpriv->eps[ep_index].datafd > 0) &&
(hpriv->eps[ep_index].statfd > 0)) {
- usbi_dbg("ep 0x%02x already opened, return success",
+ usbi_dbg(HANDLE_CTX(hdl), "ep 0x%02x already opened, return success",
ep_addr);
return (0);
}
if (sunos_find_interface(hdl, ep_addr, &ifc) < 0) {
- usbi_dbg("can't find interface for endpoint 0x%02x",
+ usbi_dbg(HANDLE_CTX(hdl), "can't find interface for endpoint 0x%02x",
ep_addr);
return (EACCES);
@@ -896,7 +896,7 @@ sunos_check_device_and_status_open(struct libusb_device_handle *hdl,
}
/* Open the xfer endpoint first */
if ((fd = open(filename, mode)) == -1) {
- usbi_dbg("can't open %s: errno %d (%s)", filename, errno,
+ usbi_dbg(HANDLE_CTX(hdl), "can't open %s: errno %d (%s)", filename, errno,
strerror(errno));
return (errno);
@@ -917,7 +917,7 @@ sunos_check_device_and_status_open(struct libusb_device_handle *hdl,
/* Open the status endpoint with RDWR */
if ((fdstat = open(statfilename, O_RDWR)) == -1) {
- usbi_dbg("can't open %s RDWR: errno %d (%s)",
+ usbi_dbg(HANDLE_CTX(hdl), "can't open %s RDWR: errno %d (%s)",
statfilename, errno, strerror(errno));
return (errno);
@@ -925,7 +925,7 @@ sunos_check_device_and_status_open(struct libusb_device_handle *hdl,
count = write(fdstat, &control, sizeof(control));
if (count != 1) {
/* this should have worked */
- usbi_dbg("can't write to %s: errno %d (%s)",
+ usbi_dbg(HANDLE_CTX(hdl), "can't write to %s: errno %d (%s)",
statfilename, errno, strerror(errno));
(void) close(fdstat);
@@ -934,7 +934,7 @@ sunos_check_device_and_status_open(struct libusb_device_handle *hdl,
}
} else {
if ((fdstat = open(statfilename, O_RDONLY)) == -1) {
- usbi_dbg("can't open %s: errno %d (%s)", statfilename, errno,
+ usbi_dbg(HANDLE_CTX(hdl), "can't open %s: errno %d (%s)", statfilename, errno,
strerror(errno));
return (errno);
@@ -943,7 +943,7 @@ sunos_check_device_and_status_open(struct libusb_device_handle *hdl,
/* Re-open the xfer endpoint */
if ((fd = open(filename, mode)) == -1) {
- usbi_dbg("can't open %s: errno %d (%s)", filename, errno,
+ usbi_dbg(HANDLE_CTX(hdl), "can't open %s: errno %d (%s)", filename, errno,
strerror(errno));
(void) close(fdstat);
@@ -952,7 +952,7 @@ sunos_check_device_and_status_open(struct libusb_device_handle *hdl,
hpriv->eps[ep_index].datafd = fd;
hpriv->eps[ep_index].statfd = fdstat;
- usbi_dbg("ep=0x%02x datafd=%d, statfd=%d", ep_addr, fd, fdstat);
+ usbi_dbg(HANDLE_CTX(hdl), "ep=0x%02x datafd=%d, statfd=%d", ep_addr, fd, fdstat);
return (0);
}
@@ -981,7 +981,7 @@ sunos_open(struct libusb_device_handle *handle)
}
if ((ret = sunos_usb_open_ep0(hpriv, dpriv)) != LIBUSB_SUCCESS) {
- usbi_dbg("fail: %d", ret);
+ usbi_dbg(HANDLE_CTX(handle), "fail: %d", ret);
return (ret);
}
@@ -993,7 +993,7 @@ sunos_close(struct libusb_device_handle *handle)
{
sunos_dev_handle_priv_t *hpriv;
- usbi_dbg(" ");
+ usbi_dbg(HANDLE_CTX(handle), " ");
hpriv = usbi_get_device_handle_priv(handle);
@@ -1016,14 +1016,14 @@ sunos_get_active_config_descriptor(struct libusb_device *dev,
* has ever been changed through setCfg.
*/
if ((node = di_init(dpriv->phypath, DINFOCPYALL)) == DI_NODE_NIL) {
- usbi_dbg("di_int() failed: errno %d (%s)", errno,
+ usbi_dbg(DEVICE_CTX(dev), "di_int() failed: errno %d (%s)", errno,
strerror(errno));
return (LIBUSB_ERROR_IO);
}
proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
"usb-raw-cfg-descriptors", &rdata);
if (proplen <= 0) {
- usbi_dbg("can't find raw config descriptors");
+ usbi_dbg(DEVICE_CTX(dev), "can't find raw config descriptors");
return (LIBUSB_ERROR_IO);
}
@@ -1040,7 +1040,7 @@ sunos_get_active_config_descriptor(struct libusb_device *dev,
cfg = (struct libusb_config_descriptor *)dpriv->raw_cfgdescr;
len = MIN(len, libusb_le16_to_cpu(cfg->wTotalLength));
memcpy(buf, dpriv->raw_cfgdescr, len);
- usbi_dbg("path:%s len %zu", dpriv->phypath, len);
+ usbi_dbg(DEVICE_CTX(dev), "path:%s len %zu", dpriv->phypath, len);
return (len);
}
@@ -1061,7 +1061,7 @@ sunos_get_configuration(struct libusb_device_handle *handle, uint8_t *config)
*config = dpriv->cfgvalue;
- usbi_dbg("bConfigurationValue %u", *config);
+ usbi_dbg(HANDLE_CTX(handle), "bConfigurationValue %u", *config);
return (LIBUSB_SUCCESS);
}
@@ -1072,7 +1072,7 @@ sunos_set_configuration(struct libusb_device_handle *handle, int config)
sunos_dev_priv_t *dpriv = usbi_get_device_priv(handle->dev);
sunos_dev_handle_priv_t *hpriv;
- usbi_dbg("bConfigurationValue %d", config);
+ usbi_dbg(HANDLE_CTX(handle), "bConfigurationValue %d", config);
hpriv = usbi_get_device_handle_priv(handle);
if (dpriv->ugenpath == NULL)
@@ -1092,7 +1092,7 @@ sunos_claim_interface(struct libusb_device_handle *handle, uint8_t iface)
{
UNUSED(handle);
- usbi_dbg("iface %u", iface);
+ usbi_dbg(HANDLE_CTX(handle), "iface %u", iface);
return (LIBUSB_SUCCESS);
}
@@ -1102,7 +1102,7 @@ sunos_release_interface(struct libusb_device_handle *handle, uint8_t iface)
{
sunos_dev_handle_priv_t *hpriv = usbi_get_device_handle_priv(handle);
- usbi_dbg("iface %u", iface);
+ usbi_dbg(HANDLE_CTX(handle), "iface %u", iface);
/* XXX: can we release it? */
hpriv->altsetting[iface] = 0;
@@ -1117,7 +1117,7 @@ sunos_set_interface_altsetting(struct libusb_device_handle *handle, uint8_t ifac
sunos_dev_priv_t *dpriv = usbi_get_device_priv(handle->dev);
sunos_dev_handle_priv_t *hpriv = usbi_get_device_handle_priv(handle);
- usbi_dbg("iface %u, setting %u", iface, altsetting);
+ usbi_dbg(HANDLE_CTX(handle), "iface %u, setting %u", iface, altsetting);
if (dpriv->ugenpath == NULL)
return (LIBUSB_ERROR_NOT_FOUND);
@@ -1169,7 +1169,7 @@ sunos_async_callback(union sigval arg)
ret = aio_error(aiocb);
if (ret != 0) {
- xfer->status = sunos_usb_get_status(hpriv->eps[ep].statfd);
+ xfer->status = sunos_usb_get_status(TRANSFER_CTX(xfer), hpriv->eps[ep].statfd);
} else {
xfer->actual_length =
LIBUSB_TRANSFER_TO_USBI_TRANSFER(xfer)->transferred =
@@ -1178,7 +1178,7 @@ sunos_async_callback(union sigval arg)
usb_dump_data(xfer->buffer, xfer->actual_length);
- usbi_dbg("ret=%d, len=%d, actual_len=%d", ret, xfer->length,
+ usbi_dbg(TRANSFER_CTX(xfer), "ret=%d, len=%d, actual_len=%d", ret, xfer->length,
xfer->actual_length);
/* async notification */
@@ -1195,7 +1195,7 @@ sunos_do_async_io(struct libusb_transfer *transfer)
uint8_t ep;
struct sunos_transfer_priv *tpriv;
- usbi_dbg(" ");
+ usbi_dbg(TRANSFER_CTX(transfer), " ");
tpriv = usbi_get_transfer_priv(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer));
hpriv = usbi_get_device_handle_priv(transfer->dev_handle);
@@ -1225,12 +1225,12 @@ sunos_do_async_io(struct libusb_transfer *transfer)
/* return the number of bytes read/written */
static ssize_t
-usb_do_io(int fd, int stat_fd, void *data, size_t size, int flag, int *status)
+usb_do_io(struct libusb_context *ctx, int fd, int stat_fd, void *data, size_t size, int flag, int *status)
{
int error;
ssize_t ret = -1;
- usbi_dbg("usb_do_io(): datafd=%d statfd=%d size=0x%zx flag=%s",
+ usbi_dbg(ctx, "usb_do_io(): datafd=%d statfd=%d size=0x%zx flag=%s",
fd, stat_fd, size, flag? "WRITE":"READ");
switch (flag) {
@@ -1246,17 +1246,17 @@ usb_do_io(int fd, int stat_fd, void *data, size_t size, int flag, int *status)
break;
}
- usbi_dbg("usb_do_io(): amount=%zd", ret);
+ usbi_dbg(ctx, "usb_do_io(): amount=%zd", ret);
if (ret < 0) {
int save_errno = errno;
- usbi_dbg("TID=%x io %s errno %d (%s)", pthread_self(),
+ usbi_dbg(ctx, "TID=%x io %s errno %d (%s)", pthread_self(),
flag?"WRITE":"READ", errno, strerror(errno));
/* sunos_usb_get_status will do a read and overwrite errno */
- error = sunos_usb_get_status(stat_fd);
- usbi_dbg("io status=%d errno %d (%s)", error,
+ error = sunos_usb_get_status(ctx, stat_fd);
+ usbi_dbg(ctx, "io status=%d errno %d (%s)", error,
save_errno, strerror(save_errno));
if (status) {
@@ -1286,26 +1286,26 @@ solaris_submit_ctrl_on_default(struct libusb_transfer *transfer)
wLength = transfer->length - LIBUSB_CONTROL_SETUP_SIZE;
if (hpriv->eps[0].datafd == -1) {
- usbi_dbg("ep0 not opened");
+ usbi_dbg(TRANSFER_CTX(transfer), "ep0 not opened");
return (LIBUSB_ERROR_NOT_FOUND);
}
if ((data[0] & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) {
- usbi_dbg("IN request");
- ret = usb_do_io(hpriv->eps[0].datafd,
+ usbi_dbg(TRANSFER_CTX(transfer), "IN request");
+ ret = usb_do_io(TRANSFER_CTX(transfer), hpriv->eps[0].datafd,
hpriv->eps[0].statfd, data, LIBUSB_CONTROL_SETUP_SIZE,
WRITE, &status);
} else {
- usbi_dbg("OUT request");
- ret = usb_do_io(hpriv->eps[0].datafd, hpriv->eps[0].statfd,
+ usbi_dbg(TRANSFER_CTX(transfer), "OUT request");
+ ret = usb_do_io(TRANSFER_CTX(transfer), hpriv->eps[0].datafd, hpriv->eps[0].statfd,
transfer->buffer, transfer->length, WRITE,
(int *)&transfer->status);
}
setup_ret = ret;
if (ret < (ssize_t)LIBUSB_CONTROL_SETUP_SIZE) {
- usbi_dbg("error sending control msg: %zd", ret);
+ usbi_dbg(TRANSFER_CTX(transfer), "error sending control msg: %zd", ret);
return (LIBUSB_ERROR_IO);
}
@@ -1315,8 +1315,8 @@ solaris_submit_ctrl_on_default(struct libusb_transfer *transfer)
/* Read the remaining bytes for IN request */
if ((wLength) && ((data[0] & LIBUSB_ENDPOINT_DIR_MASK) ==
LIBUSB_ENDPOINT_IN)) {
- usbi_dbg("DATA: %d", transfer->length - (int)setup_ret);
- ret = usb_do_io(hpriv->eps[0].datafd,
+ usbi_dbg(TRANSFER_CTX(transfer), "DATA: %d", transfer->length - (int)setup_ret);
+ ret = usb_do_io(TRANSFER_CTX(transfer), hpriv->eps[0].datafd,
hpriv->eps[0].statfd,
transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE,
wLength, READ, (int *)&transfer->status);
@@ -1325,7 +1325,7 @@ solaris_submit_ctrl_on_default(struct libusb_transfer *transfer)
if (ret >= 0) {
LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer)->transferred = ret;
}
- usbi_dbg("Done: ctrl data bytes %zd", ret);
+ usbi_dbg(TRANSFER_CTX(transfer), "Done: ctrl data bytes %zd", ret);
/**
* Sync transfer handling.
@@ -1345,13 +1345,13 @@ sunos_clear_halt(struct libusb_device_handle *handle, unsigned char endpoint)
{
int ret;
- usbi_dbg("endpoint=0x%02x", endpoint);
+ usbi_dbg(HANDLE_CTX(handle), "endpoint=0x%02x", endpoint);
ret = libusb_control_transfer(handle, LIBUSB_ENDPOINT_OUT |
LIBUSB_RECIPIENT_ENDPOINT | LIBUSB_REQUEST_TYPE_STANDARD,
LIBUSB_REQUEST_CLEAR_FEATURE, 0, endpoint, NULL, 0, 1000);
- usbi_dbg("ret=%d", ret);
+ usbi_dbg(HANDLE_CTX(handle), "ret=%d", ret);
return (ret);
}
@@ -1361,7 +1361,7 @@ sunos_destroy_device(struct libusb_device *dev)
{
sunos_dev_priv_t *dpriv = usbi_get_device_priv(dev);
- usbi_dbg("destroy everything");
+ usbi_dbg(DEVICE_CTX(dev), "destroy everything");
free(dpriv->raw_cfgdescr);
free(dpriv->ugenpath);
free(dpriv->phypath);
@@ -1387,7 +1387,7 @@ sunos_submit_transfer(struct usbi_transfer *itransfer)
switch (transfer->type) {
case LIBUSB_TRANSFER_TYPE_CONTROL:
/* sync transfer */
- usbi_dbg("CTRL transfer: %d", transfer->length);
+ usbi_dbg(ITRANSFER_CTX(itransfer), "CTRL transfer: %d", transfer->length);
err = solaris_submit_ctrl_on_default(transfer);
break;
@@ -1395,9 +1395,9 @@ sunos_submit_transfer(struct usbi_transfer *itransfer)
/* fallthru */
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
if (transfer->type == LIBUSB_TRANSFER_TYPE_BULK)
- usbi_dbg("BULK transfer: %d", transfer->length);
+ usbi_dbg(ITRANSFER_CTX(itransfer), "BULK transfer: %d", transfer->length);
else
- usbi_dbg("INTR transfer: %d", transfer->length);
+ usbi_dbg(ITRANSFER_CTX(itransfer), "INTR transfer: %d", transfer->length);
err = sunos_do_async_io(transfer);
break;
@@ -1407,9 +1407,9 @@ sunos_submit_transfer(struct usbi_transfer *itransfer)
/* fallthru */
case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
- usbi_dbg("ISOC transfer: %d", transfer->length);
+ usbi_dbg(ITRANSFER_CTX(itransfer), "ISOC transfer: %d", transfer->length);
else
- usbi_dbg("BULK STREAM transfer: %d", transfer->length);
+ usbi_dbg(ITRANSFER_CTX(itransfer), "BULK STREAM transfer: %d", transfer->length);
err = LIBUSB_ERROR_NOT_SUPPORTED;
break;
}
@@ -1435,7 +1435,7 @@ sunos_cancel_transfer(struct usbi_transfer *itransfer)
ret = aio_cancel(hpriv->eps[ep].datafd, aiocb);
- usbi_dbg("aio->fd=%d fd=%d ret = %d, %s", aiocb->aio_fildes,
+ usbi_dbg(ITRANSFER_CTX(itransfer), "aio->fd=%d fd=%d ret = %d, %s", aiocb->aio_fildes,
hpriv->eps[ep].datafd, ret, (ret == AIO_CANCELED)?
strerror(0):strerror(errno));
@@ -1461,7 +1461,7 @@ sunos_handle_transfer_completion(struct usbi_transfer *itransfer)
int
_errno_to_libusb(int err)
{
- usbi_dbg("error: %s (%d)", strerror(err), err);
+ usbi_dbg(NULL, "error: %s (%d)", strerror(err), err);
switch (err) {
case EIO:
@@ -1486,96 +1486,96 @@ _errno_to_libusb(int err)
* Returns: ugen's last cmd status
*/
static int
-sunos_usb_get_status(int fd)
+sunos_usb_get_status(struct libusb_context *ctx, int fd)
{
int status;
ssize_t ret;
- usbi_dbg("sunos_usb_get_status(): fd=%d", fd);
+ usbi_dbg(ctx, "sunos_usb_get_status(): fd=%d", fd);
ret = read(fd, &status, sizeof(status));
if (ret == sizeof(status)) {
switch (status) {
case USB_LC_STAT_NOERROR:
- usbi_dbg("No Error");
+ usbi_dbg(ctx, "No Error");
break;
case USB_LC_STAT_CRC:
- usbi_dbg("CRC Timeout Detected\n");
+ usbi_dbg(ctx, "CRC Timeout Detected\n");
break;
case USB_LC_STAT_BITSTUFFING:
- usbi_dbg("Bit Stuffing Violation\n");
+ usbi_dbg(ctx, "Bit Stuffing Violation\n");
break;
case USB_LC_STAT_DATA_TOGGLE_MM:
- usbi_dbg("Data Toggle Mismatch\n");
+ usbi_dbg(ctx, "Data Toggle Mismatch\n");
break;
case USB_LC_STAT_STALL:
- usbi_dbg("End Point Stalled\n");
+ usbi_dbg(ctx, "End Point Stalled\n");
break;
case USB_LC_STAT_DEV_NOT_RESP:
- usbi_dbg("Device is Not Responding\n");
+ usbi_dbg(ctx, "Device is Not Responding\n");
break;
case USB_LC_STAT_PID_CHECKFAILURE:
- usbi_dbg("PID Check Failure\n");
+ usbi_dbg(ctx, "PID Check Failure\n");
break;
case USB_LC_STAT_UNEXP_PID:
- usbi_dbg("Unexpected PID\n");
+ usbi_dbg(ctx, "Unexpected PID\n");
break;
case USB_LC_STAT_DATA_OVERRUN:
- usbi_dbg("Data Exceeded Size\n");
+ usbi_dbg(ctx, "Data Exceeded Size\n");
break;
case USB_LC_STAT_DATA_UNDERRUN:
- usbi_dbg("Less data received\n");
+ usbi_dbg(ctx, "Less data received\n");
break;
case USB_LC_STAT_BUFFER_OVERRUN:
- usbi_dbg("Buffer Size Exceeded\n");
+ usbi_dbg(ctx, "Buffer Size Exceeded\n");
break;
case USB_LC_STAT_BUFFER_UNDERRUN:
- usbi_dbg("Buffer Underrun\n");
+ usbi_dbg(ctx, "Buffer Underrun\n");
break;
case USB_LC_STAT_TIMEOUT:
- usbi_dbg("Command Timed Out\n");
+ usbi_dbg(ctx, "Command Timed Out\n");
break;
case USB_LC_STAT_NOT_ACCESSED:
- usbi_dbg("Not Accessed by h/w\n");
+ usbi_dbg(ctx, "Not Accessed by h/w\n");
break;
case USB_LC_STAT_UNSPECIFIED_ERR:
- usbi_dbg("Unspecified Error\n");
+ usbi_dbg(ctx, "Unspecified Error\n");
break;
case USB_LC_STAT_NO_BANDWIDTH:
- usbi_dbg("No Bandwidth\n");
+ usbi_dbg(ctx, "No Bandwidth\n");
break;
case USB_LC_STAT_HW_ERR:
- usbi_dbg("Host Controller h/w Error\n");
+ usbi_dbg(ctx, "Host Controller h/w Error\n");
break;
case USB_LC_STAT_SUSPENDED:
- usbi_dbg("Device was Suspended\n");
+ usbi_dbg(ctx, "Device was Suspended\n");
break;
case USB_LC_STAT_DISCONNECTED:
- usbi_dbg("Device was Disconnected\n");
+ usbi_dbg(ctx, "Device was Disconnected\n");
break;
case USB_LC_STAT_INTR_BUF_FULL:
- usbi_dbg("Interrupt buffer was full\n");
+ usbi_dbg(ctx, "Interrupt buffer was full\n");
break;
case USB_LC_STAT_INVALID_REQ:
- usbi_dbg("Request was Invalid\n");
+ usbi_dbg(ctx, "Request was Invalid\n");
break;
case USB_LC_STAT_INTERRUPTED:
- usbi_dbg("Request was Interrupted\n");
+ usbi_dbg(ctx, "Request was Interrupted\n");
break;
case USB_LC_STAT_NO_RESOURCES:
- usbi_dbg("No resources available for "
+ usbi_dbg(ctx, "No resources available for "
"request\n");
break;
case USB_LC_STAT_INTR_POLLING_FAILED:
- usbi_dbg("Failed to Restart Poll");
+ usbi_dbg(ctx, "Failed to Restart Poll");
break;
default:
- usbi_dbg("Error Not Determined %d\n",
+ usbi_dbg(ctx, "Error Not Determined %d\n",
status);
break;
}
} else {
- usbi_dbg("read stat error: %s",strerror(errno));
+ usbi_dbg(ctx, "read stat error: %s",strerror(errno));
status = -1;
}
diff --git a/libusb/os/windows_common.c b/libusb/os/windows_common.c
index 119ed49..24ac095 100644
--- a/libusb/os/windows_common.c
+++ b/libusb/os/windows_common.c
@@ -24,7 +24,6 @@
#include <config.h>
-#include <process.h>
#include <stdio.h>
#include "libusbi.h"
@@ -153,7 +152,7 @@ static bool htab_create(struct libusb_context *ctx)
// Create a mutex
usbi_mutex_init(&htab_mutex);
- usbi_dbg("using %lu entries hash table", HTAB_SIZE);
+ usbi_dbg(ctx, "using %lu entries hash table", HTAB_SIZE);
htab_filled = 0;
// allocate memory and zero out.
@@ -222,7 +221,7 @@ unsigned long htab_hash(const char *str)
if ((htab_table[idx].used == hval) && (strcmp(str, htab_table[idx].str) == 0))
goto out_unlock; // existing hash
- usbi_dbg("hash collision ('%s' vs '%s')", str, htab_table[idx].str);
+ usbi_dbg(NULL, "hash collision ('%s' vs '%s')", str, htab_table[idx].str);
// Second hash function, as suggested in [Knuth]
hval2 = 1UL + hval % (HTAB_SIZE - 2);
@@ -284,7 +283,7 @@ enum libusb_transfer_status usbd_status_to_libusb_transfer_status(USBD_STATUS st
case USBD_STATUS_DEVICE_GONE:
return LIBUSB_TRANSFER_NO_DEVICE;
default:
- usbi_dbg("USBD_STATUS 0x%08lx translated to LIBUSB_TRANSFER_ERROR", ULONG_CAST(status));
+ usbi_dbg(NULL, "USBD_STATUS 0x%08lx translated to LIBUSB_TRANSFER_ERROR", ULONG_CAST(status));
return LIBUSB_TRANSFER_ERROR;
}
}
@@ -294,15 +293,18 @@ enum libusb_transfer_status usbd_status_to_libusb_transfer_status(USBD_STATUS st
*/
void windows_force_sync_completion(struct usbi_transfer *itransfer, ULONG size)
{
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct windows_context_priv *priv = usbi_get_context_priv(TRANSFER_CTX(transfer));
struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer);
OVERLAPPED *overlapped = &transfer_priv->overlapped;
- usbi_dbg("transfer %p, length %lu", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer), ULONG_CAST(size));
+ usbi_dbg(TRANSFER_CTX(transfer), "transfer %p, length %lu", transfer, ULONG_CAST(size));
overlapped->Internal = (ULONG_PTR)STATUS_SUCCESS;
overlapped->InternalHigh = (ULONG_PTR)size;
- usbi_signal_transfer_completion(itransfer);
+ if (!PostQueuedCompletionStatus(priv->completion_port, (DWORD)size, (ULONG_PTR)transfer->dev_handle, overlapped))
+ usbi_err(TRANSFER_CTX(transfer), "failed to post I/O completion: %s", windows_error_str(0));
}
/* Windows version detection */
@@ -344,6 +346,8 @@ static enum windows_version get_windows_version(void)
if ((vi.dwMajorVersion > 6) || ((vi.dwMajorVersion == 6) && (vi.dwMinorVersion >= 2))) {
// Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version
// See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
+ // And starting with Windows 10 Preview 2, Windows enforces the use of the application/supportedOS
+ // manifest in order for VerSetConditionMask() to report the ACTUAL OS major and minor...
major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
for (major = vi.dwMajorVersion; major <= 9; major++) {
@@ -379,6 +383,7 @@ static enum windows_version get_windows_version(void)
ws = (vi.wProductType <= VER_NT_WORKSTATION);
version = vi.dwMajorVersion << 4 | vi.dwMinorVersion;
+
switch (version) {
case 0x50: winver = WINDOWS_2000; w = "2000"; break;
case 0x51: winver = WINDOWS_XP; w = "XP"; break;
@@ -388,22 +393,30 @@ static enum windows_version get_windows_version(void)
case 0x62: winver = WINDOWS_8; w = (ws ? "8" : "2012"); break;
case 0x63: winver = WINDOWS_8_1; w = (ws ? "8.1" : "2012_R2"); break;
case 0x64: // Early Windows 10 Insider Previews and Windows Server 2017 Technical Preview 1 used version 6.4
- case 0xA0: winver = WINDOWS_10; w = (ws ? "10" : "2016"); break;
+ case 0xA0: winver = WINDOWS_10; w = (ws ? "10" : "2016");
+ if (vi.dwBuildNumber < 20000)
+ break;
+ // fallthrough
+ case 0xB0: winver = WINDOWS_11; w = (ws ? "11" : "2022"); break;
default:
if (version < 0x50)
return WINDOWS_UNDEFINED;
- winver = WINDOWS_11_OR_LATER;
- w = "11 or later";
+ winver = WINDOWS_12_OR_LATER;
+ w = "12 or later";
}
+ // We cannot tell if we are on 8, 10, or 11 without "app manifest"
+ if (version == 0x62 && vi.dwBuildNumber == 9200)
+ w = "8 (or later)";
+
arch = is_x64() ? "64-bit" : "32-bit";
if (vi.wServicePackMinor)
- usbi_dbg("Windows %s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, arch);
+ usbi_dbg(NULL, "Windows %s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, arch);
else if (vi.wServicePackMajor)
- usbi_dbg("Windows %s SP%u %s", w, vi.wServicePackMajor, arch);
+ usbi_dbg(NULL, "Windows %s SP%u %s", w, vi.wServicePackMajor, arch);
else
- usbi_dbg("Windows %s %s", w, arch);
+ usbi_dbg(NULL, "Windows %s %s", w, arch);
return winver;
}
@@ -416,10 +429,14 @@ static unsigned __stdcall windows_iocp_thread(void *arg)
DWORD num_bytes;
ULONG_PTR completion_key;
OVERLAPPED *overlapped;
+ struct libusb_device_handle *dev_handle;
+ struct libusb_device_handle *opened_device_handle;
+ struct windows_device_handle_priv *handle_priv;
struct windows_transfer_priv *transfer_priv;
struct usbi_transfer *itransfer;
+ bool found;
- usbi_dbg("I/O completion thread started");
+ usbi_dbg(ctx, "I/O completion thread started");
while (true) {
overlapped = NULL;
@@ -435,14 +452,48 @@ static unsigned __stdcall windows_iocp_thread(void *arg)
break;
}
- transfer_priv = container_of(overlapped, struct windows_transfer_priv, overlapped);
+ // Find the transfer associated with the OVERLAPPED that just completed.
+ // If we cannot find a match, the I/O operation originated from outside of libusb
+ // (e.g. within libusbK) and we need to ignore it.
+ dev_handle = (struct libusb_device_handle *)completion_key;
+
+ found = false;
+ transfer_priv = NULL;
+
+ // Issue 912: lock opened device handles in context to search the current device handle
+ // to avoid accessing unallocated memory after device has been closed
+ usbi_mutex_lock(&ctx->open_devs_lock);
+ for_each_open_device(ctx, opened_device_handle) {
+ if (dev_handle == opened_device_handle) {
+ handle_priv = usbi_get_device_handle_priv(dev_handle);
+
+ usbi_mutex_lock(&dev_handle->lock);
+ list_for_each_entry(transfer_priv, &handle_priv->active_transfers, list, struct windows_transfer_priv) {
+ if (overlapped == &transfer_priv->overlapped) {
+ // This OVERLAPPED belongs to us, remove the transfer from the device handle's list
+ list_del(&transfer_priv->list);
+ found = true;
+ break;
+ }
+ }
+ usbi_mutex_unlock(&dev_handle->lock);
+ }
+ }
+ usbi_mutex_unlock(&ctx->open_devs_lock);
+
+ if (!found) {
+ usbi_dbg(ctx, "ignoring overlapped %p for handle %p (device %u.%u)",
+ overlapped, dev_handle, dev_handle->dev->bus_number, dev_handle->dev->device_address);
+ continue;
+ }
+
itransfer = (struct usbi_transfer *)((unsigned char *)transfer_priv + PTR_ALIGN(sizeof(*transfer_priv)));
- usbi_dbg("transfer %p completed, length %lu",
+ usbi_dbg(ctx, "transfer %p completed, length %lu",
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer), ULONG_CAST(num_bytes));
usbi_signal_transfer_completion(itransfer);
}
- usbi_dbg("I/O completion thread exiting");
+ usbi_dbg(ctx, "I/O completion thread exiting");
return 0;
}
@@ -450,26 +501,9 @@ static unsigned __stdcall windows_iocp_thread(void *arg)
static int windows_init(struct libusb_context *ctx)
{
struct windows_context_priv *priv = usbi_get_context_priv(ctx);
- char mutex_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
- HANDLE mutex;
bool winusb_backend_init = false;
int r;
- sprintf(mutex_name, "libusb_init%08lX", ULONG_CAST(GetCurrentProcessId() & 0xFFFFFFFFU));
- mutex = CreateMutexA(NULL, FALSE, mutex_name);
- if (mutex == NULL) {
- usbi_err(ctx, "could not create mutex: %s", windows_error_str(0));
- return LIBUSB_ERROR_NO_MEM;
- }
-
- // A successful wait gives this thread ownership of the mutex
- // => any concurrent wait stalls until the mutex is released
- if (WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0) {
- usbi_err(ctx, "failure to access mutex: %s", windows_error_str(0));
- CloseHandle(mutex);
- 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 (++init_count == 1) { // First init?
@@ -496,7 +530,7 @@ static int windows_init(struct libusb_context *ctx)
r = usbdk_backend.init(ctx);
if (r == LIBUSB_SUCCESS) {
- usbi_dbg("UsbDk backend is available");
+ usbi_dbg(ctx, "UsbDk backend is available");
usbdk_available = true;
} else {
usbi_info(ctx, "UsbDk backend is not available");
@@ -538,29 +572,12 @@ init_exit: // Holds semaphore here
--init_count;
}
- ReleaseMutex(mutex);
- CloseHandle(mutex);
return r;
}
static void windows_exit(struct libusb_context *ctx)
{
struct windows_context_priv *priv = usbi_get_context_priv(ctx);
- char mutex_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
- HANDLE mutex;
-
- sprintf(mutex_name, "libusb_init%08lX", ULONG_CAST(GetCurrentProcessId() & 0xFFFFFFFFU));
- mutex = CreateMutexA(NULL, FALSE, mutex_name);
- if (mutex == NULL)
- return;
-
- // A successful wait gives this thread ownership of the mutex
- // => any concurrent wait stalls until the mutex is released
- if (WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0) {
- usbi_err(ctx, "failed to access mutex: %s", windows_error_str(0));
- CloseHandle(mutex);
- return;
- }
// A NULL completion status will indicate to the thread that it is time to exit
if (!PostQueuedCompletionStatus(priv->completion_port, 0, (ULONG_PTR)ctx, NULL))
@@ -581,9 +598,6 @@ static void windows_exit(struct libusb_context *ctx)
winusb_backend.exit(ctx);
htab_destroy();
}
-
- ReleaseMutex(mutex);
- CloseHandle(mutex);
}
static int windows_set_option(struct libusb_context *ctx, enum libusb_option option, va_list ap)
@@ -597,7 +611,7 @@ static int windows_set_option(struct libusb_context *ctx, enum libusb_option opt
usbi_err(ctx, "UsbDk backend not available");
return LIBUSB_ERROR_NOT_FOUND;
}
- usbi_dbg("switching context %p to use UsbDk backend", ctx);
+ usbi_dbg(ctx, "switching context %p to use UsbDk backend", ctx);
priv->backend = &usbdk_backend;
return LIBUSB_SUCCESS;
}
@@ -614,6 +628,9 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered
static int windows_open(struct libusb_device_handle *dev_handle)
{
struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle));
+ struct windows_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+
+ list_init(&handle_priv->active_transfers);
return priv->backend->open(dev_handle);
}
@@ -698,8 +715,10 @@ static void windows_destroy_device(struct libusb_device *dev)
static int windows_submit_transfer(struct usbi_transfer *itransfer)
{
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- struct libusb_context *ctx = TRANSFER_CTX(transfer);
+ struct libusb_device_handle *dev_handle = transfer->dev_handle;
+ struct libusb_context *ctx = HANDLE_CTX(dev_handle);
struct windows_context_priv *priv = usbi_get_context_priv(ctx);
+ struct windows_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer);
int r;
@@ -722,8 +741,18 @@ static int windows_submit_transfer(struct usbi_transfer *itransfer)
transfer_priv->handle = NULL;
}
+ // Add transfer to the device handle's list
+ usbi_mutex_lock(&dev_handle->lock);
+ list_add_tail(&transfer_priv->list, &handle_priv->active_transfers);
+ usbi_mutex_unlock(&dev_handle->lock);
+
r = priv->backend->submit_transfer(itransfer);
if (r != LIBUSB_SUCCESS) {
+ // Remove the unsuccessful transfer from the device handle's list
+ usbi_mutex_lock(&dev_handle->lock);
+ list_del(&transfer_priv->list);
+ usbi_mutex_unlock(&dev_handle->lock);
+
// Always call the backend's clear_transfer_priv() function on failure
priv->backend->clear_transfer_priv(itransfer);
transfer_priv->handle = NULL;
@@ -772,7 +801,7 @@ static int windows_handle_transfer_completion(struct usbi_transfer *itransfer)
else
result = GetLastError();
- usbi_dbg("handling transfer %p completion with errcode %lu, length %lu",
+ usbi_dbg(ctx, "handling transfer %p completion with errcode %lu, length %lu",
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer), ULONG_CAST(result), ULONG_CAST(bytes_transferred));
switch (result) {
@@ -780,25 +809,25 @@ static int windows_handle_transfer_completion(struct usbi_transfer *itransfer)
status = backend->copy_transfer_data(itransfer, bytes_transferred);
break;
case ERROR_GEN_FAILURE:
- usbi_dbg("detected endpoint stall");
+ usbi_dbg(ctx, "detected endpoint stall");
status = LIBUSB_TRANSFER_STALL;
break;
case ERROR_SEM_TIMEOUT:
- usbi_dbg("detected semaphore timeout");
+ usbi_dbg(ctx, "detected semaphore timeout");
status = LIBUSB_TRANSFER_TIMED_OUT;
break;
case ERROR_OPERATION_ABORTED:
istatus = backend->copy_transfer_data(itransfer, bytes_transferred);
if (istatus != LIBUSB_TRANSFER_COMPLETED)
- usbi_dbg("failed to copy partial data in aborted operation: %d", (int)istatus);
+ usbi_dbg(ctx, "failed to copy partial data in aborted operation: %d", (int)istatus);
- usbi_dbg("detected operation aborted");
+ usbi_dbg(ctx, "detected operation aborted");
status = LIBUSB_TRANSFER_CANCELLED;
break;
case ERROR_FILE_NOT_FOUND:
case ERROR_DEVICE_NOT_CONNECTED:
case ERROR_NO_SUCH_DEVICE:
- usbi_dbg("detected device removed");
+ usbi_dbg(ctx, "detected device removed");
status = LIBUSB_TRANSFER_NO_DEVICE;
break;
default:
@@ -881,6 +910,6 @@ const struct usbi_os_backend usbi_backend = {
windows_handle_transfer_completion,
sizeof(struct windows_context_priv),
sizeof(union windows_device_priv),
- sizeof(union windows_device_handle_priv),
+ sizeof(struct windows_device_handle_priv),
sizeof(struct windows_transfer_priv),
};
diff --git a/libusb/os/windows_common.h b/libusb/os/windows_common.h
index 0c4b94c..4582ce4 100644
--- a/libusb/os/windows_common.h
+++ b/libusb/os/windows_common.h
@@ -52,6 +52,8 @@
#define _strdup strdup
// _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, (LPDWORD)f)
+#else
+#include <process.h>
#endif
#define safe_free(p) do {if (p != NULL) {free((void *)p); p = NULL;}} while (0)
@@ -151,7 +153,8 @@ enum windows_version {
WINDOWS_8,
WINDOWS_8_1,
WINDOWS_10,
- WINDOWS_11_OR_LATER
+ WINDOWS_11,
+ WINDOWS_12_OR_LATER
};
extern enum windows_version windows_version;
@@ -257,18 +260,26 @@ struct winusb_device_priv {
} usb_interface[USB_MAXINTERFACES];
struct hid_device_priv *hid;
PUSB_CONFIGURATION_DESCRIPTOR *config_descriptor; // list of pointers to the cached config descriptors
+ GUID class_guid; // checked for change during re-enumeration
};
struct usbdk_device_handle_priv {
// Not currently used
char dummy;
};
+
+enum WINUSB_ZLP {
+ WINUSB_ZLP_UNSET = 0,
+ WINUSB_ZLP_OFF = 1,
+ WINUSB_ZLP_ON = 2
+};
struct winusb_device_handle_priv {
int active_interface;
struct {
HANDLE dev_handle; // WinUSB needs an extra handle for the file
HANDLE api_handle; // used by the API to communicate with the device
+ uint8_t zlp[USB_MAXENDPOINTS]; // Current per-endpoint SHORT_PACKET_TERMINATE status (enum WINUSB_ZLP)
} interface_handle[USB_MAXINTERFACES];
int autoclaim_count[USB_MAXINTERFACES]; // For auto-release
};
@@ -336,20 +347,36 @@ union windows_device_priv {
struct winusb_device_priv winusb_priv;
};
-union windows_device_handle_priv {
- struct usbdk_device_handle_priv usbdk_priv;
- struct winusb_device_handle_priv winusb_priv;
+struct windows_device_handle_priv {
+ struct list_head active_transfers;
+ union {
+ struct usbdk_device_handle_priv usbdk_priv;
+ struct winusb_device_handle_priv winusb_priv;
+ };
};
struct windows_transfer_priv {
OVERLAPPED overlapped;
HANDLE handle;
+ struct list_head list;
union {
struct usbdk_transfer_priv usbdk_priv;
struct winusb_transfer_priv winusb_priv;
};
};
+static inline struct usbdk_device_handle_priv *get_usbdk_device_handle_priv(struct libusb_device_handle *dev_handle)
+{
+ struct windows_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ return &handle_priv->usbdk_priv;
+}
+
+static inline struct winusb_device_handle_priv *get_winusb_device_handle_priv(struct libusb_device_handle *dev_handle)
+{
+ struct windows_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ return &handle_priv->winusb_priv;
+}
+
static inline OVERLAPPED *get_transfer_priv_overlapped(struct usbi_transfer *itransfer)
{
struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer);
diff --git a/libusb/os/windows_usbdk.c b/libusb/os/windows_usbdk.c
index c9ebfcf..9f52b48 100644
--- a/libusb/os/windows_usbdk.c
+++ b/libusb/os/windows_usbdk.c
@@ -27,7 +27,6 @@
#include <stdio.h>
#include "libusbi.h"
-#include "windows_common.h"
#include "windows_usbdk.h"
#if !defined(STATUS_SUCCESS)
@@ -414,7 +413,7 @@ static int usbdk_open(struct libusb_device_handle *dev_handle)
device_priv->system_handle = usbdk_helper.GetRedirectorSystemHandle(device_priv->redirector_handle);
- if (CreateIoCompletionPort(device_priv->system_handle, priv->completion_port, 0, 0) == NULL) {
+ if (CreateIoCompletionPort(device_priv->system_handle, priv->completion_port, (ULONG_PTR)dev_handle, 0) == NULL) {
usbi_err(ctx, "failed to associate handle to I/O completion port: %s", windows_error_str(0));
usbdk_helper.StopRedirect(device_priv->redirector_handle);
device_priv->system_handle = NULL;
diff --git a/libusb/os/windows_winusb.c b/libusb/os/windows_winusb.c
index f291b8e..ffc1612 100644
--- a/libusb/os/windows_winusb.c
+++ b/libusb/os/windows_winusb.c
@@ -28,14 +28,9 @@
#include <windows.h>
#include <setupapi.h>
#include <ctype.h>
-#include <fcntl.h>
-#include <process.h>
#include <stdio.h>
-#include <objbase.h>
-#include <winioctl.h>
#include "libusbi.h"
-#include "windows_common.h"
#include "windows_winusb.h"
#define HANDLE_VALID(h) (((h) != NULL) && ((h) != INVALID_HANDLE_VALUE))
@@ -109,12 +104,12 @@ static struct winusb_interface WinUSBX[SUB_API_MAX];
} while (0)
#if defined(ENABLE_LOGGING)
-static const char *guid_to_string(const GUID *guid)
+static const char *guid_to_string(const GUID *guid, char guid_string[MAX_GUID_STRING_LENGTH])
{
- static char guid_string[MAX_GUID_STRING_LENGTH];
-
- if (guid == NULL)
- return "";
+ if (guid == NULL) {
+ guid_string[0] = '\0';
+ return guid_string;
+ }
sprintf(guid_string, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
(unsigned int)guid->Data1, guid->Data2, guid->Data3,
@@ -125,6 +120,37 @@ static const char *guid_to_string(const GUID *guid)
}
#endif
+static bool string_to_guid(const char guid_string[MAX_GUID_STRING_LENGTH], GUID *guid)
+{
+ unsigned short tmp[4];
+ int num_chars = -1;
+ char extra;
+ int r;
+
+ // Unfortunately MinGW complains that '%hhx' is not a valid format specifier,
+ // even though Visual Studio 2013 and later support it. Rather than complicating
+ // the logic in this function with '#ifdef's, use a temporary array on the stack
+ // to store the conversions.
+ r = sscanf(guid_string, "{%8x-%4hx-%4hx-%4hx-%4hx%4hx%4hx}%n%c",
+ (unsigned int *)&guid->Data1, &guid->Data2, &guid->Data3,
+ &tmp[0], &tmp[1], &tmp[2], &tmp[3], &num_chars, &extra);
+
+ if ((r != 7) || (num_chars != 38))
+ return false;
+
+ // Extract the bytes from the 2-byte shorts
+ guid->Data4[0] = (unsigned char)((tmp[0] >> 8) & 0xFF);
+ guid->Data4[1] = (unsigned char)(tmp[0] & 0xFF);
+ guid->Data4[2] = (unsigned char)((tmp[1] >> 8) & 0xFF);
+ guid->Data4[3] = (unsigned char)(tmp[1] & 0xFF);
+ guid->Data4[4] = (unsigned char)((tmp[2] >> 8) & 0xFF);
+ guid->Data4[5] = (unsigned char)(tmp[2] & 0xFF);
+ guid->Data4[6] = (unsigned char)((tmp[3] >> 8) & 0xFF);
+ guid->Data4[7] = (unsigned char)(tmp[3] & 0xFF);
+
+ return true;
+}
+
/*
* Normalize Microsoft's paths: return a duplicate of the given path
* with all characters converted to uppercase
@@ -154,12 +180,9 @@ static bool init_dlls(struct libusb_context *ctx)
// Prefixed to avoid conflict with header files
DLL_GET_HANDLE(ctx, AdvAPI32);
- DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegQueryValueExW, true);
+ DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegQueryValueExA, true);
DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegCloseKey, true);
- DLL_GET_HANDLE(ctx, OLE32);
- DLL_LOAD_FUNC_PREFIXED(OLE32, p, IIDFromString, true);
-
DLL_GET_HANDLE(ctx, SetupAPI);
DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetClassDevsA, true);
DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiEnumDeviceInfo, true);
@@ -177,7 +200,6 @@ static bool init_dlls(struct libusb_context *ctx)
static void exit_dlls(void)
{
DLL_FREE_HANDLE(SetupAPI);
- DLL_FREE_HANDLE(OLE32);
DLL_FREE_HANDLE(AdvAPI32);
DLL_FREE_HANDLE(Cfgmgr32);
}
@@ -238,6 +260,7 @@ static int get_interface_details(struct libusb_context *ctx, HDEVINFO dev_info,
{
SP_DEVICE_INTERFACE_DATA dev_interface_data;
PSP_DEVICE_INTERFACE_DETAIL_DATA_A dev_interface_details;
+ char guid_string[MAX_GUID_STRING_LENGTH];
DWORD size;
dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA);
@@ -246,7 +269,7 @@ static int get_interface_details(struct libusb_context *ctx, HDEVINFO dev_info,
if (!pSetupDiEnumDeviceInfo(dev_info, *_index, dev_info_data)) {
if (GetLastError() != ERROR_NO_MORE_ITEMS) {
usbi_err(ctx, "Could not obtain device info data for %s index %lu: %s",
- guid_to_string(guid), ULONG_CAST(*_index), windows_error_str(0));
+ guid_to_string(guid, guid_string), ULONG_CAST(*_index), windows_error_str(0));
return LIBUSB_ERROR_OTHER;
}
@@ -262,7 +285,7 @@ static int get_interface_details(struct libusb_context *ctx, HDEVINFO dev_info,
if (GetLastError() != ERROR_NO_MORE_ITEMS) {
usbi_err(ctx, "Could not obtain interface data for %s devInst %lX: %s",
- guid_to_string(guid), ULONG_CAST(dev_info_data->DevInst), windows_error_str(0));
+ guid_to_string(guid, guid_string), ULONG_CAST(dev_info_data->DevInst), windows_error_str(0));
return LIBUSB_ERROR_OTHER;
}
@@ -274,7 +297,7 @@ static int get_interface_details(struct libusb_context *ctx, HDEVINFO dev_info,
// The dummy call should fail with ERROR_INSUFFICIENT_BUFFER
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
usbi_err(ctx, "could not access interface data (dummy) for %s devInst %lX: %s",
- guid_to_string(guid), ULONG_CAST(dev_info_data->DevInst), windows_error_str(0));
+ guid_to_string(guid, guid_string), ULONG_CAST(dev_info_data->DevInst), windows_error_str(0));
return LIBUSB_ERROR_OTHER;
}
} else {
@@ -285,7 +308,7 @@ static int get_interface_details(struct libusb_context *ctx, HDEVINFO dev_info,
dev_interface_details = malloc(size);
if (dev_interface_details == NULL) {
usbi_err(ctx, "could not allocate interface data for %s devInst %lX",
- guid_to_string(guid), ULONG_CAST(dev_info_data->DevInst));
+ guid_to_string(guid, guid_string), ULONG_CAST(dev_info_data->DevInst));
return LIBUSB_ERROR_NO_MEM;
}
@@ -293,7 +316,7 @@ static int get_interface_details(struct libusb_context *ctx, HDEVINFO dev_info,
if (!pSetupDiGetDeviceInterfaceDetailA(dev_info, &dev_interface_data,
dev_interface_details, size, NULL, NULL)) {
usbi_err(ctx, "could not access interface data (actual) for %s devInst %lX: %s",
- guid_to_string(guid), ULONG_CAST(dev_info_data->DevInst), windows_error_str(0));
+ guid_to_string(guid, guid_string), ULONG_CAST(dev_info_data->DevInst), windows_error_str(0));
free(dev_interface_details);
return LIBUSB_ERROR_OTHER;
}
@@ -303,7 +326,7 @@ static int get_interface_details(struct libusb_context *ctx, HDEVINFO dev_info,
if (*dev_interface_path == NULL) {
usbi_err(ctx, "could not allocate interface path for %s devInst %lX",
- guid_to_string(guid), ULONG_CAST(dev_info_data->DevInst));
+ guid_to_string(guid, guid_string), ULONG_CAST(dev_info_data->DevInst));
return LIBUSB_ERROR_NO_MEM;
}
@@ -386,14 +409,14 @@ static int get_interface_details_filter(struct libusb_context *ctx, HDEVINFO *de
DWORD value_length = sizeof(DWORD);
LONG status;
- status = pRegQueryValueExW(hkey_dev_interface, L"LUsb0", NULL, NULL,
+ status = pRegQueryValueExA(hkey_dev_interface, "LUsb0", NULL, NULL,
(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.
sprintf(filter_path, "\\\\.\\libusb0-%04u", (unsigned int)libusb0_symboliclink_index);
- usbi_dbg("assigned libusb0 symbolic link %s", filter_path);
+ usbi_dbg(ctx, "assigned libusb0 symbolic link %s", filter_path);
} else {
// libusb0.sys was connected to this device instance at one time; but not anymore.
}
@@ -451,23 +474,23 @@ static int get_interface_by_endpoint(struct libusb_config_descriptor *conf_desc,
intf_desc = &intf->altsetting[j];
for (k = 0; k < intf_desc->bNumEndpoints; k++) {
if (intf_desc->endpoint[k].bEndpointAddress == ep) {
- usbi_dbg("found endpoint %02X on interface %d", intf_desc->bInterfaceNumber, i);
+ usbi_dbg(NULL, "found endpoint %02X on interface %d", intf_desc->bInterfaceNumber, i);
return intf_desc->bInterfaceNumber;
}
}
}
}
- usbi_dbg("endpoint %02X not found on any interface", ep);
+ usbi_dbg(NULL, "endpoint %02X not found on any interface", ep);
return LIBUSB_ERROR_NOT_FOUND;
}
/*
* Open a device and associate the HANDLE with the context's I/O completion port
*/
-static HANDLE windows_open(struct libusb_device *dev, const char *path, DWORD access)
+static HANDLE windows_open(struct libusb_device_handle *dev_handle, const char *path, DWORD access)
{
- struct libusb_context *ctx = DEVICE_CTX(dev);
+ struct libusb_context *ctx = HANDLE_CTX(dev_handle);
struct windows_context_priv *priv = usbi_get_context_priv(ctx);
HANDLE handle;
@@ -475,7 +498,7 @@ static HANDLE windows_open(struct libusb_device *dev, const char *path, DWORD ac
if (handle == INVALID_HANDLE_VALUE)
return handle;
- if (CreateIoCompletionPort(handle, priv->completion_port, 0, 0) == NULL) {
+ if (CreateIoCompletionPort(handle, priv->completion_port, (ULONG_PTR)dev_handle, 0) == NULL) {
usbi_err(ctx, "failed to associate handle to I/O completion port: %s", windows_error_str(0));
CloseHandle(handle);
return INVALID_HANDLE_VALUE;
@@ -500,26 +523,26 @@ static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, uin
return r;
}
+ if (iface >= conf_desc->bNumInterfaces) {
+ usbi_err(HANDLE_CTX(dev_handle), "interface %d out of range for device", iface);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
if_desc = &conf_desc->interface[iface].altsetting[altsetting];
safe_free(priv->usb_interface[iface].endpoint);
if (if_desc->bNumEndpoints == 0) {
- usbi_dbg("no endpoints found for interface %u", iface);
- libusb_free_config_descriptor(conf_desc);
- priv->usb_interface[iface].current_altsetting = altsetting;
- return LIBUSB_SUCCESS;
- }
-
- priv->usb_interface[iface].endpoint = malloc(if_desc->bNumEndpoints);
- if (priv->usb_interface[iface].endpoint == NULL) {
- libusb_free_config_descriptor(conf_desc);
- return LIBUSB_ERROR_NO_MEM;
- }
-
- priv->usb_interface[iface].nb_endpoints = if_desc->bNumEndpoints;
- for (i = 0; i < if_desc->bNumEndpoints; i++) {
- priv->usb_interface[iface].endpoint[i] = if_desc->endpoint[i].bEndpointAddress;
- usbi_dbg("(re)assigned endpoint %02X to interface %u", priv->usb_interface[iface].endpoint[i], iface);
+ usbi_dbg(HANDLE_CTX(dev_handle), "no endpoints found for interface %u", iface);
+ } else {
+ priv->usb_interface[iface].endpoint = malloc(if_desc->bNumEndpoints);
+ if (priv->usb_interface[iface].endpoint == NULL) {
+ libusb_free_config_descriptor(conf_desc);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+ priv->usb_interface[iface].nb_endpoints = if_desc->bNumEndpoints;
+ for (i = 0; i < if_desc->bNumEndpoints; i++) {
+ priv->usb_interface[iface].endpoint[i] = if_desc->endpoint[i].bEndpointAddress;
+ usbi_dbg(HANDLE_CTX(dev_handle), "(re)assigned endpoint %02X to interface %u", priv->usb_interface[iface].endpoint[i], iface);
+ }
}
libusb_free_config_descriptor(conf_desc);
@@ -570,7 +593,7 @@ static int get_sub_api(char *driver, int api)
static int auto_claim(struct libusb_transfer *transfer, int *interface_number, int api_type)
{
struct winusb_device_handle_priv *handle_priv =
- usbi_get_device_handle_priv(transfer->dev_handle);
+ get_winusb_device_handle_priv(transfer->dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev);
int current_interface = *interface_number;
int r = LIBUSB_SUCCESS;
@@ -589,7 +612,7 @@ static int auto_claim(struct libusb_transfer *transfer, int *interface_number, i
// Must claim an interface of the same API type
if ((priv->usb_interface[current_interface].apib->id == api_type)
&& (libusb_claim_interface(transfer->dev_handle, current_interface) == LIBUSB_SUCCESS)) {
- usbi_dbg("auto-claimed interface %d for control request", current_interface);
+ usbi_dbg(TRANSFER_CTX(transfer), "auto-claimed interface %d for control request", current_interface);
if (handle_priv->autoclaim_count[current_interface] != 0)
usbi_err(TRANSFER_CTX(transfer), "program assertion failed - autoclaim_count was nonzero");
handle_priv->autoclaim_count[current_interface]++;
@@ -617,7 +640,7 @@ static void auto_release(struct usbi_transfer *itransfer)
struct winusb_transfer_priv *transfer_priv = get_winusb_transfer_priv(itransfer);
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
libusb_device_handle *dev_handle = transfer->dev_handle;
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
int r;
usbi_mutex_lock(&autoclaim_lock);
@@ -626,9 +649,9 @@ static void auto_release(struct usbi_transfer *itransfer)
if (handle_priv->autoclaim_count[transfer_priv->interface_number] == 0) {
r = libusb_release_interface(dev_handle, transfer_priv->interface_number);
if (r == LIBUSB_SUCCESS)
- usbi_dbg("auto-released interface %d", transfer_priv->interface_number);
+ usbi_dbg(ITRANSFER_CTX(itransfer), "auto-released interface %d", transfer_priv->interface_number);
else
- usbi_dbg("failed to auto-release interface %d (%s)",
+ usbi_dbg(ITRANSFER_CTX(itransfer), "failed to auto-release interface %d (%s)",
transfer_priv->interface_number, libusb_error_name((enum libusb_error)r));
}
}
@@ -769,7 +792,7 @@ static void cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handl
continue;
}
- usbi_dbg("cached config descriptor %u (bConfigurationValue=%u, %u bytes)",
+ usbi_dbg(ctx, "cached config descriptor %u (bConfigurationValue=%u, %u bytes)",
i, cd_data->bConfigurationValue, cd_data->wTotalLength);
// Cache the descriptor
@@ -886,7 +909,7 @@ static int init_root_hub(struct libusb_device *dev)
}
num_ports = hub_info.u.HubInformation.HubDescriptor.bNumberOfPorts;
- usbi_dbg("root hub '%s' reports %lu ports", priv->dev_id, ULONG_CAST(num_ports));
+ usbi_dbg(ctx, "root hub '%s' reports %lu ports", priv->dev_id, ULONG_CAST(num_ports));
if (windows_version >= WINDOWS_8) {
// Windows 8 and later is better at reporting the speed capabilities of the root hub,
@@ -1021,7 +1044,7 @@ make_descriptors:
static int init_device(struct libusb_device *dev, struct libusb_device *parent_dev,
uint8_t port_number, DEVINST devinst)
{
- struct libusb_context *ctx;
+ struct libusb_context *ctx = NULL;
struct libusb_device *tmp_dev;
struct winusb_device_priv *priv, *parent_priv, *tmp_priv;
USB_NODE_CONNECTION_INFORMATION_EX conn_info;
@@ -1120,7 +1143,7 @@ static int init_device(struct libusb_device *dev, struct libusb_device *parent_d
priv->active_config = conn_info.CurrentConfigurationValue;
if (priv->active_config == 0) {
- usbi_dbg("0x%x:0x%x found %u configurations (not configured)",
+ usbi_dbg(ctx, "0x%x:0x%x found %u configurations (not configured)",
dev->device_descriptor.idVendor,
dev->device_descriptor.idProduct,
dev->device_descriptor.bNumConfigurations);
@@ -1143,7 +1166,7 @@ static int init_device(struct libusb_device *dev, struct libusb_device *parent_d
dev->device_descriptor.bNumConfigurations);
priv->active_config = 1;
} else {
- usbi_dbg("found %u configurations (current config: %u)", dev->device_descriptor.bNumConfigurations, priv->active_config);
+ usbi_dbg(ctx, "found %u configurations (current config: %u)", dev->device_descriptor.bNumConfigurations, priv->active_config);
}
// Cache as many config descriptors as we can
@@ -1194,12 +1217,53 @@ static int init_device(struct libusb_device *dev, struct libusb_device *parent_d
priv->initialized = true;
- usbi_dbg("(bus: %u, addr: %u, depth: %u, port: %u): '%s'",
+ usbi_dbg(ctx, "(bus: %u, addr: %u, depth: %u, port: %u): '%s'",
dev->bus_number, dev->device_address, priv->depth, dev->port_number, priv->dev_id);
return LIBUSB_SUCCESS;
}
+static bool get_dev_port_number(HDEVINFO dev_info, SP_DEVINFO_DATA *dev_info_data, DWORD *port_nr)
+{
+ char buffer[MAX_KEY_LENGTH];
+ DWORD size;
+
+ // First try SPDRP_LOCATION_INFORMATION, which returns a REG_SZ. The string *may* have a format
+ // similar to "Port_#0002.Hub_#000D", in which case we can extract the port number. However, we
+ // cannot extract the port if the returned string does not follow this format.
+ if (pSetupDiGetDeviceRegistryPropertyA(dev_info, dev_info_data, SPDRP_LOCATION_INFORMATION,
+ NULL, (PBYTE)buffer, sizeof(buffer), NULL)) {
+ // Check for the required format.
+ if (strncmp(buffer, "Port_#", 6) == 0) {
+ *port_nr = atoi(buffer + 6);
+ return true;
+ }
+ }
+
+ // Next try SPDRP_LOCATION_PATHS, which returns a REG_MULTI_SZ (but we only examine the first
+ // string in it). Each path has a format similar to,
+ // "PCIROOT(B2)#PCI(0300)#PCI(0000)#USBROOT(0)#USB(1)#USB(2)#USBMI(3)", and the port number is
+ // the number within the last "USB(x)" token.
+ if (pSetupDiGetDeviceRegistryPropertyA(dev_info, dev_info_data, SPDRP_LOCATION_PATHS,
+ NULL, (PBYTE)buffer, sizeof(buffer), NULL)) {
+ // Find the last "#USB(x)" substring
+ for (char *token = strrchr(buffer, '#'); token != NULL; token = strrchr(buffer, '#')) {
+ if (strncmp(token, "#USB(", 5) == 0) {
+ *port_nr = atoi(token + 5);
+ return true;
+ }
+ // Shorten the string and try again.
+ *token = '\0';
+ }
+ }
+
+ // Lastly, try SPDRP_ADDRESS, which returns a REG_DWORD. The address *may* be the port number,
+ // which is true for the Microsoft driver but may not be true for other drivers. However, we
+ // have no other options here but to accept what it returns.
+ return pSetupDiGetDeviceRegistryPropertyA(dev_info, dev_info_data, SPDRP_ADDRESS,
+ NULL, (PBYTE)port_nr, sizeof(*port_nr), &size) && (size == sizeof(*port_nr));
+}
+
static int enumerate_hcd_root_hub(struct libusb_context *ctx, const char *dev_id,
uint8_t bus_number, DEVINST devinst)
{
@@ -1222,7 +1286,7 @@ static int enumerate_hcd_root_hub(struct libusb_context *ctx, const char *dev_id
if (dev->bus_number == 0) {
// Only do this once
- usbi_dbg("assigning HCD '%s' bus number %u", dev_id, bus_number);
+ usbi_dbg(ctx, "assigning HCD '%s' bus number %u", dev_id, bus_number);
dev->bus_number = bus_number;
if (sscanf(dev_id, "PCI\\VEN_%04hx&DEV_%04hx%*s", &dev->device_descriptor.idVendor, &dev->device_descriptor.idProduct) != 2)
@@ -1266,10 +1330,10 @@ static void get_api_type(HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data,
if (lookup[k].list[l] == 0)
lookup[k].list[l] = LIST_SEPARATOR;
}
- usbi_dbg("%s(s): %s", lookup[k].designation, lookup[k].list);
+ usbi_dbg(NULL, "%s(s): %s", lookup[k].designation, lookup[k].list);
} else {
if (GetLastError() != ERROR_INVALID_DATA)
- usbi_dbg("could not access %s: %s", lookup[k].designation, windows_error_str(0));
+ usbi_dbg(NULL, "could not access %s: %s", lookup[k].designation, windows_error_str(0));
lookup[k].list[0] = 0;
}
}
@@ -1278,7 +1342,7 @@ static void get_api_type(HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data,
for (k = 0; k < 3; k++) {
j = get_sub_api(lookup[k].list, i);
if (j >= 0) {
- usbi_dbg("matched %s name against %s", lookup[k].designation,
+ usbi_dbg(NULL, "matched %s name against %s", lookup[k].designation,
(i != USB_API_WINUSBX) ? usb_api_backend[i].designation : usb_api_backend[i].driver_name_list[j]);
*api = i;
*sub_api = j;
@@ -1314,7 +1378,7 @@ static int set_composite_interface(struct libusb_context *ctx, struct libusb_dev
if (priv->usb_interface[interface_number].path != NULL) {
if (api == USB_API_HID) {
// HID devices can have multiple collections (COL##) for each MI_## interface
- usbi_dbg("interface[%d] already set - ignoring HID collection: %s",
+ usbi_dbg(ctx, "interface[%d] already set - ignoring HID collection: %s",
interface_number, device_id);
return LIBUSB_ERROR_ACCESS;
}
@@ -1322,7 +1386,7 @@ static int set_composite_interface(struct libusb_context *ctx, struct libusb_dev
safe_free(priv->usb_interface[interface_number].path);
}
- usbi_dbg("interface[%d] = %s", interface_number, dev_interface_path);
+ usbi_dbg(ctx, "interface[%d] = %s", interface_number, dev_interface_path);
priv->usb_interface[interface_number].path = dev_interface_path;
priv->usb_interface[interface_number].apib = &usb_api_backend[api];
priv->usb_interface[interface_number].sub_api = sub_api;
@@ -1351,14 +1415,14 @@ static int set_hid_interface(struct libusb_context *ctx, struct libusb_device *d
for (i = 0; i < priv->hid->nb_interfaces; i++) {
if ((priv->usb_interface[i].path != NULL) && strcmp(priv->usb_interface[i].path, dev_interface_path) == 0) {
- usbi_dbg("interface[%u] already set to %s", i, dev_interface_path);
+ usbi_dbg(ctx, "interface[%u] already set to %s", i, dev_interface_path);
return LIBUSB_ERROR_ACCESS;
}
}
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[%u] = %s", priv->hid->nb_interfaces, dev_interface_path);
+ usbi_dbg(ctx, "interface[%u] = %s", priv->hid->nb_interfaces, dev_interface_path);
priv->hid->nb_interfaces++;
return LIBUSB_SUCCESS;
}
@@ -1384,7 +1448,7 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_
unsigned long session_id;
DWORD size, port_nr, reg_type, install_state;
HKEY key;
- WCHAR guid_string_w[MAX_GUID_STRING_LENGTH];
+ char guid_string[MAX_GUID_STRING_LENGTH];
GUID *if_guid;
LONG s;
#define HUB_PASS 0
@@ -1454,7 +1518,7 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_
//#define ENUM_DEBUG
#if defined(ENABLE_LOGGING) && defined(ENUM_DEBUG)
const char * const passname[] = {"HUB", "DEV", "HCD", "GEN", "HID", "EXT"};
- usbi_dbg("#### PROCESSING %ss %s", passname[MIN(pass, EXT_PASS)], guid_to_string(guid_list[pass]));
+ usbi_dbg(ctx, "#### PROCESSING %ss %s", passname[MIN(pass, EXT_PASS)], guid_to_string(guid_list[pass], guid_string));
#endif
if ((pass == HID_PASS) && (guid_list[HID_PASS] == NULL))
continue;
@@ -1506,7 +1570,7 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_
}
#ifdef ENUM_DEBUG
- usbi_dbg("PRO: %s", dev_id);
+ usbi_dbg(ctx, "PRO: %s", dev_id);
#endif
// Set API to use or get additional data from generic pass
@@ -1529,7 +1593,7 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_
break;
}
if (j == nb_usb_enumerators) {
- usbi_dbg("found new PnP enumerator string '%s'", enumerator);
+ usbi_dbg(ctx, "found new PnP enumerator string '%s'", enumerator);
if (nb_usb_enumerators < ARRAYSIZE(usb_enumerator)) {
usb_enumerator[nb_usb_enumerators] = _strdup(enumerator);
if (usb_enumerator[nb_usb_enumerators] != NULL) {
@@ -1555,16 +1619,25 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_
if (key == INVALID_HANDLE_VALUE)
break;
// Look for both DeviceInterfaceGUIDs *and* DeviceInterfaceGUID, in that order
- size = sizeof(guid_string_w);
- s = pRegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, &reg_type,
- (LPBYTE)guid_string_w, &size);
+ // If multiple GUIDs just process the first and ignore the others
+ size = sizeof(guid_string);
+ s = pRegQueryValueExA(key, "DeviceInterfaceGUIDs", NULL, &reg_type,
+ (LPBYTE)guid_string, &size);
if (s == ERROR_FILE_NOT_FOUND)
- s = pRegQueryValueExW(key, L"DeviceInterfaceGUID", NULL, &reg_type,
- (LPBYTE)guid_string_w, &size);
+ s = pRegQueryValueExA(key, "DeviceInterfaceGUID", NULL, &reg_type,
+ (LPBYTE)guid_string, &size);
pRegCloseKey(key);
- if ((s == ERROR_SUCCESS) &&
- (((reg_type == REG_SZ) && (size == (sizeof(guid_string_w) - sizeof(WCHAR)))) ||
- ((reg_type == REG_MULTI_SZ) && (size == sizeof(guid_string_w))))) {
+ if (s == ERROR_FILE_NOT_FOUND) {
+ break; /* no DeviceInterfaceGUID registered */
+ } else if (s != ERROR_SUCCESS && s != ERROR_MORE_DATA) {
+ usbi_warn(ctx, "unexpected error from pRegQueryValueExA for '%s'", dev_id);
+ break;
+ }
+ // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryvalueexa#remarks
+ // - "string may not have been stored with the proper terminating null characters"
+ // - "Note that REG_MULTI_SZ strings could have two terminating null characters"
+ if ((reg_type == REG_SZ && size >= sizeof(guid_string) - sizeof(char))
+ || (reg_type == REG_MULTI_SZ && size >= sizeof(guid_string) - 2 * sizeof(char))) {
if (nb_guids == guid_size) {
new_guid_list = realloc((void *)guid_list, (guid_size + GUID_SIZE_STEP) * sizeof(void *));
if (new_guid_list == NULL) {
@@ -1579,8 +1652,8 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_
usbi_err(ctx, "failed to alloc if_guid");
LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
}
- if (pIIDFromString(guid_string_w, if_guid) != 0) {
- usbi_warn(ctx, "device '%s' has malformed DeviceInterfaceGUID string, skipping", dev_id);
+ if (!string_to_guid(guid_string, if_guid)) {
+ usbi_warn(ctx, "device '%s' has malformed DeviceInterfaceGUID string '%s', skipping", dev_id, guid_string);
free(if_guid);
} else {
// Check if we've already seen this GUID
@@ -1589,14 +1662,14 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_
break;
}
if (j == nb_guids) {
- usbi_dbg("extra GUID: %s", guid_to_string(if_guid));
+ usbi_dbg(ctx, "extra GUID: %s", guid_string);
guid_list[nb_guids++] = if_guid;
} else {
// Duplicate, ignore
free(if_guid);
}
}
- } else if (s == ERROR_SUCCESS) {
+ } else {
usbi_warn(ctx, "unexpected type/size of DeviceInterfaceGUID for '%s'", dev_id);
}
break;
@@ -1631,7 +1704,7 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_
libusb_unref_device(dev);
}
- usbi_dbg("unlisted ancestor for '%s' (non USB HID, newly connected, etc.) - ignoring", dev_id);
+ usbi_dbg(ctx, "unlisted ancestor for '%s' (non USB HID, newly connected, etc.) - ignoring", dev_id);
continue;
}
@@ -1650,23 +1723,30 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_
dev = usbi_get_device_by_session_id(ctx, session_id);
if (dev == NULL) {
alloc_device:
- usbi_dbg("allocating new device for session [%lX]", session_id);
+ usbi_dbg(ctx, "allocating new device for session [%lX]", session_id);
dev = usbi_alloc_device(ctx, session_id);
if (dev == NULL)
LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
priv = winusb_device_priv_init(dev);
priv->dev_id = _strdup(dev_id);
+ priv->class_guid = dev_info_data.ClassGuid;
if (priv->dev_id == NULL) {
libusb_unref_device(dev);
LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
}
} else {
- usbi_dbg("found existing device for session [%lX]", session_id);
+ usbi_dbg(ctx, "found existing device for session [%lX]", session_id);
priv = usbi_get_device_priv(dev);
if (strcmp(priv->dev_id, dev_id) != 0) {
- usbi_dbg("device instance ID for session [%lX] changed", session_id);
+ usbi_dbg(ctx, "device instance ID for session [%lX] changed", session_id);
+ usbi_disconnect_device(dev);
+ libusb_unref_device(dev);
+ goto alloc_device;
+ }
+ if (!IsEqualGUID(&priv->class_guid, &dev_info_data.ClassGuid)) {
+ usbi_dbg(ctx, "device class GUID for session [%lX] changed", session_id);
usbi_disconnect_device(dev);
libusb_unref_device(dev);
goto alloc_device;
@@ -1724,10 +1804,8 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_
r = enumerate_hcd_root_hub(ctx, dev_id, (uint8_t)(i + 1), dev_info_data.DevInst);
break;
case GEN_PASS:
- // The SPDRP_ADDRESS for USB devices is the device port number on the hub
port_nr = 0;
- if (!pSetupDiGetDeviceRegistryPropertyA(*dev_info, &dev_info_data, SPDRP_ADDRESS,
- NULL, (PBYTE)&port_nr, sizeof(port_nr), &size) || (size != sizeof(port_nr)))
+ if (!get_dev_port_number(*dev_info, &dev_info_data, &port_nr))
usbi_warn(ctx, "could not retrieve port number for device '%s': %s", dev_id, windows_error_str(0));
r = init_device(dev, parent_dev, (uint8_t)port_nr, dev_info_data.DevInst);
if (r == LIBUSB_SUCCESS) {
@@ -1747,10 +1825,10 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_
default: // HID_PASS and later
if (parent_priv->apib->id == USB_API_HID || parent_priv->apib->id == USB_API_COMPOSITE) {
if (parent_priv->apib->id == USB_API_HID) {
- usbi_dbg("setting HID interface for [%lX]:", parent_dev->session_data);
+ usbi_dbg(ctx, "setting HID interface for [%lX]:", parent_dev->session_data);
r = set_hid_interface(ctx, parent_dev, dev_interface_path);
} else {
- usbi_dbg("setting composite interface for [%lX]:", parent_dev->session_data);
+ usbi_dbg(ctx, "setting composite interface for [%lX]:", parent_dev->session_data);
r = set_composite_interface(ctx, parent_dev, dev_interface_path, dev_id, api, sub_api);
}
switch (r) {
@@ -2001,8 +2079,6 @@ static int winusb_submit_transfer(struct usbi_transfer *itransfer)
break;
case LIBUSB_TRANSFER_TYPE_BULK:
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
- if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET))
- return LIBUSB_ERROR_NOT_SUPPORTED;
transfer_fn = priv->apib->submit_bulk_transfer;
break;
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
@@ -2263,10 +2339,10 @@ cleanup_winusb:
KLIB_VERSION LibK_Version;
pLibK_GetVersion(&LibK_Version);
- usbi_dbg("libusbK DLL found, version: %d.%d.%d.%d", LibK_Version.Major, LibK_Version.Minor,
+ usbi_dbg(ctx, "libusbK DLL found, version: %d.%d.%d.%d", LibK_Version.Major, LibK_Version.Minor,
LibK_Version.Micro, LibK_Version.Nano);
} else {
- usbi_dbg("libusbK DLL found, version unknown");
+ usbi_dbg(ctx, "libusbK DLL found, version unknown");
}
pLibK_GetProcAddress = (LibK_GetProcAddress_t)GetProcAddress(hlibusbK, "LibK_GetProcAddress");
@@ -2352,7 +2428,7 @@ static void winusbx_exit(void)
static int winusbx_open(int sub_api, struct libusb_device_handle *dev_handle)
{
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
HANDLE file_handle;
int i;
@@ -2362,7 +2438,7 @@ static int winusbx_open(int sub_api, struct libusb_device_handle *dev_handle)
for (i = 0; i < USB_MAXINTERFACES; i++) {
if ((priv->usb_interface[i].path != NULL)
&& (priv->usb_interface[i].apib->id == USB_API_WINUSBX)) {
- file_handle = windows_open(dev_handle->dev, priv->usb_interface[i].path, GENERIC_READ | GENERIC_WRITE);
+ file_handle = windows_open(dev_handle, priv->usb_interface[i].path, GENERIC_READ | GENERIC_WRITE);
if (file_handle == INVALID_HANDLE_VALUE) {
usbi_err(HANDLE_CTX(dev_handle), "could not open device %s (interface %d): %s", priv->usb_interface[i].path, i, windows_error_str(0));
switch (GetLastError()) {
@@ -2384,7 +2460,7 @@ 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)
{
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
HANDLE handle;
int i;
@@ -2429,7 +2505,7 @@ 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, uint8_t iface)
{
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
HANDLE winusb_handle = handle_priv->interface_handle[iface].api_handle;
UCHAR policy;
@@ -2445,35 +2521,36 @@ static int winusbx_configure_endpoints(int sub_api, struct libusb_device_handle
endpoint_address = (i == -1) ? 0 : priv->usb_interface[iface].endpoint[i];
if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout))
- usbi_dbg("failed to set PIPE_TRANSFER_TIMEOUT for control endpoint %02X", endpoint_address);
+ usbi_dbg(HANDLE_CTX(dev_handle), "failed to set PIPE_TRANSFER_TIMEOUT for control endpoint %02X", endpoint_address);
if ((i == -1) || (sub_api == SUB_API_LIBUSB0))
continue; // Other policies don't apply to control endpoint or libusb0
policy = false;
+ handle_priv->interface_handle[iface].zlp[endpoint_address] = WINUSB_ZLP_UNSET;
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);
+ usbi_dbg(HANDLE_CTX(dev_handle), "failed to disable SHORT_PACKET_TERMINATE for endpoint %02X", endpoint_address);
if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
IGNORE_SHORT_PACKETS, sizeof(UCHAR), &policy))
- usbi_dbg("failed to disable IGNORE_SHORT_PACKETS for endpoint %02X", endpoint_address);
+ usbi_dbg(HANDLE_CTX(dev_handle), "failed to disable IGNORE_SHORT_PACKETS for endpoint %02X", endpoint_address);
policy = true;
/* ALLOW_PARTIAL_READS must be enabled due to likely libusbK bug. See:
https://sourceforge.net/mailarchive/message.php?msg_id=29736015 */
if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
ALLOW_PARTIAL_READS, sizeof(UCHAR), &policy))
- usbi_dbg("failed to enable ALLOW_PARTIAL_READS for endpoint %02X", endpoint_address);
+ usbi_dbg(HANDLE_CTX(dev_handle), "failed to enable ALLOW_PARTIAL_READS for endpoint %02X", endpoint_address);
if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
AUTO_CLEAR_STALL, sizeof(UCHAR), &policy))
- usbi_dbg("failed to enable AUTO_CLEAR_STALL for endpoint %02X", endpoint_address);
+ usbi_dbg(HANDLE_CTX(dev_handle), "failed to enable AUTO_CLEAR_STALL for endpoint %02X", endpoint_address);
if (sub_api == SUB_API_LIBUSBK) {
if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
ISO_ALWAYS_START_ASAP, sizeof(UCHAR), &policy))
- usbi_dbg("failed to enable ISO_ALWAYS_START_ASAP for endpoint %02X", endpoint_address);
+ usbi_dbg(HANDLE_CTX(dev_handle), "failed to enable ISO_ALWAYS_START_ASAP for endpoint %02X", endpoint_address);
}
}
@@ -2483,7 +2560,7 @@ static int winusbx_configure_endpoints(int sub_api, struct libusb_device_handle
static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface)
{
struct libusb_context *ctx = HANDLE_CTX(dev_handle);
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
bool is_using_usbccgp = (priv->apib->id == USB_API_COMPOSITE);
HDEVINFO dev_info;
@@ -2534,7 +2611,7 @@ static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev
*dev_interface_path_guid_start = '\0';
if (strncmp(dev_interface_path, priv->usb_interface[iface].path, strlen(dev_interface_path)) == 0) {
- file_handle = windows_open(dev_handle->dev, filter_path, GENERIC_READ | GENERIC_WRITE);
+ file_handle = windows_open(dev_handle, filter_path, GENERIC_READ | GENERIC_WRITE);
if (file_handle != INVALID_HANDLE_VALUE) {
if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) {
// Replace the existing file handle with the working one
@@ -2591,7 +2668,7 @@ static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev
}
handle_priv->interface_handle[iface].dev_handle = handle_priv->interface_handle[0].dev_handle;
}
- usbi_dbg("claimed interface %u", iface);
+ usbi_dbg(ctx, "claimed interface %u", iface);
handle_priv->active_interface = iface;
return LIBUSB_SUCCESS;
@@ -2599,7 +2676,7 @@ static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev
static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface)
{
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
HANDLE winusb_handle;
@@ -2620,12 +2697,12 @@ static int winusbx_release_interface(int sub_api, struct libusb_device_handle *d
*/
static int get_valid_interface(struct libusb_device_handle *dev_handle, int api_id)
{
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
int i;
if ((api_id < USB_API_WINUSBX) || (api_id > USB_API_HID)) {
- usbi_dbg("unsupported API ID");
+ usbi_dbg(HANDLE_CTX(dev_handle), "unsupported API ID");
return -1;
}
@@ -2644,14 +2721,14 @@ static int get_valid_interface(struct libusb_device_handle *dev_handle, int api_
*/
static int check_valid_interface(struct libusb_device_handle *dev_handle, unsigned short interface, int api_id)
{
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
if (interface >= USB_MAXINTERFACES)
return -1;
if ((api_id < USB_API_WINUSBX) || (api_id > USB_API_HID)) {
- usbi_dbg("unsupported API ID");
+ usbi_dbg(HANDLE_CTX(dev_handle), "unsupported API ID");
return -1;
}
@@ -2691,9 +2768,9 @@ static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *it
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev);
struct winusb_transfer_priv *transfer_priv = get_winusb_transfer_priv(itransfer);
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(transfer->dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(transfer->dev_handle);
PWINUSB_SETUP_PACKET setup = (PWINUSB_SETUP_PACKET)transfer->buffer;
- ULONG size;
+ ULONG size, transferred;
HANDLE winusb_handle;
OVERLAPPED *overlapped;
int current_interface;
@@ -2703,7 +2780,7 @@ static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *it
size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE;
// Windows places upper limits on the control transfer size
- // See: https://msdn.microsoft.com/en-us/library/windows/hardware/ff538112.aspx
+ // See: https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/usb-bandwidth-allocation#maximum-transfer-size
if (size > MAX_CTRL_BUFFER_LENGTH)
return LIBUSB_ERROR_INVALID_PARAM;
@@ -2716,8 +2793,9 @@ static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *it
return LIBUSB_ERROR_NOT_FOUND;
}
- usbi_dbg("will use interface %d", current_interface);
+ usbi_dbg(ITRANSFER_CTX(itransfer), "will use interface %d", current_interface);
+ transfer_priv->interface_number = (uint8_t)current_interface;
winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
set_transfer_priv_handle(itransfer, handle_priv->interface_handle[current_interface].dev_handle);
overlapped = get_transfer_priv_overlapped(itransfer);
@@ -2732,22 +2810,22 @@ static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *it
}
windows_force_sync_completion(itransfer, 0);
} else {
- if (!WinUSBX[sub_api].ControlTransfer(winusb_handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, overlapped)) {
+ if (!WinUSBX[sub_api].ControlTransfer(winusb_handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, &transferred, overlapped)) {
if (GetLastError() != ERROR_IO_PENDING) {
usbi_warn(TRANSFER_CTX(transfer), "ControlTransfer failed: %s", windows_error_str(0));
return LIBUSB_ERROR_IO;
}
+ } else {
+ windows_force_sync_completion(itransfer, transferred);
}
}
- transfer_priv->interface_number = (uint8_t)current_interface;
-
return LIBUSB_SUCCESS;
}
static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting)
{
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
HANDLE winusb_handle;
@@ -2803,7 +2881,7 @@ static int winusbx_submit_iso_transfer(int sub_api, struct usbi_transfer *itrans
{
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct winusb_transfer_priv *transfer_priv = get_winusb_transfer_priv(itransfer);
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(transfer->dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(transfer->dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev);
HANDLE winusb_handle;
OVERLAPPED *overlapped;
@@ -2818,8 +2896,9 @@ static int winusbx_submit_iso_transfer(int sub_api, struct usbi_transfer *itrans
return LIBUSB_ERROR_NOT_FOUND;
}
- usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface);
+ usbi_dbg(TRANSFER_CTX(transfer), "matched endpoint %02X with interface %d", transfer->endpoint, current_interface);
+ transfer_priv->interface_number = (uint8_t)current_interface;
winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
set_transfer_priv_handle(itransfer, handle_priv->interface_handle[current_interface].dev_handle);
overlapped = get_transfer_priv_overlapped(itransfer);
@@ -2852,10 +2931,10 @@ static int winusbx_submit_iso_transfer(int sub_api, struct usbi_transfer *itrans
}
if (IS_XFERIN(transfer)) {
- usbi_dbg("reading %d iso packets", transfer->num_iso_packets);
+ usbi_dbg(TRANSFER_CTX(transfer), "reading %d iso packets", transfer->num_iso_packets);
ret = WinUSBX[sub_api].IsoReadPipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, overlapped, iso_context);
} else {
- usbi_dbg("writing %d iso packets", transfer->num_iso_packets);
+ usbi_dbg(TRANSFER_CTX(transfer), "writing %d iso packets", transfer->num_iso_packets);
ret = WinUSBX[sub_api].IsoWritePipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, overlapped, iso_context);
}
@@ -2864,8 +2943,6 @@ static int winusbx_submit_iso_transfer(int sub_api, struct usbi_transfer *itrans
return LIBUSB_ERROR_IO;
}
- transfer_priv->interface_number = (uint8_t)current_interface;
-
return LIBUSB_SUCCESS;
} else if (sub_api == SUB_API_WINUSB) {
WINUSB_PIPE_INFORMATION_EX pipe_info_ex = { 0 };
@@ -2913,7 +2990,11 @@ static int winusbx_submit_iso_transfer(int sub_api, struct usbi_transfer *itrans
// WinUSB only supports isoch transfers spanning a full USB frames. Later, we might be smarter about this
// and allocate a temporary buffer. However, this is harder than it seems as its destruction would depend on overlapped
// IO...
- iso_transfer_size_multiple = (pipe_info_ex.MaximumBytesPerInterval * 8) / interval;
+ if (transfer->dev_handle->dev->speed >= LIBUSB_SPEED_HIGH) // Microframes (125us)
+ iso_transfer_size_multiple = (pipe_info_ex.MaximumBytesPerInterval * 8) / interval;
+ else // Normal Frames (1ms)
+ iso_transfer_size_multiple = pipe_info_ex.MaximumBytesPerInterval / interval;
+
if (transfer->length % iso_transfer_size_multiple != 0) {
usbi_err(TRANSFER_CTX(transfer), "length of isoch buffer must be a multiple of the MaximumBytesPerInterval * 8 / Interval");
return LIBUSB_ERROR_INVALID_PARAM;
@@ -2980,8 +3061,6 @@ static int winusbx_submit_iso_transfer(int sub_api, struct usbi_transfer *itrans
transfer_priv->isoch_buffer_handle = buffer_handle;
- transfer_priv->interface_number = (uint8_t)current_interface;
-
return LIBUSB_SUCCESS;
} else {
PRINT_UNSUPPORTED_API(winusbx_submit_iso_transfer);
@@ -2993,7 +3072,7 @@ static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itran
{
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct winusb_transfer_priv *transfer_priv = get_winusb_transfer_priv(itransfer);
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(transfer->dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(transfer->dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev);
HANDLE winusb_handle;
OVERLAPPED *overlapped;
@@ -3008,17 +3087,35 @@ static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itran
return LIBUSB_ERROR_NOT_FOUND;
}
- usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface);
+ usbi_dbg(TRANSFER_CTX(transfer), "matched endpoint %02X with interface %d", transfer->endpoint, current_interface);
+ transfer_priv->interface_number = (uint8_t)current_interface;
winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
set_transfer_priv_handle(itransfer, handle_priv->interface_handle[current_interface].dev_handle);
overlapped = get_transfer_priv_overlapped(itransfer);
if (IS_XFERIN(transfer)) {
- usbi_dbg("reading %d bytes", transfer->length);
+ usbi_dbg(TRANSFER_CTX(transfer), "reading %d bytes", transfer->length);
ret = WinUSBX[sub_api].ReadPipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, overlapped);
} else {
- usbi_dbg("writing %d bytes", transfer->length);
+ // Set SHORT_PACKET_TERMINATE if ZLP requested.
+ // Changing this can be a problem with packets in flight, so only allow on the first transfer.
+ UCHAR policy = (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) != 0;
+ uint8_t* current_zlp = &handle_priv->interface_handle[current_interface].zlp[transfer->endpoint];
+ if (*current_zlp == WINUSB_ZLP_UNSET) {
+ if (policy &&
+ !WinUSBX[sub_api].SetPipePolicy(winusb_handle, transfer->endpoint,
+ SHORT_PACKET_TERMINATE, sizeof(UCHAR), &policy)) {
+ usbi_err(TRANSFER_CTX(transfer), "failed to set SHORT_PACKET_TERMINATE for endpoint %02X", transfer->endpoint);
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+ *current_zlp = policy ? WINUSB_ZLP_ON : WINUSB_ZLP_OFF;
+ } else if (policy != (*current_zlp == WINUSB_ZLP_ON)) {
+ usbi_err(TRANSFER_CTX(transfer), "cannot change ZERO_PACKET for endpoint %02X on Windows", transfer->endpoint);
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+
+ usbi_dbg(TRANSFER_CTX(transfer), "writing %d bytes", transfer->length);
ret = WinUSBX[sub_api].WritePipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, overlapped);
}
@@ -3027,14 +3124,12 @@ static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itran
return LIBUSB_ERROR_IO;
}
- transfer_priv->interface_number = (uint8_t)current_interface;
-
return LIBUSB_SUCCESS;
}
static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint)
{
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
HANDLE winusb_handle;
int current_interface;
@@ -3047,7 +3142,7 @@ static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_hand
return LIBUSB_ERROR_NOT_FOUND;
}
- usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface);
+ usbi_dbg(HANDLE_CTX(dev_handle), "matched endpoint %02X with interface %d", endpoint, current_interface);
winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
if (!WinUSBX[sub_api].ResetPipe(winusb_handle, endpoint)) {
@@ -3061,7 +3156,7 @@ static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_hand
static int winusbx_cancel_transfer(int sub_api, struct usbi_transfer *itransfer)
{
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(transfer->dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(transfer->dev_handle);
struct winusb_transfer_priv *transfer_priv = get_winusb_transfer_priv(itransfer);
struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev);
int current_interface = transfer_priv->interface_number;
@@ -3069,7 +3164,7 @@ static int winusbx_cancel_transfer(int sub_api, struct usbi_transfer *itransfer)
CHECK_WINUSBX_AVAILABLE(sub_api);
- usbi_dbg("will use interface %d", current_interface);
+ usbi_dbg(TRANSFER_CTX(transfer), "will use interface %d", current_interface);
handle = handle_priv->interface_handle[current_interface].api_handle;
if (!WinUSBX[sub_api].AbortPipe(handle, transfer->endpoint)) {
@@ -3091,7 +3186,7 @@ static int winusbx_cancel_transfer(int sub_api, struct usbi_transfer *itransfer)
// 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 winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
HANDLE winusb_handle;
int i, j;
@@ -3103,7 +3198,7 @@ static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_ha
winusb_handle = handle_priv->interface_handle[i].api_handle;
if (HANDLE_VALID(winusb_handle)) {
for (j = 0; j < priv->usb_interface[i].nb_endpoints; j++) {
- usbi_dbg("resetting ep %02X", priv->usb_interface[i].endpoint[j]);
+ usbi_dbg(HANDLE_CTX(dev_handle), "resetting ep %02X", priv->usb_interface[i].endpoint[j]);
if (!WinUSBX[sub_api].AbortPipe(winusb_handle, priv->usb_interface[i].endpoint[j]))
usbi_err(HANDLE_CTX(dev_handle), "AbortPipe (pipe address %02X) failed: %s",
priv->usb_interface[i].endpoint[j], windows_error_str(0));
@@ -3138,12 +3233,25 @@ static enum libusb_transfer_status winusbx_copy_transfer_data(int sub_api, struc
int i;
if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
+ struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev);
+
+ if (sub_api == SUB_API_NOTSET)
+ sub_api = priv->sub_api;
+ if (WinUSBX[sub_api].hDll == NULL)
+ return LIBUSB_TRANSFER_ERROR;
+
// for isochronous, need to copy the individual iso packet actual_lengths and statuses
if ((sub_api == SUB_API_LIBUSBK) || (sub_api == SUB_API_LIBUSB0)) {
// iso only supported on libusbk-based backends for now
PKISO_CONTEXT iso_context = transfer_priv->iso_context;
for (i = 0; i < transfer->num_iso_packets; i++) {
- transfer->iso_packet_desc[i].actual_length = iso_context->IsoPackets[i].actual_length;
+ if (IS_XFERIN(transfer)) {
+ transfer->iso_packet_desc[i].actual_length = iso_context->IsoPackets[i].actual_length;
+ } else {
+ // On Windows the usbd Length field is not used for OUT transfers.
+ // Copy the requested value back for consistency with other platforms.
+ transfer->iso_packet_desc[i].actual_length = transfer->iso_packet_desc[i].length;
+ }
// TODO translate USDB_STATUS codes http://msdn.microsoft.com/en-us/library/ff539136(VS.85).aspx to libusb_transfer_status
//transfer->iso_packet_desc[i].status = transfer_priv->iso_context->IsoPackets[i].status;
}
@@ -3164,6 +3272,9 @@ static enum libusb_transfer_status winusbx_copy_transfer_data(int sub_api, struc
} else {
for (i = 0; i < transfer->num_iso_packets; i++) {
transfer->iso_packet_desc[i].status = LIBUSB_TRANSFER_COMPLETED;
+ // On Windows the usbd Length field is not used for OUT transfers.
+ // Copy the requested value back for consistency with other platforms.
+ transfer->iso_packet_desc[i].actual_length = transfer->iso_packet_desc[i].length;
}
}
} else {
@@ -3439,28 +3550,28 @@ static int _hid_get_descriptor(struct libusb_device *dev, HANDLE hid_handle, int
switch (type) {
case LIBUSB_DT_DEVICE:
- usbi_dbg("LIBUSB_DT_DEVICE");
+ usbi_dbg(DEVICE_CTX(dev), "LIBUSB_DT_DEVICE");
return _hid_get_device_descriptor(priv->hid, data, size);
case LIBUSB_DT_CONFIG:
- usbi_dbg("LIBUSB_DT_CONFIG");
+ usbi_dbg(DEVICE_CTX(dev), "LIBUSB_DT_CONFIG");
if (!_index)
return _hid_get_config_descriptor(priv->hid, data, size);
return LIBUSB_ERROR_INVALID_PARAM;
case LIBUSB_DT_STRING:
- usbi_dbg("LIBUSB_DT_STRING");
+ usbi_dbg(DEVICE_CTX(dev), "LIBUSB_DT_STRING");
return _hid_get_string_descriptor(priv->hid, _index, data, size, hid_handle);
case LIBUSB_DT_HID:
- usbi_dbg("LIBUSB_DT_HID");
+ usbi_dbg(DEVICE_CTX(dev), "LIBUSB_DT_HID");
if (!_index)
return _hid_get_hid_descriptor(priv->hid, data, size);
return LIBUSB_ERROR_INVALID_PARAM;
case LIBUSB_DT_REPORT:
- usbi_dbg("LIBUSB_DT_REPORT");
+ usbi_dbg(DEVICE_CTX(dev), "LIBUSB_DT_REPORT");
if (!_index)
return _hid_get_report_descriptor(priv->hid, data, size);
return LIBUSB_ERROR_INVALID_PARAM;
case LIBUSB_DT_PHYSICAL:
- usbi_dbg("LIBUSB_DT_PHYSICAL");
+ usbi_dbg(DEVICE_CTX(dev), "LIBUSB_DT_PHYSICAL");
if (HidD_GetPhysicalDescriptor(hid_handle, data, (ULONG)*size))
return LIBUSB_COMPLETED;
return LIBUSB_ERROR_OTHER;
@@ -3502,7 +3613,7 @@ static int _hid_get_report(struct libusb_device *dev, HANDLE hid_handle, int id,
return LIBUSB_ERROR_NO_MEM;
buf[0] = (uint8_t)id; // Must be set always
- usbi_dbg("report ID: 0x%02X", buf[0]);
+ usbi_dbg(DEVICE_CTX(dev), "report ID: 0x%02X", buf[0]);
// 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,
@@ -3550,7 +3661,7 @@ static int _hid_set_report(struct libusb_device *dev, HANDLE hid_handle, int id,
return LIBUSB_ERROR_INVALID_PARAM;
}
- usbi_dbg("report ID: 0x%02X", id);
+ usbi_dbg(DEVICE_CTX(dev), "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)
@@ -3644,7 +3755,7 @@ static int hid_open(int sub_api, struct libusb_device_handle *dev_handle)
{
struct libusb_device *dev = dev_handle->dev;
struct winusb_device_priv *priv = usbi_get_device_priv(dev);
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
HIDD_ATTRIBUTES hid_attributes;
PHIDP_PREPARSED_DATA preparsed_data = NULL;
HIDP_CAPS capabilities;
@@ -3669,7 +3780,7 @@ static int hid_open(int sub_api, struct libusb_device_handle *dev_handle)
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 = windows_open(dev, priv->usb_interface[i].path, GENERIC_READ | GENERIC_WRITE);
+ hid_handle = windows_open(dev_handle, priv->usb_interface[i].path, GENERIC_READ | GENERIC_WRITE);
/*
* 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
@@ -3679,7 +3790,7 @@ static int hid_open(int sub_api, struct libusb_device_handle *dev_handle)
*/
if (hid_handle == INVALID_HANDLE_VALUE) {
usbi_warn(HANDLE_CTX(dev_handle), "could not open HID device in R/W mode (keyboard or mouse?) - trying without");
- hid_handle = windows_open(dev, priv->usb_interface[i].path, 0);
+ hid_handle = windows_open(dev_handle, priv->usb_interface[i].path, 0);
if (hid_handle == INVALID_HANDLE_VALUE) {
usbi_err(HANDLE_CTX(dev_handle), "could not open device %s (interface %d): %s", priv->path, i, windows_error_str(0));
switch (GetLastError()) {
@@ -3709,7 +3820,7 @@ static int hid_open(int sub_api, struct libusb_device_handle *dev_handle)
// 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);
+ usbi_dbg(HANDLE_CTX(dev_handle), "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) {
@@ -3726,7 +3837,7 @@ static int hid_open(int sub_api, struct libusb_device_handle *dev_handle)
size[1] = capabilities.NumberOutputValueCaps;
size[2] = capabilities.NumberFeatureValueCaps;
for (j = HidP_Input; j <= HidP_Feature; j++) {
- usbi_dbg("%lu HID %s report value(s) found", ULONG_CAST(size[j]), type[j]);
+ usbi_dbg(HANDLE_CTX(dev_handle), "%lu HID %s report value(s) found", ULONG_CAST(size[j]), type[j]);
priv->hid->uses_report_ids[j] = false;
if (size[j] > 0) {
value_caps = calloc(size[j], sizeof(HIDP_VALUE_CAPS));
@@ -3736,7 +3847,7 @@ static int hid_open(int sub_api, struct libusb_device_handle *dev_handle)
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);
+ usbi_dbg(HANDLE_CTX(dev_handle), " Report ID: 0x%02X", value_caps[i].ReportID);
if (value_caps[i].ReportID != 0)
nb_ids[1]++;
else
@@ -3773,7 +3884,10 @@ static int hid_open(int sub_api, struct libusb_device_handle *dev_handle)
priv->hid->string_index[1] = dev->device_descriptor.iProduct;
if (priv->hid->string_index[1] != 0)
- HidD_GetProductString(hid_handle, priv->hid->string[1], sizeof(priv->hid->string[1]));
+ // Using HidD_GetIndexedString() instead of HidD_GetProductString(), as the latter would otherwise return the name
+ // of the interface instead of the iProduct string whenever the iInterface member of the USB_INTERFACE_DESCRIPTOR
+ // structure for the interface is nonzero (see Remarks section in the documentation of the HID API routines)
+ HidD_GetIndexedString(hid_handle, priv->hid->string_index[1], priv->hid->string[1], sizeof(priv->hid->string[1]));
else
priv->hid->string[1][0] = 0;
@@ -3793,7 +3907,7 @@ 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)
{
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
HANDLE file_handle;
int i;
@@ -3813,7 +3927,7 @@ 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, uint8_t iface)
{
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
UNUSED(sub_api);
@@ -3829,7 +3943,7 @@ static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_han
handle_priv->interface_handle[iface].dev_handle = INTERFACE_CLAIMED;
- usbi_dbg("claimed interface %u", iface);
+ usbi_dbg(HANDLE_CTX(dev_handle), "claimed interface %u", iface);
handle_priv->active_interface = iface;
return LIBUSB_SUCCESS;
@@ -3837,7 +3951,7 @@ static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_han
static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, uint8_t iface)
{
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
UNUSED(sub_api);
@@ -3874,7 +3988,7 @@ static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itrans
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct winusb_transfer_priv *transfer_priv = get_winusb_transfer_priv(itransfer);
struct libusb_device_handle *dev_handle = transfer->dev_handle;
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev);
WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *)transfer->buffer;
HANDLE hid_handle;
@@ -3900,8 +4014,9 @@ static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itrans
return LIBUSB_ERROR_NOT_FOUND;
}
- usbi_dbg("will use interface %d", current_interface);
+ usbi_dbg(ITRANSFER_CTX(itransfer), "will use interface %d", current_interface);
+ transfer_priv->interface_number = (uint8_t)current_interface;
hid_handle = handle_priv->interface_handle[current_interface].api_handle;
set_transfer_priv_handle(itransfer, hid_handle);
overlapped = get_transfer_priv_overlapped(itransfer);
@@ -3963,8 +4078,6 @@ static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itrans
r = LIBUSB_SUCCESS;
}
- transfer_priv->interface_number = (uint8_t)current_interface;
-
return LIBUSB_SUCCESS;
}
@@ -3972,7 +4085,7 @@ static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer
{
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct winusb_transfer_priv *transfer_priv = get_winusb_transfer_priv(itransfer);
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(transfer->dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(transfer->dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev);
HANDLE hid_handle;
OVERLAPPED *overlapped;
@@ -3983,6 +4096,9 @@ static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer
UNUSED(sub_api);
CHECK_HID_AVAILABLE;
+ if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET))
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+
transfer_priv->hid_dest = NULL;
safe_free(transfer_priv->hid_buffer);
@@ -3992,8 +4108,9 @@ static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer
return LIBUSB_ERROR_NOT_FOUND;
}
- usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface);
+ usbi_dbg(TRANSFER_CTX(transfer), "matched endpoint %02X with interface %d", transfer->endpoint, current_interface);
+ transfer_priv->interface_number = (uint8_t)current_interface;
hid_handle = handle_priv->interface_handle[current_interface].api_handle;
set_transfer_priv_handle(itransfer, hid_handle);
overlapped = get_transfer_priv_overlapped(itransfer);
@@ -4015,7 +4132,7 @@ static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer
if (direction_in) {
transfer_priv->hid_dest = transfer->buffer;
- usbi_dbg("reading %d bytes (report ID: 0x00)", length);
+ usbi_dbg(TRANSFER_CTX(transfer), "reading %d bytes (report ID: 0x00)", length);
ret = ReadFile(hid_handle, transfer_priv->hid_buffer, length + 1, NULL, overlapped);
} else {
if (!priv->hid->uses_report_ids[1])
@@ -4024,7 +4141,7 @@ static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer
// 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]);
+ usbi_dbg(TRANSFER_CTX(transfer), "writing %d bytes (report ID: 0x%02X)", length, transfer_priv->hid_buffer[0]);
ret = WriteFile(hid_handle, transfer_priv->hid_buffer, length, NULL, overlapped);
}
@@ -4034,14 +4151,12 @@ static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer
return LIBUSB_ERROR_IO;
}
- transfer_priv->interface_number = (uint8_t)current_interface;
-
return LIBUSB_SUCCESS;
}
static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle)
{
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
HANDLE hid_handle;
int current_interface;
@@ -4060,7 +4175,7 @@ static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle
static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint)
{
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
HANDLE hid_handle;
int current_interface;
@@ -4074,7 +4189,7 @@ static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle,
return LIBUSB_ERROR_NOT_FOUND;
}
- usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface);
+ usbi_dbg(HANDLE_CTX(dev_handle), "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
@@ -4109,8 +4224,6 @@ static enum libusb_transfer_status hid_copy_transfer_data(int sub_api, struct us
}
if (transfer_priv->hid_buffer[0] == 0) {
- // Discard the 1 byte report ID prefix
- length--;
memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer + 1, length);
} else {
memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer, length);
@@ -4171,7 +4284,7 @@ static int composite_open(int sub_api, struct libusb_device_handle *dev_handle)
// open HID devices with a U2F usage unless running as administrator. We ignore this
// failure and proceed without the HID device opened.
if (r == LIBUSB_ERROR_ACCESS) {
- usbi_dbg("ignoring access denied error while opening HID interface of composite device");
+ usbi_dbg(HANDLE_CTX(dev_handle), "ignoring access denied error while opening HID interface of composite device");
r = LIBUSB_SUCCESS;
}
}
@@ -4280,7 +4393,7 @@ static int composite_submit_control_transfer(int sub_api, struct usbi_transfer *
// Try and target a specific interface if the control setup indicates such
if ((iface >= 0) && (iface < USB_MAXINTERFACES)) {
- usbi_dbg("attempting control transfer targeted to interface %d", iface);
+ usbi_dbg(TRANSFER_CTX(transfer), "attempting control transfer targeted to interface %d", iface);
if ((priv->usb_interface[iface].path != NULL)
&& (priv->usb_interface[iface].apib->submit_control_transfer != NULL)) {
r = priv->usb_interface[iface].apib->submit_control_transfer(priv->usb_interface[iface].sub_api, itransfer);
@@ -4296,10 +4409,10 @@ static int composite_submit_control_transfer(int sub_api, struct usbi_transfer *
if ((priv->usb_interface[iface].path != NULL)
&& (priv->usb_interface[iface].apib->submit_control_transfer != NULL)) {
if ((pass == 0) && (priv->usb_interface[iface].restricted_functionality)) {
- usbi_dbg("trying to skip restricted interface #%d (HID keyboard or mouse?)", iface);
+ usbi_dbg(TRANSFER_CTX(transfer), "trying to skip restricted interface #%d (HID keyboard or mouse?)", iface);
continue;
}
- usbi_dbg("using interface %d", iface);
+ usbi_dbg(TRANSFER_CTX(transfer), "using interface %d", iface);
r = priv->usb_interface[iface].apib->submit_control_transfer(priv->usb_interface[iface].sub_api, itransfer);
// If not supported on this API, it may be supported on another, so don't give up yet!!
if (r == LIBUSB_ERROR_NOT_SUPPORTED)
@@ -4316,7 +4429,7 @@ static int composite_submit_control_transfer(int sub_api, struct usbi_transfer *
static int composite_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer)
{
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(transfer->dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(transfer->dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev);
int current_interface;
@@ -4337,7 +4450,7 @@ static int composite_submit_bulk_transfer(int sub_api, struct usbi_transfer *itr
static int composite_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer)
{
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(transfer->dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(transfer->dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(transfer->dev_handle->dev);
int current_interface;
@@ -4357,7 +4470,7 @@ static int composite_submit_iso_transfer(int sub_api, struct usbi_transfer *itra
static int composite_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint)
{
- struct winusb_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+ struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
int current_interface;
diff --git a/libusb/os/windows_winusb.h b/libusb/os/windows_winusb.h
index 49355d4..6afd5cb 100644
--- a/libusb/os/windows_winusb.h
+++ b/libusb/os/windows_winusb.h
@@ -23,20 +23,10 @@
#ifndef LIBUSB_WINDOWS_WINUSB_H
#define LIBUSB_WINDOWS_WINUSB_H
-#include "windows_common.h"
-
-#if defined(_MSC_VER)
-// disable /W4 MSVC warnings that are benign
-#pragma warning(disable:4214) // bit field types other than int
-#endif
+#include <devioctl.h>
+#include <guiddef.h>
-// Missing from MSVC6 setupapi.h
-#ifndef SPDRP_ADDRESS
-#define SPDRP_ADDRESS 28
-#endif
-#ifndef SPDRP_INSTALL_STATE
-#define SPDRP_INSTALL_STATE 34
-#endif
+#include "windows_common.h"
#define MAX_CTRL_BUFFER_LENGTH 4096
#define MAX_USB_STRING_LENGTH 128
@@ -55,18 +45,10 @@
// 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
-#ifndef GUID_DEVINTERFACE_USB_HOST_CONTROLLER
-const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = {0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27}};
-#endif
-#ifndef GUID_DEVINTERFACE_USB_DEVICE
-const GUID GUID_DEVINTERFACE_USB_DEVICE = {0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}};
-#endif
-#ifndef GUID_DEVINTERFACE_USB_HUB
-const GUID GUID_DEVINTERFACE_USB_HUB = {0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8}};
-#endif
-#ifndef GUID_DEVINTERFACE_LIBUSB0_FILTER
-const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = {0xF9F3FF14, 0xAE21, 0x48A0, {0x8A, 0x25, 0x80, 0x11, 0xA7, 0xA9, 0x31, 0xD9}};
-#endif
+static const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = {0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27}};
+static const GUID GUID_DEVINTERFACE_USB_HUB = {0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8}};
+static const GUID GUID_DEVINTERFACE_USB_DEVICE = {0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}};
+static const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = {0xF9F3FF14, 0xAE21, 0x48A0, {0x8A, 0x25, 0x80, 0x11, 0xA7, 0xA9, 0x31, 0xD9}};
// The following define MUST be == sizeof(USB_DESCRIPTOR_REQUEST)
#define USB_DESCRIPTOR_REQUEST_SIZE 12U
@@ -114,7 +96,7 @@ struct windows_usb_api_backend {
extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX];
#define PRINT_UNSUPPORTED_API(fname) \
- usbi_dbg("unsupported API call for '%s' " \
+ usbi_dbg(NULL, "unsupported API call for '%s' " \
"(unrecognized device driver)", #fname)
#define CHECK_SUPPORTED_API(apip, fname) \
@@ -152,11 +134,6 @@ struct libusb_hid_descriptor {
#define LIBUSB_REQ_IN(request_type) ((request_type) & LIBUSB_ENDPOINT_IN)
#define LIBUSB_REQ_OUT(request_type) (!LIBUSB_REQ_IN(request_type))
-#ifndef CTL_CODE
-#define CTL_CODE(DeviceType, Function, Method, Access) \
- (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
-#endif
-
// The following are used for HID reports IOCTLs
#define HID_IN_CTL_CODE(id) \
CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_IN_DIRECT, FILE_ANY_ACCESS)
@@ -259,13 +236,9 @@ DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG));
/* AdvAPI32 dependencies */
DLL_DECLARE_HANDLE(AdvAPI32);
-DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegQueryValueExW, (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD));
+DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegQueryValueExA, (HKEY, LPCSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD));
DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY));
-/* OLE32 dependency */
-DLL_DECLARE_HANDLE(OLE32);
-DLL_DECLARE_FUNC_PREFIXED(WINAPI, HRESULT, p, IIDFromString, (LPCOLESTR, LPIID));
-
/* SetupAPI dependencies */
DLL_DECLARE_HANDLE(SetupAPI);
DLL_DECLARE_FUNC_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (LPCGUID, PCSTR, HWND, DWORD));
@@ -282,23 +255,12 @@ DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiDestroyDeviceInfoList, (HDEVIN
DLL_DECLARE_FUNC_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDevRegKey, (HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM));
DLL_DECLARE_FUNC_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDeviceInterfaceRegKey, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, DWORD, DWORD));
+#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN
-#ifndef USB_GET_NODE_INFORMATION
#define USB_GET_NODE_INFORMATION 258
-#endif
-#ifndef USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION
#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260
-#endif
-#ifndef USB_GET_NODE_CONNECTION_INFORMATION_EX
#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274
-#endif
-#ifndef USB_GET_NODE_CONNECTION_INFORMATION_EX_V2
#define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 279
-#endif
-
-#ifndef FILE_DEVICE_USB
-#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN
-#endif
#define USB_CTL_CODE(id) \
CTL_CODE(FILE_DEVICE_USB, (id), METHOD_BUFFERED, FILE_ANY_ACCESS)
@@ -342,6 +304,12 @@ typedef enum _USB_HUB_NODE {
UsbMIParent
} USB_HUB_NODE;
+#if defined(_MSC_VER)
+// disable /W4 MSVC warnings that are benign
+#pragma warning(push)
+#pragma warning(disable:4214) // bit field types other than int
+#endif
+
// Most of the structures below need to be packed
#include <pshpack1.h>
@@ -439,6 +407,11 @@ typedef struct _USB_NODE_CONNECTION_INFORMATION_EX_V2 {
#include <poppack.h>
+#if defined(_MSC_VER)
+// Restore original warnings
+#pragma warning(pop)
+#endif
+
/* winusb.dll interface */
/* pipe policies */
diff --git a/libusb/sync.c b/libusb/sync.c
index adc95b4..1fa1f0b 100644
--- a/libusb/sync.c
+++ b/libusb/sync.c
@@ -36,7 +36,7 @@ static void LIBUSB_CALL sync_transfer_cb(struct libusb_transfer *transfer)
{
int *completed = transfer->user_data;
*completed = 1;
- usbi_dbg("actual_length=%d", transfer->actual_length);
+ usbi_dbg(TRANSFER_CTX(transfer), "actual_length=%d", transfer->actual_length);
/* caller interprets result and frees transfer */
}
diff --git a/libusb/version.h b/libusb/version.h
index d8ebde4..fe95d84 100644
--- a/libusb/version.h
+++ b/libusb/version.h
@@ -7,7 +7,7 @@
#define LIBUSB_MINOR 0
#endif
#ifndef LIBUSB_MICRO
-#define LIBUSB_MICRO 24
+#define LIBUSB_MICRO 26
#endif
#ifndef LIBUSB_NANO
#define LIBUSB_NANO 0
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index 0f100a8..dbd5d5f 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 11584
+#define LIBUSB_NANO 11724
diff --git a/msvc/libusb_dll_2013.vcxproj b/msvc/libusb_dll_2013.vcxproj
index 56ffd75..03212dc 100644
--- a/msvc/libusb_dll_2013.vcxproj
+++ b/msvc/libusb_dll_2013.vcxproj
@@ -83,7 +83,6 @@
<ItemGroup>
<ClInclude Include=".\config.h" />
<ClInclude Include="..\libusb\os\events_windows.h" />
- <ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
diff --git a/msvc/libusb_dll_2013.vcxproj.filters b/msvc/libusb_dll_2013.vcxproj.filters
index 8da28e3..c8643f2 100644
--- a/msvc/libusb_dll_2013.vcxproj.filters
+++ b/msvc/libusb_dll_2013.vcxproj.filters
@@ -21,9 +21,6 @@
<ClInclude Include="..\libusb\os\events_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\hotplug.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\libusb.h">
<Filter>Header Files</Filter>
</ClInclude>
diff --git a/msvc/libusb_dll_2015.vcxproj b/msvc/libusb_dll_2015.vcxproj
index d2c850d..f24d94b 100644
--- a/msvc/libusb_dll_2015.vcxproj
+++ b/msvc/libusb_dll_2015.vcxproj
@@ -84,7 +84,6 @@
<ItemGroup>
<ClInclude Include=".\config.h" />
<ClInclude Include="..\libusb\os\events_windows.h" />
- <ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
diff --git a/msvc/libusb_dll_2015.vcxproj.filters b/msvc/libusb_dll_2015.vcxproj.filters
index 8da28e3..c8643f2 100644
--- a/msvc/libusb_dll_2015.vcxproj.filters
+++ b/msvc/libusb_dll_2015.vcxproj.filters
@@ -21,9 +21,6 @@
<ClInclude Include="..\libusb\os\events_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\hotplug.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\libusb.h">
<Filter>Header Files</Filter>
</ClInclude>
diff --git a/msvc/libusb_dll_2017.vcxproj b/msvc/libusb_dll_2017.vcxproj
index 598159d..2ff2f94 100644
--- a/msvc/libusb_dll_2017.vcxproj
+++ b/msvc/libusb_dll_2017.vcxproj
@@ -103,7 +103,6 @@
<ItemGroup>
<ClInclude Include=".\config.h" />
<ClInclude Include="..\libusb\os\events_windows.h" />
- <ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
diff --git a/msvc/libusb_dll_2017.vcxproj.filters b/msvc/libusb_dll_2017.vcxproj.filters
index 8da28e3..c8643f2 100644
--- a/msvc/libusb_dll_2017.vcxproj.filters
+++ b/msvc/libusb_dll_2017.vcxproj.filters
@@ -21,9 +21,6 @@
<ClInclude Include="..\libusb\os\events_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\hotplug.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\libusb.h">
<Filter>Header Files</Filter>
</ClInclude>
diff --git a/msvc/libusb_dll_2019.vcxproj b/msvc/libusb_dll_2019.vcxproj
index dbd8717..266166e 100644
--- a/msvc/libusb_dll_2019.vcxproj
+++ b/msvc/libusb_dll_2019.vcxproj
@@ -103,7 +103,6 @@
<ItemGroup>
<ClInclude Include=".\config.h" />
<ClInclude Include="..\libusb\os\events_windows.h" />
- <ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
diff --git a/msvc/libusb_dll_2019.vcxproj.filters b/msvc/libusb_dll_2019.vcxproj.filters
index 8da28e3..c8643f2 100644
--- a/msvc/libusb_dll_2019.vcxproj.filters
+++ b/msvc/libusb_dll_2019.vcxproj.filters
@@ -21,9 +21,6 @@
<ClInclude Include="..\libusb\os\events_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\hotplug.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\libusb.h">
<Filter>Header Files</Filter>
</ClInclude>
diff --git a/msvc/libusb_static_2013.vcxproj b/msvc/libusb_static_2013.vcxproj
index 1b287e5..94ba597 100644
--- a/msvc/libusb_static_2013.vcxproj
+++ b/msvc/libusb_static_2013.vcxproj
@@ -79,7 +79,6 @@
<ItemGroup>
<ClInclude Include=".\config.h" />
<ClInclude Include="..\libusb\os\events_windows.h" />
- <ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
diff --git a/msvc/libusb_static_2013.vcxproj.filters b/msvc/libusb_static_2013.vcxproj.filters
index 2994ca1..a3294da 100644
--- a/msvc/libusb_static_2013.vcxproj.filters
+++ b/msvc/libusb_static_2013.vcxproj.filters
@@ -17,9 +17,6 @@
<ClInclude Include="..\libusb\os\events_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\hotplug.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\libusb.h">
<Filter>Header Files</Filter>
</ClInclude>
diff --git a/msvc/libusb_static_2015.vcxproj b/msvc/libusb_static_2015.vcxproj
index 9fa30da..f951523 100644
--- a/msvc/libusb_static_2015.vcxproj
+++ b/msvc/libusb_static_2015.vcxproj
@@ -80,7 +80,6 @@
<ItemGroup>
<ClInclude Include=".\config.h" />
<ClInclude Include="..\libusb\os\events_windows.h" />
- <ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
diff --git a/msvc/libusb_static_2015.vcxproj.filters b/msvc/libusb_static_2015.vcxproj.filters
index 2994ca1..a3294da 100644
--- a/msvc/libusb_static_2015.vcxproj.filters
+++ b/msvc/libusb_static_2015.vcxproj.filters
@@ -17,9 +17,6 @@
<ClInclude Include="..\libusb\os\events_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\hotplug.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\libusb.h">
<Filter>Header Files</Filter>
</ClInclude>
diff --git a/msvc/libusb_static_2017.vcxproj b/msvc/libusb_static_2017.vcxproj
index 62076e0..857ee3f 100644
--- a/msvc/libusb_static_2017.vcxproj
+++ b/msvc/libusb_static_2017.vcxproj
@@ -99,7 +99,6 @@
<ItemGroup>
<ClInclude Include=".\config.h" />
<ClInclude Include="..\libusb\os\events_windows.h" />
- <ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
diff --git a/msvc/libusb_static_2017.vcxproj.filters b/msvc/libusb_static_2017.vcxproj.filters
index 2994ca1..a3294da 100644
--- a/msvc/libusb_static_2017.vcxproj.filters
+++ b/msvc/libusb_static_2017.vcxproj.filters
@@ -17,9 +17,6 @@
<ClInclude Include="..\libusb\os\events_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\hotplug.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\libusb.h">
<Filter>Header Files</Filter>
</ClInclude>
diff --git a/msvc/libusb_static_2019.vcxproj b/msvc/libusb_static_2019.vcxproj
index 60ad642..036ce95 100644
--- a/msvc/libusb_static_2019.vcxproj
+++ b/msvc/libusb_static_2019.vcxproj
@@ -99,7 +99,6 @@
<ItemGroup>
<ClInclude Include=".\config.h" />
<ClInclude Include="..\libusb\os\events_windows.h" />
- <ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
diff --git a/msvc/libusb_static_2019.vcxproj.filters b/msvc/libusb_static_2019.vcxproj.filters
index 2994ca1..a3294da 100644
--- a/msvc/libusb_static_2019.vcxproj.filters
+++ b/msvc/libusb_static_2019.vcxproj.filters
@@ -17,9 +17,6 @@
<ClInclude Include="..\libusb\os\events_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\hotplug.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\libusb.h">
<Filter>Header Files</Filter>
</ClInclude>
diff --git a/test b/test
deleted file mode 100644
index e69de29..0000000
--- a/test
+++ /dev/null
diff --git a/tests/Makefile.am b/tests/Makefile.am
index cb8fad9..cf6237e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -2,6 +2,17 @@ AM_CPPFLAGS = -I$(top_srcdir)/libusb
LDADD = ../libusb/libusb-1.0.la
LIBS =
+stress_SOURCES = stress.c libusb_testlib.h testlib.c
+
noinst_PROGRAMS = stress
-stress_SOURCES = stress.c libusb_testlib.h testlib.c
+if BUILD_UMOCKDEV_TEST
+# NOTE: We add libumockdev-preload.so so that we can run tests in-process
+# We also use -Wl,-lxxx as the compiler doesn't need it and libtool
+# would reorder the flags otherwise.
+umockdev_CPPFLAGS = ${UMOCKDEV_CFLAGS} -I$(top_srcdir)/libusb
+umockdev_LDFLAGS = -Wl,--push-state,--no-as-needed -Wl,-lumockdev-preload -Wl,--pop-state ${UMOCKDEV_LIBS}
+umockdev_SOURCES = umockdev.c
+
+noinst_PROGRAMS += umockdev
+endif
diff --git a/tests/stress.c b/tests/stress.c
index 6dcb8f3..0dc9173 100644
--- a/tests/stress.c
+++ b/tests/stress.c
@@ -130,10 +130,13 @@ static libusb_testlib_result test_default_context_change(void)
return TEST_STATUS_FAILURE;
}
- /* Enable debug output, to be sure to use the context */
- libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);
+ /* Enable debug output on new context, to be sure to use the context */
libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);
+ /* Enable debug output on the default context. This should work even before
+ * the context has been created. */
+ libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);
+
/* Now create a reference to the default context */
r = libusb_init(NULL);
if (r != LIBUSB_SUCCESS) {
diff --git a/tests/umockdev.c b/tests/umockdev.c
new file mode 100644
index 0000000..488edc2
--- /dev/null
+++ b/tests/umockdev.c
@@ -0,0 +1,1175 @@
+/*
+ * libusb umockdev based tests
+ *
+ * Copyright (C) 2022 Benjamin Berg <bberg@redhat.com>
+ *
+ * This program 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 program 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 program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <linux/ioctl.h>
+#include <linux/usbdevice_fs.h>
+
+#include "libusb.h"
+
+#include "umockdev.h"
+
+#define UNUSED_DATA __attribute__ ((unused)) gconstpointer unused_data
+
+/* avoid leak reports inside assertions; leaking stuff on assertion failures does not matter in tests */
+#if !defined(__clang__)
+#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
+#pragma GCC diagnostic ignored "-Wanalyzer-file-leak"
+#endif
+
+typedef struct {
+ pid_t thread;
+ libusb_context *ctx;
+ enum libusb_log_level level;
+ char *str;
+} LogMessage;
+
+static void
+log_message_free(LogMessage *msg)
+{
+ g_free(msg->str);
+ g_free(msg);
+}
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(LogMessage, log_message_free)
+
+typedef struct _UsbChat UsbChat;
+
+struct _UsbChat {
+ gboolean submit;
+ gboolean reap;
+ UsbChat *reaps;
+ UsbChat *next;
+
+ /* struct usbdevfs_urb */
+ unsigned char type;
+ unsigned char endpoint;
+ int status;
+ unsigned int flags;
+ const unsigned char *buffer;
+ int buffer_length;
+ int actual_length;
+
+ /* <submit urb> */
+ UMockdevIoctlData *submit_urb;
+};
+
+typedef struct {
+ UMockdevTestbed *testbed;
+ UMockdevIoctlBase *handler;
+ struct libusb_context *ctx;
+
+ gchar *root_dir;
+ gchar *sys_dir;
+
+ gboolean libusb_log_silence;
+ GList *libusb_log;
+
+ UsbChat *chat;
+ GList *flying_urbs;
+ GList *discarded_urbs;
+
+ /* GMutex confuses tsan unecessarily */
+ pthread_mutex_t mutex;
+} UMockdevTestbedFixture;
+
+/* Global for log handler */
+static UMockdevTestbedFixture *cur_fixture = NULL;
+
+static void
+log_handler(libusb_context *ctx, enum libusb_log_level level, const char *str)
+{
+ /* May be called from different threads without synchronization! */
+ LogMessage *msg;
+ pid_t tid = gettid();
+
+ g_assert (cur_fixture != NULL);
+ g_assert(pthread_mutex_lock(&cur_fixture->mutex) == 0);
+
+ msg = g_new0(LogMessage, 1);
+ msg->ctx = ctx;
+ msg->level = level;
+ msg->str = g_strchomp (g_strdup(str));
+ msg->thread = tid;
+
+ if (!cur_fixture->libusb_log_silence)
+ g_printerr("%s\n", msg->str);
+
+ cur_fixture->libusb_log = g_list_append(cur_fixture->libusb_log, msg);
+ pthread_mutex_unlock(&cur_fixture->mutex);
+}
+
+static void
+log_handler_null(libusb_context *ctx, enum libusb_log_level level, const char *str)
+{
+ (void) ctx;
+ (void) level;
+ (void) str;
+}
+
+static void
+clear_libusb_log(UMockdevTestbedFixture * fixture, enum libusb_log_level level)
+{
+ g_assert(pthread_mutex_lock(&fixture->mutex) == 0);
+
+ while (fixture->libusb_log) {
+ LogMessage *msg = fixture->libusb_log->data;
+
+ g_assert(msg->ctx == fixture->ctx);
+
+ if (msg->level < level) {
+ pthread_mutex_unlock(&fixture->mutex);
+ return;
+ }
+
+ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log);
+ log_message_free(msg);
+ }
+ pthread_mutex_unlock(&fixture->mutex);
+}
+
+static void
+assert_libusb_log_msg(UMockdevTestbedFixture * fixture, enum libusb_log_level level, const char *re)
+{
+ g_assert(pthread_mutex_lock(&fixture->mutex) == 0);
+
+ while (fixture->libusb_log) {
+ g_autoptr(LogMessage) msg = NULL;
+
+ if (fixture->libusb_log == NULL)
+ g_error ("No level %d message found searching for %s", level, re);
+
+ msg = fixture->libusb_log->data;
+ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log);
+
+ if (msg->ctx != fixture->ctx)
+ g_error ("Saw unexpected message \"%s\" from context %p while %p was expected",
+ msg->str, msg->ctx, fixture->ctx);
+
+ if (msg->level == level && g_regex_match_simple(re, msg->str, 0, 0)) {
+ pthread_mutex_unlock(&fixture->mutex);
+ return;
+ }
+
+ /* Allow skipping INFO and DEBUG messages */
+ if (msg->level >= LIBUSB_LOG_LEVEL_INFO)
+ continue;
+
+ g_error ("Searched for \"%s\" (%d) but found \"%s\" (%d)", re, level, msg->str, msg->level);
+ }
+
+ pthread_mutex_unlock(&fixture->mutex);
+ g_error ("Searched for \"%s\" (%d) but no message matched", re, level);
+}
+
+static void
+assert_libusb_no_log_msg(UMockdevTestbedFixture * fixture, enum libusb_log_level level, const char *re)
+{
+ g_assert(pthread_mutex_lock(&fixture->mutex) == 0);
+
+ while (fixture->libusb_log) {
+ g_autoptr(LogMessage) msg = NULL;
+ gboolean matching;
+
+ msg = fixture->libusb_log->data;
+ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log);
+
+ g_assert(msg->ctx == fixture->ctx);
+
+ matching = (msg->level == level && g_regex_match_simple(re, msg->str, 0, 0));
+
+ /* Allow skipping INFO and DEBUG messages */
+ if (!matching && msg->level >= LIBUSB_LOG_LEVEL_INFO)
+ continue;
+
+ g_error ("Asserting \"%s\" (%d) not logged and found \"%s\" (%d)", re, level, msg->str, msg->level);
+ }
+
+ pthread_mutex_unlock(&fixture->mutex);
+}
+
+static void
+dump_buffer(const unsigned char *buffer, int len)
+{
+ g_autoptr(GString) line = NULL;
+
+ line = g_string_new ("");
+ for (gint i = 0; i < len; i++) {
+ g_string_append_printf(line, "%02x ", buffer[i]);
+ if ((i + 1) % 16 == 0) {
+ g_printerr(" %s\n", line->str);
+ g_string_set_size(line, 0);
+ }
+ }
+
+ if (line->len)
+ g_printerr(" %s\n", line->str);
+}
+
+static gint
+cmp_ioctl_data_addr(const void *data, const void *addr)
+{
+ return ((const UMockdevIoctlData*) data)->client_addr != (gulong) addr;
+}
+
+static gboolean
+handle_ioctl_cb (UMockdevIoctlBase *handler, UMockdevIoctlClient *client, UMockdevTestbedFixture *fixture)
+{
+ UMockdevIoctlData *ioctl_arg;
+ long int request;
+ struct usbdevfs_urb *urb;
+
+ (void) handler;
+
+ request = umockdev_ioctl_client_get_request (client);
+ ioctl_arg = umockdev_ioctl_client_get_arg (client);
+
+ /* NOTE: We share the address space, dereferencing pointers *will* work.
+ * However, to make tsan work, we still stick to the API that resolves
+ * the data into a local copy! */
+
+ switch (request) {
+ case USBDEVFS_GET_CAPABILITIES: {
+ g_autoptr(UMockdevIoctlData) d = NULL;
+ d = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(guint32), NULL);
+
+ *(guint32*) d->data = USBDEVFS_CAP_BULK_SCATTER_GATHER |
+ USBDEVFS_CAP_BULK_CONTINUATION |
+ USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
+ USBDEVFS_CAP_REAP_AFTER_DISCONNECT |
+ USBDEVFS_CAP_ZERO_PACKET;
+
+ umockdev_ioctl_client_complete(client, 0, 0);
+ return TRUE;
+ }
+
+ case USBDEVFS_CLAIMINTERFACE:
+ case USBDEVFS_RELEASEINTERFACE:
+ case USBDEVFS_CLEAR_HALT:
+ case USBDEVFS_RESET:
+ case USBDEVFS_RESETEP:
+ umockdev_ioctl_client_complete(client, 0, 0);
+ return TRUE;
+
+ case USBDEVFS_SUBMITURB: {
+ g_autoptr(UMockdevIoctlData) urb_buffer = NULL;
+ g_autoptr(UMockdevIoctlData) urb_data = NULL;
+ gsize buflen;
+
+ if (!fixture->chat || !fixture->chat->submit)
+ return FALSE;
+
+ buflen = fixture->chat->buffer_length;
+ if (fixture->chat->type == USBDEVFS_URB_TYPE_CONTROL)
+ buflen = 8;
+
+ urb_data = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(struct usbdevfs_urb), NULL);
+ urb = (struct usbdevfs_urb*) urb_data->data;
+ urb_buffer = umockdev_ioctl_data_resolve(urb_data, G_STRUCT_OFFSET(struct usbdevfs_urb, buffer), urb->buffer_length, NULL);
+
+ if (fixture->chat->type == urb->type &&
+ fixture->chat->endpoint == urb->endpoint &&
+ fixture->chat->buffer_length == urb->buffer_length &&
+ (fixture->chat->buffer == NULL || memcmp (fixture->chat->buffer, urb_buffer->data, buflen) == 0)) {
+ fixture->flying_urbs = g_list_append (fixture->flying_urbs, umockdev_ioctl_data_ref(urb_data));
+
+ if (fixture->chat->reaps)
+ fixture->chat->reaps->submit_urb = urb_data;
+
+ if (fixture->chat->status)
+ umockdev_ioctl_client_complete(client, -1, -fixture->chat->status);
+ else
+ umockdev_ioctl_client_complete(client, 0, 0);
+
+ if (fixture->chat->next)
+ fixture->chat = fixture->chat->next;
+ else
+ fixture->chat += 1;
+ return TRUE;
+ }
+
+ /* chat message didn't match, don't accept it */
+ g_printerr("Could not process submit urb:\n");
+ g_printerr(" t: %d, ep: %d, actual_length: %d, buffer_length: %d\n",
+ urb->type, urb->endpoint, urb->actual_length, urb->buffer_length);
+ if (urb->type == USBDEVFS_URB_TYPE_CONTROL || urb->endpoint & LIBUSB_ENDPOINT_IN)
+ dump_buffer(urb->buffer, urb->buffer_length);
+ g_printerr("Looking for:\n");
+ g_printerr(" t: %d, ep: %d, actual_length: %d, buffer_length: %d\n",
+ fixture->chat->type, fixture->chat->endpoint,
+ fixture->chat->actual_length, fixture->chat->buffer_length);
+ if (fixture->chat->buffer)
+ dump_buffer(fixture->chat->buffer, buflen);
+
+ return FALSE;
+ }
+
+ case USBDEVFS_REAPURB:
+ case USBDEVFS_REAPURBNDELAY: {
+ g_autoptr(UMockdevIoctlData) urb_ptr = NULL;
+ g_autoptr(UMockdevIoctlData) urb_data = NULL;
+
+ if (fixture->discarded_urbs) {
+ urb_data = fixture->discarded_urbs->data;
+ urb = (struct usbdevfs_urb*) urb_data->data;
+ fixture->discarded_urbs = g_list_delete_link(fixture->discarded_urbs, fixture->discarded_urbs);
+ urb->status = -ENOENT;
+
+ urb_ptr = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(gpointer), NULL);
+ umockdev_ioctl_data_set_ptr(urb_ptr, 0, urb_data);
+
+ umockdev_ioctl_client_complete(client, 0, 0);
+ return TRUE;
+ }
+
+ if (fixture->chat && fixture->chat->reap) {
+ GList *l = g_list_find(fixture->flying_urbs, fixture->chat->submit_urb);
+
+ if (l) {
+ fixture->flying_urbs = g_list_remove_link(fixture->flying_urbs, fixture->flying_urbs);
+
+ urb_data = fixture->chat->submit_urb;
+ urb = (struct usbdevfs_urb*) urb_data->data;
+ urb->actual_length = fixture->chat->actual_length;
+ if (urb->type == USBDEVFS_URB_TYPE_CONTROL && urb->actual_length)
+ urb->actual_length -= 8;
+ if (fixture->chat->buffer)
+ memcpy(urb->buffer, fixture->chat->buffer, fixture->chat->actual_length);
+ urb->status = fixture->chat->status;
+
+ urb_ptr = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(gpointer), NULL);
+ umockdev_ioctl_data_set_ptr(urb_ptr, 0, urb_data);
+ if (fixture->chat->next)
+ fixture->chat = fixture->chat->next;
+ else
+ fixture->chat += 1;
+ umockdev_ioctl_client_complete(client, 0, 0);
+ return TRUE;
+ }
+ }
+
+ /* Nothing to reap */
+ umockdev_ioctl_client_complete(client, -1, EAGAIN);
+ return TRUE;
+ }
+
+ case USBDEVFS_DISCARDURB: {
+ GList *l = g_list_find_custom(fixture->flying_urbs, *(void**) ioctl_arg->data, cmp_ioctl_data_addr);
+
+ if (l) {
+ fixture->discarded_urbs = g_list_append(fixture->discarded_urbs, l->data);
+ fixture->flying_urbs = g_list_delete_link(fixture->flying_urbs, l);
+ umockdev_ioctl_client_complete(client, 0, 0);
+ } else {
+ umockdev_ioctl_client_complete(client, -1, EINVAL);
+ }
+
+ return TRUE;
+ }
+
+ default:
+ return FALSE;
+ }
+}
+
+static void
+test_fixture_add_canon(UMockdevTestbedFixture * fixture)
+{
+ /* Setup first, so we can be sure libusb_open works when the add uevent
+ * happens.
+ */
+ g_assert_cmpint(umockdev_testbed_attach_ioctl(fixture->testbed, "/dev/bus/usb/001/001", fixture->handler, NULL), ==, 1);
+
+ /* NOTE: add_device would not create a file, needed for device emulation */
+ /* XXX: Racy, see https://github.com/martinpitt/umockdev/issues/173 */
+ umockdev_testbed_add_from_string(fixture->testbed,
+ "P: /devices/usb1\n"
+ "N: bus/usb/001/001\n"
+ "E: SUBSYSTEM=usb\n"
+ "E: DRIVER=usb\n"
+ "E: BUSNUM=001\n"
+ "E: DEVNUM=001\n"
+ "E: DEVNAME=/dev/bus/usb/001/001\n"
+ "E: DEVTYPE=usb_device\n"
+ "A: bConfigurationValue=1\\n\n"
+ "A: busnum=1\\n\n"
+ "A: devnum=1\\n\n"
+ "A: bConfigurationValue=1\\n\n"
+ "A: speed=480\\n\n"
+ /* descriptor from a Canon PowerShot SX200; VID 04a9 PID 31c0 */
+ "H: descriptors="
+ "1201000200000040a904c03102000102"
+ "030109022700010100c0010904000003"
+ "06010100070581020002000705020200"
+ "020007058303080009\n",
+ NULL);
+}
+
+static void
+test_fixture_setup_libusb(UMockdevTestbedFixture * fixture, int devcount)
+{
+ libusb_device **devs = NULL;
+
+ libusb_init (&fixture->ctx);
+
+ /* Supress global log messages completely
+ * (though, in some tests it might be interesting to check there are no real ones).
+ */
+ libusb_set_log_cb (NULL, log_handler_null, LIBUSB_LOG_CB_GLOBAL);
+ libusb_set_option (fixture->ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);
+ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, devcount);
+ libusb_free_device_list(devs, TRUE);
+ libusb_set_log_cb (fixture->ctx, log_handler, LIBUSB_LOG_CB_CONTEXT);
+}
+
+static void
+test_fixture_setup_common(UMockdevTestbedFixture * fixture)
+{
+ g_assert(cur_fixture == NULL);
+ cur_fixture = fixture;
+
+ pthread_mutex_init(&fixture->mutex, NULL);
+
+ fixture->testbed = umockdev_testbed_new();
+ g_assert(fixture->testbed != NULL);
+ fixture->root_dir = umockdev_testbed_get_root_dir(fixture->testbed);
+ fixture->sys_dir = umockdev_testbed_get_sys_dir(fixture->testbed);
+
+ fixture->handler = umockdev_ioctl_base_new();
+ g_object_connect(fixture->handler, "signal-after::handle-ioctl", handle_ioctl_cb, fixture, NULL);
+}
+
+static void
+test_fixture_setup_empty(UMockdevTestbedFixture * fixture, UNUSED_DATA)
+{
+ test_fixture_setup_common(fixture);
+
+ test_fixture_setup_libusb(fixture, 0);
+}
+
+static void
+test_fixture_setup_with_canon(UMockdevTestbedFixture * fixture, UNUSED_DATA)
+{
+ test_fixture_setup_common(fixture);
+
+ test_fixture_add_canon(fixture);
+
+ test_fixture_setup_libusb(fixture, 1);
+}
+
+static void
+test_fixture_teardown(UMockdevTestbedFixture * fixture, UNUSED_DATA)
+{
+ g_assert(cur_fixture == fixture);
+
+ /* Abort if there are any warnings/errors in the log */
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO);
+
+ if (fixture->ctx) {
+ libusb_device **devs = NULL;
+ int count = libusb_get_device_list(fixture->ctx, &devs);
+ libusb_free_device_list(devs, TRUE);
+
+ libusb_exit (fixture->ctx);
+
+ /* libusb_exit should result in the correct number of devices being destroyed */
+ for (int i = 0; i < count; i++)
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_unref_device");
+
+ assert_libusb_no_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_unref_device");
+ }
+ libusb_set_log_cb (NULL, NULL, LIBUSB_LOG_CB_GLOBAL);
+ cur_fixture = NULL;
+
+ /* Abort if there are any warnings/errors in the log */
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO);
+ fixture->ctx = NULL;
+ g_assert_null(fixture->libusb_log);
+
+ g_clear_object(&fixture->handler);
+ g_clear_object(&fixture->testbed);
+
+ /* verify that temp dir gets cleaned up properly */
+ g_assert(!g_file_test(fixture->root_dir, G_FILE_TEST_EXISTS));
+ g_free(fixture->root_dir);
+ g_free(fixture->sys_dir);
+
+ while (fixture->flying_urbs) {
+ umockdev_ioctl_data_unref (fixture->flying_urbs->data);
+ fixture->flying_urbs = g_list_delete_link (fixture->flying_urbs, fixture->flying_urbs);
+ }
+
+ pthread_mutex_destroy(&fixture->mutex);
+}
+
+static void
+test_open_close(UMockdevTestbedFixture * fixture, UNUSED_DATA)
+{
+ libusb_device **devs = NULL;
+ struct libusb_device_descriptor desc;
+ libusb_device_handle *handle = NULL;
+
+ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, 1);
+ /* The linux_enumerate_device may happen from a different thread */
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_get_device_list");
+ /* We have exactly one device */
+ g_assert_cmpint(libusb_get_bus_number(devs[0]), ==, 1);
+ g_assert_cmpint(libusb_get_device_address(devs[0]), ==, 1);
+
+ /* Get/Check descriptor */
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO);
+ libusb_get_device_descriptor (devs[0], &desc);
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_get_device_descriptor");
+ g_assert_cmpint(desc.idVendor, ==, 0x04a9);
+ g_assert_cmpint(desc.idProduct, ==, 0x31c0);
+
+ /* Open and close */
+ g_assert_cmpint(libusb_open(devs[0], &handle), ==, 0);
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "usbi_add_event_source");
+ g_assert_nonnull(handle);
+ libusb_close(handle);
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "usbi_remove_event_source");
+
+ libusb_free_device_list(devs, TRUE);
+
+ /* Open and close using vid/pid */
+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0);
+ g_assert_nonnull(handle);
+ libusb_close(handle);
+}
+
+static void
+test_implicit_default(UMockdevTestbedFixture * fixture, UNUSED_DATA)
+{
+ libusb_device **devs = NULL;
+
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO);
+ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1);
+ libusb_free_device_list(devs, TRUE);
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[usbi_get_context\\].*implicit default");
+
+ /* Only warns once */
+ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1);
+ libusb_free_device_list(devs, TRUE);
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO);
+
+ libusb_init(NULL);
+ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1);
+ libusb_exit(NULL);
+
+ /* We free late, causing a warning from libusb_exit. However,
+ * we never see this warning (i.e. test success) because it is on a
+ * different context.
+ */
+ libusb_free_device_list(devs, TRUE);
+}
+
+static void
+test_close_flying(UMockdevTestbedFixture * fixture, UNUSED_DATA)
+{
+ UsbChat chat[] = {
+ {
+ .submit = TRUE,
+ .type = USBDEVFS_URB_TYPE_BULK,
+ .endpoint = LIBUSB_ENDPOINT_OUT,
+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 },
+ .buffer_length = 4,
+ },
+ { .submit = FALSE }
+ };
+ libusb_device_handle *handle = NULL;
+ struct libusb_transfer *transfer = NULL;
+
+ fixture->chat = chat;
+
+ /* Open */
+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0);
+ g_assert_nonnull(handle);
+
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer,
+ handle,
+ LIBUSB_ENDPOINT_OUT,
+ (unsigned char*) chat[0].buffer,
+ chat[0].buffer_length,
+ NULL,
+ NULL,
+ 1);
+
+ /* Submit */
+ libusb_submit_transfer(transfer);
+
+ /* Closing logs fat error (two lines) */
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG);
+ libusb_close(handle);
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*connected as far as we know");
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*cancellation hasn't even been scheduled");
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[do_close\\] Removed transfer");
+
+ /* Free'ing the transfer works, and logs to the right context */
+ libusb_free_transfer(transfer);
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[libusb_free_transfer\\]");
+}
+
+static void
+test_close_cancelled(UMockdevTestbedFixture * fixture, UNUSED_DATA)
+{
+ UsbChat chat[] = {
+ {
+ .submit = TRUE,
+ .type = USBDEVFS_URB_TYPE_BULK,
+ .endpoint = LIBUSB_ENDPOINT_OUT,
+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 },
+ .buffer_length = 4,
+ },
+ { .submit = FALSE }
+ };
+ libusb_device_handle *handle = NULL;
+ struct libusb_transfer *transfer = NULL;
+
+ fixture->chat = chat;
+
+ /* Open */
+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0);
+ g_assert_nonnull(handle);
+
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer,
+ handle,
+ LIBUSB_ENDPOINT_OUT,
+ (unsigned char*) chat[0].buffer,
+ chat[0].buffer_length,
+ NULL,
+ NULL,
+ 1);
+
+ /* Submit */
+ libusb_submit_transfer(transfer);
+ libusb_cancel_transfer(transfer);
+
+ /* Closing logs fat error (two lines) */
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG);
+ libusb_close(handle);
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*connected as far as we know");
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[do_close\\] .*cancellation.*hasn't completed");
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[do_close\\] Removed transfer");
+
+ libusb_free_transfer(transfer);
+}
+
+static void
+test_ctx_destroy(UMockdevTestbedFixture * fixture, UNUSED_DATA)
+{
+ UsbChat chat[] = {
+ {
+ .submit = TRUE,
+ .type = USBDEVFS_URB_TYPE_BULK,
+ .endpoint = LIBUSB_ENDPOINT_OUT,
+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 },
+ .buffer_length = 4,
+ },
+ { .submit = FALSE }
+ };
+ libusb_device_handle *handle = NULL;
+ struct libusb_transfer *transfer = NULL;
+
+ fixture->chat = chat;
+
+ /* Open */
+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0);
+ g_assert_nonnull(handle);
+
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer,
+ handle,
+ LIBUSB_ENDPOINT_OUT,
+ (unsigned char*) chat[0].buffer,
+ chat[0].buffer_length,
+ NULL,
+ NULL,
+ 1);
+
+ /* Submit */
+ libusb_submit_transfer(transfer);
+
+ /* Now we are evil and destroy the ctx! */
+ libusb_exit(fixture->ctx);
+
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[libusb_exit\\] device.*still referenced");
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[libusb_exit\\] application left some devices open");
+
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG);
+ fixture->ctx = NULL;
+
+ /* XXX: Closing crashes the application as it unref's the NULL pointer */
+ /* libusb_close(handle); */
+
+ libusb_free_transfer(transfer);
+}
+
+static void
+test_get_string_descriptor(UMockdevTestbedFixture * fixture, UNUSED_DATA)
+{
+ unsigned char data[255] = { 0, };
+ libusb_device_handle *handle = NULL;
+ UsbChat chat[] = {
+ {
+ .submit = TRUE,
+ .reaps = &chat[1],
+ .type = USBDEVFS_URB_TYPE_CONTROL,
+ .buffer_length = 12, /* 8 byte out*/
+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00",
+ }, {
+ /* String with content 0x0409 (en_US) */
+ .reap = TRUE,
+ .actual_length = 12,
+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00\x04\x03\x09\x04",
+ }, {
+ .submit = TRUE,
+ .reaps = &chat[3],
+ .type = USBDEVFS_URB_TYPE_CONTROL,
+ .buffer_length = 263, /* 8 byte out*/
+ .buffer = (const unsigned char*) "\x80\x06\x01\x03\x09\x04\xff\x00",
+ }, {
+ /* 4 byte string, "ab" */
+ .reap = TRUE,
+ .actual_length = 14,
+ .buffer = (const unsigned char*) "\x80\x06\x01\x03\x09\x04\xff\x00\x06\x03\x61\x00\x62\x00",
+ }, {
+ .submit = TRUE,
+ .reaps = &chat[5],
+ .type = USBDEVFS_URB_TYPE_CONTROL,
+ .buffer_length = 12, /* 8 byte out*/
+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00",
+ }, {
+ .reap = TRUE,
+ .status = -ENOENT,
+ }, {
+ .submit = TRUE,
+ .status = -ENOENT,
+ .type = USBDEVFS_URB_TYPE_CONTROL,
+ .buffer_length = 12, /* 8 byte out*/
+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00",
+ }, {
+ .submit = FALSE,
+ }
+ };
+
+ fixture->chat = chat;
+
+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0);
+ g_assert_nonnull(handle);
+
+ /* The chat allows us to fetch the descriptor */
+ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, 2);
+ g_assert_cmpint(memcmp(data, "ab", 2), ==, 0);
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG);
+
+ /* Again, but the URB fails with ENOENT when reaping */
+ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, -1);
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG);
+
+ /* Again, but the URB fails to submit with ENOENT */
+ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, -1);
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[submit_control_transfer\\] submiturb failed, errno=2");
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG);
+
+ libusb_close(handle);
+}
+
+static void
+transfer_cb_inc_user_data(struct libusb_transfer *transfer)
+{
+ *(int*)transfer->user_data += 1;
+}
+
+static void
+test_timeout(UMockdevTestbedFixture * fixture, UNUSED_DATA)
+{
+ UsbChat chat[] = {
+ {
+ .submit = TRUE,
+ .type = USBDEVFS_URB_TYPE_BULK,
+ .endpoint = LIBUSB_ENDPOINT_OUT,
+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 },
+ .buffer_length = 4,
+ },
+ {
+ .submit = FALSE,
+ }
+ };
+ int completed = 0;
+ libusb_device_handle *handle = NULL;
+ struct libusb_transfer *transfer = NULL;
+
+ fixture->chat = chat;
+
+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0);
+ g_assert_nonnull(handle);
+
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer,
+ handle,
+ LIBUSB_ENDPOINT_OUT,
+ (unsigned char*) chat[0].buffer,
+ chat[0].buffer_length,
+ transfer_cb_inc_user_data,
+ &completed,
+ 10);
+
+ libusb_submit_transfer(transfer);
+ while (!completed) {
+ g_assert_cmpint(libusb_handle_events_completed(fixture->ctx, &completed), ==, 0);
+ /* Silence after one iteration. */
+ fixture->libusb_log_silence = TRUE;
+ }
+ fixture->libusb_log_silence = FALSE;
+
+ g_assert_cmpint(transfer->status, ==, LIBUSB_TRANSFER_TIMED_OUT);
+ libusb_free_transfer(transfer);
+
+ libusb_close(handle);
+}
+
+#define THREADED_SUBMIT_URB_SETS 64
+#define THREADED_SUBMIT_URB_IN_FLIGHT 64
+typedef struct {
+ struct libusb_transfer *transfers[THREADED_SUBMIT_URB_IN_FLIGHT * THREADED_SUBMIT_URB_SETS];
+ int submitted;
+ int completed;
+ int done;
+ UMockdevTestbedFixture *fixture;
+} TestThreadedSubmit;
+
+static gpointer
+transfer_submit_all_retry(TestThreadedSubmit *data)
+{
+ for (guint i = 0; i < G_N_ELEMENTS(data->transfers); i++) {
+ while (libusb_submit_transfer(data->transfers[i]) < 0) {
+ assert_libusb_log_msg(data->fixture, LIBUSB_LOG_LEVEL_ERROR, "submit_bulk_transfer");
+ continue;
+ }
+
+ data->submitted += 1;
+ }
+
+ return NULL;
+}
+
+static void
+test_threaded_submit_transfer_cb(struct libusb_transfer *transfer)
+{
+ TestThreadedSubmit *data = transfer->user_data;
+
+ /* We should only be receiving packets in the main thread */
+ g_assert_cmpint (getpid(), ==, gettid());
+
+ /* Check that the transfer buffer has the expected value */
+ g_assert_cmpint (*(int*)transfer->buffer, ==, data->completed);
+ data->completed += 1;
+
+ if (data->completed == G_N_ELEMENTS(data->transfers))
+ data->done = TRUE;
+}
+
+static void
+test_threaded_submit(UMockdevTestbedFixture * fixture, UNUSED_DATA)
+{
+ GThread *thread = NULL;
+ TestThreadedSubmit data = { .fixture = fixture };
+ UsbChat out_msg = {
+ .submit = TRUE,
+ .type = USBDEVFS_URB_TYPE_BULK,
+ .endpoint = LIBUSB_ENDPOINT_IN,
+ .buffer_length = sizeof(int),
+ };
+ UsbChat in_msg = {
+ .reap = TRUE,
+ .actual_length = 4,
+ };
+ UsbChat *c;
+ libusb_device_handle *handle = NULL;
+ int urb;
+
+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0);
+ g_assert_nonnull(handle);
+
+ fixture->libusb_log_silence = TRUE;
+
+ c = fixture->chat = g_new0(UsbChat, G_N_ELEMENTS(data.transfers) * 2 + 1);
+ urb = 0;
+ for (int i = 0; i < THREADED_SUBMIT_URB_SETS; i++) {
+ for (int j = 0; j < THREADED_SUBMIT_URB_IN_FLIGHT; j++) {
+ c[i*2*THREADED_SUBMIT_URB_IN_FLIGHT + j] = out_msg;
+ c[i*2*THREADED_SUBMIT_URB_IN_FLIGHT + j].reaps = &c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j];
+ c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j] = in_msg;
+ c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j].buffer = (unsigned char*) g_new0(int, 1);
+ *(int*) c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j].buffer = urb;
+
+ data.transfers[urb] = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(data.transfers[urb],
+ handle,
+ LIBUSB_ENDPOINT_IN,
+ g_malloc(out_msg.buffer_length),
+ out_msg.buffer_length,
+ test_threaded_submit_transfer_cb,
+ &data,
+ G_MAXUINT);
+ data.transfers[urb]->flags = LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER;
+ urb++;
+ }
+ }
+
+ thread = g_thread_new("transfer all", (GThreadFunc) transfer_submit_all_retry, &data);
+
+ while (!data.done)
+ g_assert_cmpint(libusb_handle_events_completed(fixture->ctx, &data.done), ==, 0);
+
+ g_thread_join(thread);
+
+ fixture->libusb_log_silence = FALSE;
+ libusb_close(handle);
+
+ for (int i = 0; i < 2 * THREADED_SUBMIT_URB_SETS * THREADED_SUBMIT_URB_SETS; i++)
+ g_clear_pointer ((void**) &c->buffer, g_free);
+ g_free (c);
+}
+
+static int
+hotplug_count_arrival_cb(libusb_context *ctx,
+ libusb_device *device,
+ libusb_hotplug_event event,
+ void *user_data)
+{
+ g_assert_cmpint(event, ==, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED);
+
+ (void) ctx;
+ (void) device;
+
+ *(int*) user_data += 1;
+
+ return 0;
+}
+
+#ifdef UMOCKDEV_HOTPLUG
+static int
+hotplug_count_removal_cb(libusb_context *ctx,
+ libusb_device *device,
+ libusb_hotplug_event event,
+ void *user_data)
+{
+ g_assert_cmpint(event, ==, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT);
+
+ (void) ctx;
+ (void) device;
+
+ *(int*) user_data += 1;
+
+ return 0;
+}
+#endif
+
+static void
+test_hotplug_enumerate(UMockdevTestbedFixture * fixture, UNUSED_DATA)
+{
+ libusb_hotplug_callback_handle handle_enumerate;
+ libusb_hotplug_callback_handle handle_no_enumerate;
+ int event_count_enumerate = 0;
+ int event_count_no_enumerate = 0;
+ struct timeval zero_tv = { 0 };
+ int r;
+
+ r = libusb_hotplug_register_callback(fixture->ctx,
+ LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
+ LIBUSB_HOTPLUG_ENUMERATE,
+ LIBUSB_HOTPLUG_MATCH_ANY,
+ LIBUSB_HOTPLUG_MATCH_ANY,
+ LIBUSB_HOTPLUG_MATCH_ANY,
+ hotplug_count_arrival_cb,
+ &event_count_enumerate,
+ &handle_enumerate);
+ g_assert_cmpint(r, ==, 0);
+
+ r = libusb_hotplug_register_callback(fixture->ctx,
+ LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
+ 0,
+ LIBUSB_HOTPLUG_MATCH_ANY,
+ LIBUSB_HOTPLUG_MATCH_ANY,
+ LIBUSB_HOTPLUG_MATCH_ANY,
+ hotplug_count_arrival_cb,
+ &event_count_no_enumerate,
+ &handle_no_enumerate);
+ g_assert_cmpint(r, ==, 0);
+
+ g_assert_cmpint(event_count_enumerate, ==, 1);
+ g_assert_cmpint(event_count_no_enumerate, ==, 0);
+
+ libusb_handle_events_timeout(fixture->ctx, &zero_tv);
+
+ g_assert_cmpint(event_count_enumerate, ==, 1);
+ g_assert_cmpint(event_count_no_enumerate, ==, 0);
+
+ libusb_hotplug_deregister_callback(fixture->ctx, handle_enumerate);
+ libusb_hotplug_deregister_callback(fixture->ctx, handle_no_enumerate);
+
+ libusb_handle_events_timeout(fixture->ctx, &zero_tv);
+
+ g_assert_cmpint(event_count_enumerate, ==, 1);
+ g_assert_cmpint(event_count_no_enumerate, ==, 0);
+}
+
+static void
+test_hotplug_add_remove(UMockdevTestbedFixture * fixture, UNUSED_DATA)
+{
+#ifdef UMOCKDEV_HOTPLUG
+ libusb_device **devs = NULL;
+ libusb_hotplug_callback_handle handle_add;
+ libusb_hotplug_callback_handle handle_remove;
+ int event_count_add = 0;
+ int event_count_remove = 0;
+ struct timeval zero_tv = { 0 };
+ int r;
+
+ r = libusb_hotplug_register_callback(fixture->ctx,
+ LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
+ LIBUSB_HOTPLUG_ENUMERATE,
+ LIBUSB_HOTPLUG_MATCH_ANY,
+ LIBUSB_HOTPLUG_MATCH_ANY,
+ LIBUSB_HOTPLUG_MATCH_ANY,
+ hotplug_count_arrival_cb,
+ &event_count_add,
+ &handle_add);
+ g_assert_cmpint(r, ==, 0);
+
+ r = libusb_hotplug_register_callback(fixture->ctx,
+ LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
+ LIBUSB_HOTPLUG_ENUMERATE,
+ LIBUSB_HOTPLUG_MATCH_ANY,
+ LIBUSB_HOTPLUG_MATCH_ANY,
+ LIBUSB_HOTPLUG_MATCH_ANY,
+ hotplug_count_removal_cb,
+ &event_count_remove,
+ &handle_remove);
+ g_assert_cmpint(r, ==, 0);
+
+ /* No device, even going into the mainloop will not call cb. */
+ libusb_handle_events_timeout(fixture->ctx, &zero_tv);
+ g_assert_cmpint(event_count_add, ==, 0);
+ g_assert_cmpint(event_count_remove, ==, 0);
+
+ /* Add a device */
+ test_fixture_add_canon(fixture);
+
+ /* Either the thread has picked it up already, or we do so now. */
+ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, 1);
+ libusb_free_device_list(devs, TRUE);
+
+ /* The hotplug event is pending now, but has not yet fired. */
+ g_assert_cmpint(event_count_add, ==, 0);
+ g_assert_cmpint(event_count_remove, ==, 0);
+
+ /* Fire hotplug event. */
+ libusb_handle_events_timeout(fixture->ctx, &zero_tv);
+ g_assert_cmpint(event_count_add, ==, 1);
+ g_assert_cmpint(event_count_remove, ==, 0);
+
+ umockdev_testbed_uevent(fixture->testbed, "/sys/devices/usb1", "remove");
+ //umockdev_testbed_remove_device(fixture->testbed, "/devices/usb1");
+
+ /* Either the thread has picked it up already, or we do so now. */
+ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, 0);
+ libusb_free_device_list(devs, TRUE);
+
+ /* The hotplug event is pending now, but has not yet fired. */
+ g_assert_cmpint(event_count_add, ==, 1);
+ g_assert_cmpint(event_count_remove, ==, 0);
+
+ /* Fire hotplug event. */
+ libusb_handle_events_timeout(fixture->ctx, &zero_tv);
+ g_assert_cmpint(event_count_add, ==, 1);
+ g_assert_cmpint(event_count_remove, ==, 1);
+
+ libusb_hotplug_deregister_callback(fixture->ctx, handle_add);
+ libusb_hotplug_deregister_callback(fixture->ctx, handle_remove);
+#else
+ (void) fixture;
+ g_test_skip("UMockdev is too old to test hotplug");
+#endif
+}
+
+int
+main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add("/libusb/open-close", UMockdevTestbedFixture, NULL,
+ test_fixture_setup_with_canon,
+ test_open_close,
+ test_fixture_teardown);
+
+ g_test_add("/libusb/implicit-default", UMockdevTestbedFixture, NULL,
+ test_fixture_setup_with_canon,
+ test_implicit_default,
+ test_fixture_teardown);
+
+ g_test_add("/libusb/close-flying", UMockdevTestbedFixture, NULL,
+ test_fixture_setup_with_canon,
+ test_close_flying,
+ test_fixture_teardown);
+ g_test_add("/libusb/close-cancelled", UMockdevTestbedFixture, NULL,
+ test_fixture_setup_with_canon,
+ test_close_cancelled,
+ test_fixture_teardown);
+
+ g_test_add("/libusb/ctx-destroy", UMockdevTestbedFixture, NULL,
+ test_fixture_setup_with_canon,
+ test_ctx_destroy,
+ test_fixture_teardown);
+
+ g_test_add("/libusb/string-descriptor", UMockdevTestbedFixture, NULL,
+ test_fixture_setup_with_canon,
+ test_get_string_descriptor,
+ test_fixture_teardown);
+
+ g_test_add("/libusb/timeout", UMockdevTestbedFixture, NULL,
+ test_fixture_setup_with_canon,
+ test_timeout,
+ test_fixture_teardown);
+
+ g_test_add("/libusb/threaded-submit", UMockdevTestbedFixture, NULL,
+ test_fixture_setup_with_canon,
+ test_threaded_submit,
+ test_fixture_teardown);
+
+ g_test_add("/libusb/hotplug/enumerate", UMockdevTestbedFixture, NULL,
+ test_fixture_setup_with_canon,
+ test_hotplug_enumerate,
+ test_fixture_teardown);
+
+ g_test_add("/libusb/hotplug/add-remove", UMockdevTestbedFixture, NULL,
+ test_fixture_setup_empty,
+ test_hotplug_add_remove,
+ test_fixture_teardown);
+
+ return g_test_run();
+}