diff options
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 @@ -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 @@ -96,6 +96,7 @@ cc_library { host_ldlibs: [ "-framework CoreFoundation", "-framework IOKit", + "-framework Security", "-lobjc" ], @@ -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 @@ -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" @@ -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(×tamp_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(×tamp_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, ¤t_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, ®_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, ®_type, + (LPBYTE)guid_string, &size); if (s == ERROR_FILE_NOT_FOUND) - s = pRegQueryValueExW(key, L"DeviceInterfaceGUID", NULL, ®_type, - (LPBYTE)guid_string_w, &size); + s = pRegQueryValueExA(key, "DeviceInterfaceGUID", NULL, ®_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/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(); +} |